I have the following algorithm:
class CycleData : List<IntPoint>{
public IntPoint startPoint;
public Boolean ended=false;
public CycleData(IntPoint startpoint) { startPoint = startpoint; base.Add(startpoint); }
}
class GeoDataGraphPoint
{
private IntPoint point;
private List<GeoDataGraphPoint> connected = new List<GeoDataGraphPoint>();
private int generation=-9999;
public void AddConnection(GeoDataGraphPoint c)
{
connected.Add(c);
c.connected.Add(this);
}
public GeoDataGraphPoint(IntPoint point)
{
this.point = point;
}
public List<CycleData> GetCycles(int gen)
{
if (generation != -9999)
{
var r = new CycleData(point);
return new List<CycleData> { r };
}
generation = gen;
List<CycleData> res = new List<CycleData>();
foreach (GeoDataGraphPoint p in connected)
{
if (p.generation != gen-1)
{
res.AddRange(p.GetCycles(gen + 1));
}
}
foreach (CycleData list in res)
{
if (list.ended == false)
{
list.Add(point);
if (list.startPoint == this.point)
{
list.ended = false;
}
}
}
gen = -9999;
return res;
}
}
Now in principle this should return every cycle in the graph (for polygon detection). However it seems to fail to return anything in some occasions, I suspect that there is some kind of memory problem as removing parts of the graph sometimes causes new cycles to be found.
Here is a part of the input where it fails:
connection:(2282,3) to (2282,-192)
connection:(2282,3) to (2085,3)
connection:(2282,-192) to (2282,3)
connection:(2282,-192) to (2466,-192)
connection:(2466,-192) to (2282,-192)
connection:(2466,-192) to (2466,581)
connection:(2466,581) to (2466,-192)
connection:(2466,581) to (1494,581)
connection:(1494,581) to (2466,581)
connection:(1494,581) to (1494,397)
connection:(1494,397) to (1494,581)
connection:(1494,397) to (2282,397)
connection:(2282,397) to (1494,397)
connection:(2282,397) to (2282,187)
connection:(2282,187) to (2282,397)
connection:(2282,187) to (2085,187)
connection:(2085,187) to (2282,187)
connection:(2085,187) to (2085,3)
connection:(2085,3) to (2085,187)
connection:(2085,3) to (2282,3)
connection:(2085,3) to (2085,187)
connection:(2085,3) to (2282,3)
connection:(2085,187) to (2282,187)
connection:(2085,187) to (2085,3)
connection:(2282,187) to (2282,397)
connection:(2282,187) to (2085,187)
connection:(2282,397) to (1494,397)
The above code is for two triangles arranged to form a square (in coordinates) where both sides touch one another so like this:
Where I can the function as following:
class GeoDataGraph : Dictionary<IntPoint, GeoDataGraphPoint>
{
public void resetGens()
{
foreach(var v in base.Values)
{
v.generation = -9999;
}
}
public static Island GetHolesInIsland(Island input)
{
GeoDataGraph graph = new GeoDataGraph();
for (int i = 0; i < input.area.Count-1; i = i + 2)
{
var p1 = new IntPoint(input.area[i].X, input.area[i].Y);
var p2 = new IntPoint(input.area[i + 1].X, input.area[i + 1].Y);
if (!graph.ContainsKey(p1)) graph.Add(p1, new GeoDataGraphPoint(p1));
if (!graph.ContainsKey(p2)) graph.Add(p2, new GeoDataGraphPoint(p2));
graph[p1].AddConnection(graph[p2]);
}
IntPoint min = new IntPoint(int.MaxValue, int.MaxValue);
List<IntPoint> minCycle = null;
List<List<IntPoint>> cycles = new List<List<IntPoint>>();
while (graph.Count != 0)
{
var first = graph.First();
var NewCycles = first.Value.GetCycles(1);
graph.resetGens();
if (NewCycles.Count == 0)
{
graph.Remove(first.Key);
Console.WriteLine("point" + first.Key + "is uncycled");
}
cycles.AddRange(NewCycles);
foreach (var cycle in NewCycles)
{
foreach (var cycleNode in cycle)
{
graph.Remove(cycleNode);
if (min.X > cycleNode.X || min.Y > cycleNode.Y)
{
minCycle = cycle;
min = cycleNode;
}
}
}
}
cycles.Remove(minCycle);
if (minCycle == null) { minCycle = new List<IntPoint>();
foreach(IntPoint a in input.area) {
Console.Write(a);
} }
input.holes = cycles;
input.area = minCycle;
return input;
}
}
}
where Island contains.area contains a list of points ordered by connected pairs.
The basic algorithm is simple: take a node recursively visit every node connected to it until you detect a cycle, then return that node and append any node on the way out until you find the start of the cycle again. Once you have found every node connected to the start node remove the cycles (they since we checked every node connected to the cycle we shouldn't be deleting the once we already did) and start again on the next node, if a node contains no cycles remove it. I suspect something might be going wrong at this step but am unsure.
Any idea what I'm doing wrong that causes a weird interdependence that causes seemingly unrelated polygons to go wrong?
I think that one problem is the way you ignore connected nodes using p.generation != gen-1. As you use depth first search you will tag all the nodes until the most depth and when backtracking I think it can miss some nodes or explore nodes twice.
As a general advise I can say: Don't reinvent the wheel yourself but use a known algorithm.
First ask yourself what you want to do. The number of cycles can be exponential. So the question is if you really need all the cycles.
If the answer is yes and you want to find all the cycles in a undirected graph you can get more information here.
If you don't really need all the cyles maybe what your are looking for is to find strongly connected components.
Related
Solved! See my own answer.
I'm creating an app in which there is a hex based map. It takes a certain amount of time to travel between hexes and I'm trying to implement a way to press on any valid hex and get the optimal path (If possible) from the players current position.
Here comes the tricky part, only a few of the hexes are valid travel locations, so I need my path finding to be able to skip over empty hexes. Depending on the players "reach" they are allowed to skip otherwise invalid spaces. So if the players reach was f ex. 3, they are allowed to move 1, 2, or 3 spaces in one "move", even if hexes in between are invalid (Empty), but they are not allowed to "stop" on invalid hexes.
Example:
So far, I've implemented an A* pathfinding script with the help of these references:
https://www.redblobgames.com/grids/hexagons/
https://www.redblobgames.com/pathfinding/a-star/introduction.html
https://gist.github.com/keithcollins/307c3335308fea62db2731265ab44c06
But I'm not sure how to implement the "Skipping" or "Jumping" funcitonality I'm looking for. I've specifically avoided using "edge" cost, because it would require a lot of changes to my current implementation, and direction doesn't matter at all.
My version of the A* function in the third reference link:
public void AStarSearch(MapHex startHex, MapHex goalHex)
{
if (goalHex.empty)
{
Debug.Log("Goal empty.");
return;
}
SimplePriorityQueue<MapHex> frontier =
new SimplePriorityQueue<MapHex>();
frontier.Enqueue(startHex, 0);
cameFrom.Add(startHex, startHex);
costSoFar.Add(startHex, 0);
while (frontier.Count > 0)
{
MapHex current = frontier.Dequeue();
if (current.Equals(goalHex)) break;
foreach (MapHex neighbor in GetNeighbors(current))
{
float newCost = costSoFar[current] + 1;
if (!costSoFar.ContainsKey(neighbor) ||
newCost < costSoFar[neighbor])
{
if (costSoFar.ContainsKey(neighbor))
{
costSoFar.Remove(neighbor);
cameFrom.Remove(neighbor);
}
costSoFar.Add(neighbor, newCost);
cameFrom.Add(neighbor, current);
float priority = newCost +
HexHeuristicDistance(neighbor, goalHex);
frontier.Enqueue(neighbor, priority);
}
}
}
}
I just don't know how to expand the search past otherwise impassable hexes, or similar. I need the final output to be the shortest route to the end hex (If any is possible), to display the route, and the distance travelled.
Any help appreciated :)
Edit:
Okay as per Ruzihm's suggestion I'm trying to expand my "GetNeighbors" function to achieve my goal.
To get all "neighbors" in range of the max speed, I'm trying to implement the code from the "Movement range" section of Red Blob Games's hexgrid guide: https://www.redblobgames.com/grids/hexagons/#range (Second code snip)
Converting from python is proving to be a challenge though and I seem to have created an inefficient monster that may or may not work:
public IEnumerable<MapHex> GetNeighbors(MapHex hex, int distanceOffset)
{
if (distanceOffset == 0)
{
foreach (Vector2 dir in oddQDirections)
{
MapHex next = allMapHexes.Find(item => item.coordinate == new Vector2(hex.coordinate.x + dir.x, hex.coordinate.y + dir.y));
if (next != null)
{
yield return next;
}
}
}
else
{
List<MapHex> neighbors = new List<MapHex>();
List<MapHex> closeHexesX = new List<MapHex>();
Vector3 hexCubeCoord = OddQToCube(hex.coordinate);
foreach (MapHex closeHex in allMapHexes.FindAll(item => !item.empty && -(distanceOffset + 1) <= -OddQToCube(item.coordinate).x && -OddQToCube(item.coordinate).x <= distanceOffset + 1))
{
closeHexesX.Add(closeHex);
}
foreach (MapHex closeHex in closeHexesX.FindAll(item => Mathf.Max(-(distanceOffset + 1), -OddQToCube(item.coordinate).x - (distanceOffset + 1)) <= -OddQToCube(item.coordinate).y && -OddQToCube(item.coordinate).x <= Mathf.Min((distanceOffset + 1), -OddQToCube(item.coordinate).x + (distanceOffset + 1))))
{
Vector3 cubeCoord = OddQToCube(closeHex.coordinate);
cubeCoord.z = -cubeCoord.x - cubeCoord.y;
neighbors.Add(closeHexesX.Find(item => item.coordinate == CubeToOddQ(hexCubeCoord + cubeCoord)));
}
}
}
Maybe I'm better off just running GetDistance for each hex and return those that have a distance <= to "reach"..?
Alright so I ended up solving my problem (Thanks to some pointers from Ruzihm that made me rethink my problem) by simply checking all valid hexes in range, for each pathfinding node. I'm not sure this is the fastest method, but I already have a list of valid hexes, so I don't have iterate through all the empty hexes each loop and it seems to be fast enough.
Here's the "GetNeighbours" code I ended up using each path finding loop:
public List<MapHex> GetNeighborsInRange(MapHex hex, int distance)
{
List<MapHex> neighbors = new List<MapHex>();
if (distance == 0)
{
foreach (Vector2 dir in oddQDirections)
{
MapHex next = allMapHexes.Find(item => item.coordinate == new Vector2(hex.coordinate.x + dir.x, hex.coordinate.y + dir.y));
if (next != null)
{
neighbors.Add(next);
}
}
}
else
{
foreach (MapHex closeHex in nonEmptyMapHexes)
{
if (HexHeuristicDistance(hex, closeHex) <= distance)
{
neighbors.Add(closeHex);
}
}
}
return neighbors;
}
Oh also, here's the method I use to convert my pathfinding data into useful data in the form of an object I call "Journey":
public Journey GetLatestPath()
{
if (cameFrom == null || cameFrom.Count == 0 || costSoFar == null || costSoFar.Count == 0)
{
//Trying to run this before running an A* search.
return null;
}
int pathTravelTime = 0;
List<MapHex> path = new List<MapHex>();
MapHex current = aStarLatestGoal;
while (!current.Equals(aStarLatestStart))
{
if (!cameFrom.ContainsKey(current))
{
//A* was unable to find a valid path.
return null;
}
path.Add(current);
current = cameFrom[current];
pathTravelTime += HexHeuristicDistance(current, path[path.Count - 1]);
}
path.Reverse();
return new Journey(path, aStarLatestStart, pathTravelTime);
}
If you are using a cube or axial hex grid, check out this question for a possible solution to "Get all in range": https://gamedev.stackexchange.com/questions/116035/finding-cells-within-range-on-hexagonal-grid?rq=1
I am trying to calculate the amount of links between one destination and another in my game, the first method that gets called is CalculateRoute and it returns a list of map routes.
At this point you're probably thinking "so whats wrong with the code you've shown?".
When the link is further than 1 room away, it doesn't take into account that it needs to get the links from the first two rooms and just ignores the rest...
Better explain, it only gets the link instructions from the room it starts from inside of the method GetRoutesForRoom and this causes a big problem for the whole mechanism.
I'm already confused by this code so I'm just asking for some help to make it count the first parts of the link inside of the GetRoutesForRoom method.
internal class MapRoute
{
private List<int> _arrowLinks;
public MapRoute(List<int> arrowLinks)
{
_arrowLinks = arrowLinks;
}
}
Here is the CalculateRoute method...
public Dictionary<int, MapRoute> CalculateRoute(Player player, Room startLocation, Room endLocation)
{
var possibleRoutes = new Dictionary<int, MapRoute>();
var arrowsAtStart = startLocation.GetRoomItemHandler().GetFloor.Where(
x => x.GetBaseItem().InteractionType == InteractionType.ARROW);
foreach (var arrow in arrowsAtStart)
{
if (!ItemTeleporterFinder.IsTeleLinked(arrow.Id, startLocation))
{
continue;
}
var linkedRoomId = ItemTeleporterFinder.GetTeleRoomId(arrow.Id, startLocation);
if (linkedRoomId == endLocation.RoomId)
{
possibleRoutes.Add(possibleRoutes.Count + 1, new MapRoute(new List<int> { arrow.Id }));
}
else if (PlusEnvironment.GetGame().GetRoomManager().TryGetRoom(linkedRoomId, out var secondRoom))
{
foreach (var mapRoute in GetRoutesForRoom(secondRoom, startLocation.RoomId))
{
possibleRoutes.Add(possibleRoutes.Count + 1, mapRoute);
}
}
}
return possibleRoutes;
}
For calculating rooms further down the link, I use another method...
public List<MapRoute> GetRoutesForRoom(Room room, int destination)
{
var possibleRoutes = new List<MapRoute>();
var arrowsInRoom = room.GetRoomItemHandler().GetFloor.Where(
x => x.GetBaseItem().InteractionType == InteractionType.ARROW);
foreach (var arrow in arrowsInRoom)
{
if (!ItemTeleporterFinder.IsTeleLinked(arrow.Id, room))
{
continue;
}
var linkedRoomId = ItemTeleporterFinder.GetTeleRoomId(arrow.Id, room);
if (linkedRoomId == destination)
{
possibleRoutes.Add(new MapRoute(new List<int> { arrow.Id }));
}
else if (PlusEnvironment.GetGame().GetRoomManager().TryGetRoom(linkedRoomId, out var secondRoom))
{
foreach (var mapRoute in GetRoutesForRoom(secondRoom, destination))
{
possibleRoutes.Add(mapRoute);
}
}
}
return possibleRoutes;
}
I need to implement a search algorithm which only searches from the start of the string rather than anywhere within the string.
I am new to algorithms but from what I can see it seems as though they go through the string and find any occurrence.
I have a collection of strings (over 1 million) which need to be searched everytime the user types a keystroke.
EDIT:
This will be an incremental search. I currently have it implemented with the following code and my searches are coming back ranging between 300-700ms from over 1 million possible strings. The collection isnt ordered but there is no reason it couldnt be.
private ICollection<string> SearchCities(string searchString) {
return _cityDataSource.AsParallel().Where(x => x.ToLower().StartsWith(searchString)).ToArray();
}
I've adapted the code from this article from Visual Studio Magazine that implements a Trie.
The following program demonstrates how to use a Trie to do fast prefix searching.
In order to run this program, you will need a text file called "words.txt" with a large list of words. You can download one from Github here.
After you compile the program, copy the "words.txt" file into the same folder as the executable.
When you run the program, type a prefix (such as prefix ;)) and press return, and it will list all the words beginning with that prefix.
This should be a very fast lookup - see the Visual Studio Magazine article for more details!
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
var trie = new Trie();
trie.InsertRange(File.ReadLines("words.txt"));
Console.WriteLine("Type a prefix and press return.");
while (true)
{
string prefix = Console.ReadLine();
if (string.IsNullOrEmpty(prefix))
continue;
var node = trie.Prefix(prefix);
if (node.Depth == prefix.Length)
{
foreach (var suffix in suffixes(node))
Console.WriteLine(prefix + suffix);
}
else
{
Console.WriteLine("Prefix not found.");
}
Console.WriteLine();
}
}
static IEnumerable<string> suffixes(Node parent)
{
var sb = new StringBuilder();
return suffixes(parent, sb).Select(suffix => suffix.TrimEnd('$'));
}
static IEnumerable<string> suffixes(Node parent, StringBuilder current)
{
if (parent.IsLeaf())
{
yield return current.ToString();
}
else
{
foreach (var child in parent.Children)
{
current.Append(child.Value);
foreach (var value in suffixes(child, current))
yield return value;
--current.Length;
}
}
}
}
public class Node
{
public char Value { get; set; }
public List<Node> Children { get; set; }
public Node Parent { get; set; }
public int Depth { get; set; }
public Node(char value, int depth, Node parent)
{
Value = value;
Children = new List<Node>();
Depth = depth;
Parent = parent;
}
public bool IsLeaf()
{
return Children.Count == 0;
}
public Node FindChildNode(char c)
{
return Children.FirstOrDefault(child => child.Value == c);
}
public void DeleteChildNode(char c)
{
for (var i = 0; i < Children.Count; i++)
if (Children[i].Value == c)
Children.RemoveAt(i);
}
}
public class Trie
{
readonly Node _root;
public Trie()
{
_root = new Node('^', 0, null);
}
public Node Prefix(string s)
{
var currentNode = _root;
var result = currentNode;
foreach (var c in s)
{
currentNode = currentNode.FindChildNode(c);
if (currentNode == null)
break;
result = currentNode;
}
return result;
}
public bool Search(string s)
{
var prefix = Prefix(s);
return prefix.Depth == s.Length && prefix.FindChildNode('$') != null;
}
public void InsertRange(IEnumerable<string> items)
{
foreach (string item in items)
Insert(item);
}
public void Insert(string s)
{
var commonPrefix = Prefix(s);
var current = commonPrefix;
for (var i = current.Depth; i < s.Length; i++)
{
var newNode = new Node(s[i], current.Depth + 1, current);
current.Children.Add(newNode);
current = newNode;
}
current.Children.Add(new Node('$', current.Depth + 1, current));
}
public void Delete(string s)
{
if (!Search(s))
return;
var node = Prefix(s).FindChildNode('$');
while (node.IsLeaf())
{
var parent = node.Parent;
parent.DeleteChildNode(node.Value);
node = parent;
}
}
}
}
A couple of thoughts:
First, your million strings need to be ordered, so that you can "seek" to the first matching string and return strings until you no longer have a match...in order (seek via C# List<string>.BinarySearch, perhaps). That's how you touch the least number of strings possible.
Second, you should probably not try to hit the string list until there's a pause in input of at least 500 ms (give or take).
Third, your queries into the vastness should be async and cancelable, because it's certainly going to be the case that one effort will be superseded by the next keystroke.
Finally, any subsequent query should first check that the new search string is an append of the most recent search string...so that you can begin your subsequent seek from the last seek (saving lots of time).
I suggest using linq.
string x = "searchterm";
List<string> y = new List<string>();
List<string> Matches = y.Where(xo => xo.StartsWith(x)).ToList();
Where x is your keystroke search text term, y is your collection of strings to search, and Matches is the matches from your collection.
I tested this with the first 1 million prime numbers, here is the code adapted from above:
Stopwatch SW = new Stopwatch();
SW.Start();
string x = "2";
List<string> y = System.IO.File.ReadAllText("primes1.txt").Split(' ').ToList();
y.RemoveAll(xo => xo == " " || xo == "" || xo == "\r\r\n");
List <string> Matches = y.Where(xo => xo.StartsWith(x)).ToList();
SW.Stop();
Console.WriteLine("matches: " + Matches.Count);
Console.WriteLine("time taken: " + SW.Elapsed.TotalSeconds);
Console.Read();
Result is:
matches: 77025
time taken: 0.4240604
Of course this is testing against numbers and I don't know whether linq converts the values before, or if numbers make any difference.
I didn't know how to formulate the question title any better without getting too descriptive, I'm sorry in advance...
Anyhow, my problem is the following.
I have a List NodeList, and a secondary List named Unvisited.
I use the Method GetPath on the Unvisited list (it's an implementation of Dijkstra's Pathfidning Algorithm). But for some weird reason when I draw the texture stored in the Nodes in the NodeList some of the nodes (particularly, the nodes used to trace a path between) get removed.
I am looking for an explanation why the Nodes get removed from the NodeList even when I clearly set Unvisited equal NodeList...
EDIT: If there is any code that is missing to understand the problem, ask and I will edit!
Relevant Code:
public class Hotel
{
public List<Node> nodeList;
//constructor loadscontent and initialises list, ommitted here.
public void BuildHotel(ContentManager content)
{
for (int i = 0; i < 5; i++)
{
GuestRoom temp = new GuestRoom(100 + i, content, new Point(64 + (i * 64), 128), new Point(2, 1));
nodeList.Add(new Node(temp, new Point(64 + (i * 64), 128)));
}
// add edges between some nodes
for (int i = 0; i < 4; i++)
{
AddEdge(nodeList[i].Room.RoomId, nodeList[i + 1].Room.RoomId, 2);
}
guest = new Guest(content);
guest.Setpath(100, 104, nodeList);
}
}
class PathFinding
{
public List<Node> Unvisited;
public List<Node> Visited;
private Stack<Node> _temp = new Stack<Node>();
public Stack<Node> GetPath(int startroom, int finalroom, List<Node> nodeList)
{
Unvisited = nodeList;
Node startNode = Unvisited.DefaultIfEmpty(null).FirstOrDefault(x => x.Room.RoomId == startroom);
Node finalNode = Unvisited.DefaultIfEmpty(null).FirstOrDefault(x => x.Room.RoomId == finalroom);
if (startNode == null || finalNode == null)
{
Console.WriteLine("At least one of the nodes does not exist");
return null;
}
startNode.Distance = 0;
Node currentNode = startNode;
while (!IsVisited(currentNode, finalNode))
{
currentNode = Unvisited.Aggregate((l, r) => l.Distance < r.Distance ? l : r);
}
//reverse nodes in queue
Queue<Node> reversedqueue = MakePath(startNode, currentNode, finalNode);
for (int i = 0; i < MakePath(startNode, currentNode, finalNode).Count; i++)
{
_temp.Push(reversedqueue.Dequeue());
}
return _temp;
}
}
public class SimulationScreen : Screen
{
private Hotel hotel;
//.. other methods ommited.
public override void Activate(bool instancePreserved)
{
if (!instancePreserved)
{
if (_content == null)
_content = new ContentManager(ScreenManager.Game.Services, "Content");
ScreenManager.Game.ResetElapsedTime();
}
hotel = new Hotel(_content);
}
}
Visual Representation on the bug
Without the pathfinder turned on
Your problem is right here:
Unvisited = nodeList;
That's the problem, I have looked through my entire solution and there is not a single place where the nodes get removed from the NodeList ._. any removal from the list is from the Unvisited list... :s
After that assignment, any removal from the Unvisited list removes from the nodeList. List is a reference type, so when you change its value with an assignment you're really changing which object it refers to. Unvisited and nodeList both refer to the same object after this. To avoid this, instantiate a new List using the old one rather than assigning both lists to the same reference:
Unvisited = new List<Node>(nodeList);
I hope I am using the right terminology.
I have made a single-chained list.
class MyStack
{
public Node Initial { get; set; }
public MyStack()
{
Initial = null;
}
public void Push(int data)
{
var node = new Node { Data = data, Next = Initial };
Initial = node;
}
public int Pop()
{
int res = Initial.Data;
Initial = Initial.Next;
return res;
}
public int Sum()
{
int sum = 0;
Node currentNode = Initial;
while (currentNode != null)
{
sum += currentNode.Data;
currentNode = currentNode.Next;
}
return sum;
}
public int Count()
{
int count = 0;
Node currentNode = Initial;
while (currentNode != null)
{
count++;
currentNode = currentNode.Next;
}
return count;
}
public void PrintAll()
{
Node currentNode = Initial;
while(currentNode != null)
{
Console.WriteLine("tmp.Data = " + currentNode.Data);
currentNode = currentNode.Next;
}
}
}
public class Node
{
public int Data;
public Node Next;
}
Meaning you can do something like this:
var s = new MyStack();
s.Push(5);
s.Push(3);
s.Push(7);
s.PrintAll();
Console.WriteLine("Sum: " + s.Sum());
Console.WriteLine("Count: " + s.Count());
Now, I want to try and make a Reverse method. This seems to be working:
public void Reverse()
{
Node predesesor, location;
location = Initial;
predesesor = null;
while(Initial != null)
{
Initial = Initial.Next;
location.Next = predesesor;
predesesor = location;
location = Initial;
}
Initial = predesesor;
}
I am hardly able to see how it works, and it will be tough to maintain.
It seems more like a hack than anything else.
Can you offer any assistance?
It doesn't seem like a hack to me and I don't see what's there to maintain (it is either correct or not, what else would you do with it?). If you want to figure out how it works, "execute" each step on paper. Draw a list (e.g 1 -> 3 -> 5 -> 7 -> 9 -> NULL), mark out where all Nodes point at any time and start "single-stepping".
I couldn't think of a cleaner way to reverse a singly linked list. You need a reference to the next node (Initial at the beginning of the loop), before you can reverse the link between current node and previous node. You just won't be able to move on in the original list otherwise.
What you could do is fix the spelling of the variables and perhaps not use Initial in the loop itself (use a third variable, so the role of each variable is clearer) and only set Initial to the first Node in the reversed list at the end.
So, all in all:
public void Reverse() {
Node current = Initial, previous = null;
while (current) {
Node next = current.Next;
current.Next = previous;
previous = current;
current = next;
}
Initial = previous;
}
One solution would be to turn it into a doubly-linked list, and then work backwards using the previous node property:
public class Node
{
public int Data;
public Node Next;
public Node Previous;
}
There is already a doubly-linked list in the framework if you want to save yourself some effort.
you can do it recursivly:
a->b->c->d
a->null
b->null
c->null
c<-d
b<-c
a<-b
a<-b<-c<-d
public void Reverse()
{
Reverse(Initial);
}
private void Reverse(Node node)
{
if(node != null && node.Next != null)
{
//go deeper
Reverse(node.Next);
//swap
node.Next.Next = node
node.Next = null;
}
}
Instead of reinventing the wheel you should check, if there is already something you can use something from .Net Framework.
For example i would take here the LinkedList with the LinkedListNode. In other cases you could probably take the Queue or a Stack.
If you call it "nreverse" or "reverse in place" then any developer worth tuppence would recognise it as a classic algorithm rather than a hack.
It shouldn't require much maintenance, except maybe renaming any incorrectly spelled variables.
The following code might be a bit more intuitive:
public void Reverse()
{
MyStack reverse = new MyStack();
while (Initial != null)
{
reverse.Push(this.Pop());
}
Initial = reverse.Initial;
}
(Reverse is a member method of the MyStack class).
Of course, it does require twice the space compared with the original code.
stack s1
s1.push_front(...)
...
s1.push_front(...)
////////////////////////////////////////////////////////
void reverse(stack& to,stack_or_list& s )
while(!s.empty()){
to.push_front(s.pop_front());
}
}
now a series of to.pop_fronts gets you what you want
stack_or_list needs: pop_front empty
to needs: push_front,pop_front