Duplicate a linked list - c#

I wrote a method which creates a copy of linked list.
Can you guys think of any method better than this?
public static Node Duplicate(Node n)
{
Stack<Node> s = new Stack<Node>();
while (n != null)
{
Node n2 = new Node();
n2.Data = n.Data;
s.Push(n2);
n = n.Next;
}
Node temp = null;
while (s.Count > 0)
{
Node n3 = s.Pop();
n3.Next = temp;
temp = n3;
}
return temp;
}

You can do it in one pass, something like this:
public static Node Duplicate(Node n)
{
// handle the degenerate case of an empty list
if (n == null) {
return null;
}
// create the head node, keeping it for later return
Node first = new Node();
first.Data = n.Data;
// the 'temp' pointer points to the current "last" node in the new list
Node temp = first;
n = n.Next;
while (n != null)
{
Node n2 = new Node();
n2.Data = n.Data;
// modify the Next pointer of the last node to point to the new last node
temp.Next = n2;
temp = n2;
n = n.Next;
}
return first;
}

#Greg, I took your code and made it even a bit shorter :)
public static Node Duplicate(Node n)
{
// Handle the degenerate case of an empty list
if (n == null) return null;
// Create the head node, keeping it for later return
Node first = new Node();
Node current = first;
do
{
// Copy the data of the Node
current.Data = n.Data;
current = (current.Next = new Node());
n = n.Next;
} while (n != null)
return first;
}
The Do-While construct is often forgotten, but fits well here.
A Node.Clone() method would be nice as well.
+1 to Greg for the nice example.

Recursive method for small/medium lists.
public static Node Duplicate(Node n)
{
if (n == null)
return null;
return new Node() {
Data = n.Data,
Next = Duplicate(n.Next)
};
}

I'm sorry if I've missed something, but what's wrong with
LinkedList<MyType> clone = new LinkedList<MyType>(originalLinkedList);
.NET API on LinkedList Constructors
EDIT: Just wanted to emphasize an extremely good point made in the comment below:
"...changing non-value objects on the duplicate changes them on the
original too. Meaning you might want to be sure you don't need a deep
copy before trying anything on this page."

Related

Finding all possible paths from one node to another?

I was trying to find all possible paths, but I am having hard time keeping track of the paths that I have visited. Here is the code so far:
public void FindAllPaths(Node startNode, Node endNode)
{
queue.Enqueue(startNode);
while (queue.Count > 0)
{
var currentNode = queue.Dequeue();
foreach (var edge in currentNode.Edges)
{
if (edge.Visisted)
continue;
edge.Visisted = true;
queue.Enqueue(edge.TargetNode);
}
}
}
You have to keep track of the paths visited for each route, not globally. For a breadth first approach each route needs a list of visited paths. For a depth first approach you can either keep a list of visited paths, or keep it global but unvisit the paths as you backtrack.
Getting the length of the path and the total weight will more or less do itself, once you keep track of the paths for each route.
With your current algorithm you would enqueue an item that has a node and a list of visited paths:
public void FindAllPaths(Node startNode, Node endNode) {
queue.Enqueue(new QueueItem(startNode, new List<Edge>()));
while (queue.Count > 0) {
var currentItem = queue.Dequeue();
foreach (var edge in currentItem.Node.Edges) {
if (!currentItem.Visited.Contains(edge)) {
List<Edge> visited = new List<Edge>(currentItem.Visited);
visited.Add(edge);
if (edge.TargetNode == endNode) {
// visited.Count is the path length
// visited.Sum(e => e.Weight) is the total weight
} else {
queue.Enqueue(new QueueItem(edge.TargetNode, visited));
}
}
}
}
}
The QueueItem class is just:
public class QueueItem {
public Node Node { get; private set; }
public List<Edge> Visited { get; private set; }
public QueueItem(Node node, List<Edge> visited) {
Node = node;
Visited = visited;
}
}
I set up the paths this way:
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
Node d = new Node("D");
Node e = new Node("E");
a.Edges.Add(new Edge(5, b));
a.Edges.Add(new Edge(7, e));
a.Edges.Add(new Edge(5, d));
b.Edges.Add(new Edge(4, c));
c.Edges.Add(new Edge(2, e));
c.Edges.Add(new Edge(8, d));
d.Edges.Add(new Edge(8, c));
d.Edges.Add(new Edge(6, e));
e.Edges.Add(new Edge(3, b));
If you go A-B-C-E then C will be marked as visited, but since C is also part of the path A-D-C-E you won't be able to find the later. Therefore a depth-first approach seems more appropriate, as it allows you to work on one path at a time. After you are finished with a path, you can clear the Visited-flags and continue with another path. I'm trying to scetch a solution in pseudo code:
declare path as list of node;
procedure FindPath(node)
for each edge in node.Edges
if not edge.Visited then
edge.Visited = true
path.Append(edge.TargetNode)
if edge.TargetNode = goal then
Print(path)
else
FindPath(edge.TargetNode)
end
path.Remove(edge.TargetNode)
edge.Visited = false
end
end
end
Where goal is node E in your example. You would call FindPath with the start node
FindPath(A);
As stated previously, maintaining a Visited property on each edge will not work because a given edge may be present in multiple distinct paths. For example, the D/E edge will be traversed for both the A->D->E path and the A->B->C->D->E path.
You need to maintain the current path for each node added to the queue:
IEnumerable<Path> FindAllPaths(Node from, Node to)
{
Queue<Tuple<Node, List<Node>>> nodes = new Queue<Tuple<Node, List<Node>>>();
nodes.Enqueue(new Tuple<Node, List<Node>>(from, new List<Node>()));
List<Path> paths = new List<Path>();
while(nodes.Any())
{
var current = nodes.Dequeue();
Node currentNode = current.Item1;
if (current.Item2.Contains(currentNode))
{
continue;
}
current.Item2.Add(currentNode);
if (currentNode == to)
{
paths.Add(new Path(current.Item2));
continue;
}
foreach(var edge in current.Item1.Edges)
{
nodes.Enqueue(new Tuple<Node, List<Node>>(edge.Target, new List<Node>(current.Item2)));
}
}
return paths;
}

OutOfMemoryException when updating a large list?

I have a large list and I would like to overwrite one value if required. To do this, I create two subsets of the list which seems to give me an OutOfMemoryException. Here is my code snippet:
if (ownRG != "")
{
List<string> maclist = ownRG.Split(',').ToList();
List<IVFile> temp = powlist.Where(a => maclist.Contains(a.Machine)).ToList();
powlist = powlist.Where(a => !maclist.Contains(a.Machine)).ToList(); // OOME Here
temp.ForEach(a => { a.ReportingGroup = ownRG; });
powlist.AddRange(temp);
}
Essentially I'm splitting the list into the part that needs updating and the part that doesn't, then I perform the update and put the list back together. This works fine for smaller lists, but breaks with an OutOfMemoryException on the third row within the if for a large list. Can I make this more efficient?
NOTE
powlist is the large list (>1m) items. maclist only has between 1 and 10 but even with 1 item this breaks.
Solving your issue
Here is how to rearrange your code using the enumerator code from my answer:
if (!string.IsNullOrEmpty(ownRG))
{
var maclist = new CommaSeparatedStringEnumerable(str);
var temp = powlist.Where(a => maclist.Contains(a.Machine));
foreach (var p in temp)
{
p.ReportingGroup = ownRG;
}
}
You should not use ToList in your code.
You don't need to remove thee contents of temp from powlist (you are re-adding them anyway)
Streaming over a large comma-separated string
You can iterate over the list manually instead of doing what you do now, by looking for , characters and remembering the position of the last found one and the one before. This will definitely make your app work because then it won't need to store the entire set in the memory at once.
Code example:
var str = "aaa,bbb,ccc";
var previousComma = -1;
var currentComma = 0;
for (; (currentComma = str.IndexOf(',', previousComma + 1)) != -1; previousComma = currentComma)
{
var currentItem = str.Substring(previousComma + 1, currentComma - previousComma - 1);
Console.WriteLine(currentItem);
}
var lastItem = str.Substring(previousComma + 1);
Console.WriteLine(lastItem);
Custom iterator
If you want to do it 'properly' in a fancy way, you can even write a custom enumerator:
public class CommaSeparatedStringEnumerator : IEnumerator<string>
{
int previousComma = -1;
int currentComma = -1;
string bigString = null;
bool atEnd = false;
public CommaSeparatedStringEnumerator(string s)
{
if (s == null)
throw new ArgumentNullException("s");
bigString = s;
this.Reset();
}
public string Current { get; private set; }
public void Dispose() { /* No need to do anything here */ }
object IEnumerator.Current { get { return this.Current; } }
public bool MoveNext()
{
if (atEnd)
return false;
atEnd = (currentComma = bigString.IndexOf(',', previousComma + 1)) == -1;
if (!atEnd)
Current = bigString.Substring(previousComma + 1, currentComma - previousComma - 1);
else
Current = bigString.Substring(previousComma + 1);
previousComma = currentComma;
return true;
}
public void Reset()
{
previousComma = -1;
currentComma = -1;
atEnd = false;
this.Current = null;
}
}
public class CommaSeparatedStringEnumerable : IEnumerable<string>
{
string bigString = null;
public CommaSeparatedStringEnumerable(string s)
{
if (s == null)
throw new ArgumentNullException("s");
bigString = s;
}
public IEnumerator<string> GetEnumerator()
{
return new CommaSeparatedStringEnumerator(bigString);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Then you can iterate over it like this:
var str = "aaa,bbb,ccc";
var enumerable = new CommaSeparatedStringEnumerable(str);
foreach (var item in enumerable)
{
Console.WriteLine(item);
}
Other thoughts
Can I make this more efficient?
Yes, you can. I suggest to either work with a more efficient data format (you can take a look around databases or XML, JSON, etc. depending on your needs). If you really want to work with comma-separated items, see my code examples above.
There's no need to create a bunch of sub-lists from powlist and reconstruct it. Simply loop over the powlist and update the ReportingGroup property accordingly.
var maclist = new HashSet<string>( ownRG.Split(',') );
foreach( var item in powlist) {
if( maclist.Contains( item.Machine ) ){
item.ReportingGroup = ownRG;
}
}
Since this changes powlist in place, you won't allocate any extra memory and shouldn't run into an OutOfMemoryException.
In a loop find the next ',' char. Take the substring between the ',' and the previous ',' position. At the end of the loop save a reference to the previous ',' position (which is initially set to 0). So you parse the items one-by-one rather than all at once.
You can try looping the items of your lists, but this will increase processing time.
foreach(var item in powlist)
{
//do your opeartions
}

Height of binary search tree iteratively

I was trying out an iterative method to find the height/depth of a binary search tree.
Basically, I tried using Breadth First Search to calculate the depth, by using a Queue to store the tree nodes and using just an integer to hold the current depth of the tree. Each node in the tree is queued, and it is checked for child nodes. If child nodes are present, then the depth variable is incremented. Here is the code:
public void calcDepthIterative() {
Queue<TreeNode> nodeQ = new LinkedList<TreeNode>();
TreeNode node = root;
int level = 0;
boolean flag = false;
nodeQ.add(node);
while(!nodeQ.isEmpty()) {
node = nodeQ.remove();
flag = false;
if(node.leftChild != null) {
nodeQ.add(node.leftChild);
flag = true;
}
if(node.rightChild != null) {
nodeQ.add(node.rightChild);
flag = true;
}
if(flag) level++;
}
System.out.println(level);
}
However, the code doesn't work for all cases. For example, for the following tree:
10
/ \
4 18
\ / \
5 17 19
It shows the depth as 3, instead of 2.
I did an alternate version of it using an additional Queue to store the current depths, using the idea in this page. I wanted to avoid using an additional queue so I tried to optimize it. Here is the code which works, albeit using an additional Queue.
public void calcDepthIterativeQueue() {
Queue<TreeNode> nodeQ = new LinkedList<TreeNode>();
Queue<Integer> lenQ = new LinkedList<Integer>();
TreeNode node = root;
nodeQ.add(node);
lenQ.add(0);
int maxLen = 0;
while(!nodeQ.isEmpty()) {
TreeNode curr = nodeQ.remove();
int currLen = lenQ.remove();
if(curr.leftChild != null) {
nodeQ.add(curr.leftChild);
lenQ.add(currLen + 1);
}
if(curr.rightChild != null) {
nodeQ.add(curr.rightChild);
lenQ.add(currLen + 1);
}
maxLen = currLen > maxLen ? currLen : maxLen;
}
System.out.println(maxLen);
}
QUESTION:
Is there a way to fix the first method such that it returns the right depth?
EDIT
SEE ACCEPTED ANSWER BELOW
Java code for rici's answer:
public void calcDepthIterative() {
Queue<TreeNode> nodeQ = new LinkedList<TreeNode>();
int depth = 0;
nodeQ.add(root);
while(!nodeQ.isEmpty()) {
int nodeCount = nodeQ.size();
if(nodeCount == 0)
break;
depth++;
while(nodeCount > 0) {
TreeNode topNode = nodeQ.remove();
if(topNode.leftChild != null)
nodeQ.add(topNode.leftChild);
if(topNode.rightChild != null)
nodeQ.add(topNode.rightChild);
nodeCount--;
}
}
System.out.println(depth);
}
Here's one way of doing it:
Create a Queue, and push the root onto it.
Let Depth = 0
Loop:
Let NodeCount = size(Queue)
If NodeCount is 0:
return Depth.
Increment Depth.
While NodeCount > 0:
Remove the node at the front of the queue.
Push its children, if any, on the back of the queue
Decrement NodeCount.
How it works
Every time NodeCount is set, the scan is just about to start a new row. NodeCount is set to the number of Nodes in that row. When all of those Nodes have been removed (i.e., NodeCount is decremented to zero), then the row has been completed and all the children of nodes on that row have been added to the queue, so the queue once again has a complete row, and NodeCount is again set to the number of Nodes in that row.
public int height(Node root){
int ht =0;
if(root==null) return ht;
Queue<Node> q = new ArrayDeque<Node>();
q.addLast(root);
while(true){
int nodeCount = q.size();
if(nodeCount==0) return ht;
ht++;
while(nodeCount>0){
Node node = q.pop();
if(node.left!=null) q.addLast(node.left);
if(node.right!=null) q.addLast(node.right);
nodeCount--;
}
}
How about recurtion,
int Depth(Node node)
{
int depthR=0,depthL=0;
if(Right!=null)depthR=Depth(Right);
if(Left!=null)depthL=Depth(Left);
return Max(depthR,depthL)+1;
}
If tou want a zero based depth, just subtract the resulting depth by 1.

How to implement priority queue using Sorted Linked List?

i want to implement this in c#, i have my linked list class ready, but problem occurs when i sort the class using this Sort method. How can I give priority to elements, so that the highest priority element dequeue first.
public void Sort()
{
ListNode current=first;
int temp;
for (int i = 0; i < counter; i++)
{
while (current.Next != null)
{
if (current.Data > current.Next.Data)
{
temp = current.Data;
current.Data = current.Next.Data;
current.Next.Data = current.Data;
}
current = current.Next;
}
}
}
To analyze your code a little:
This looks like 1 stroke of a bubble-sort, so don't expect it to sort everything.
It will only ensure that the largest value becomes the last element. And only after you fix the swap part:
if (current.Data > current.Next.Data)
{
temp = current.Data;
current.Data = current.Next.Data;
current.Next.Data = temp;
}
I don't think you have to sort the list like this, just create an insert routine that inserts any new elements in the right spot and your list will always be sorted.
Rgds GJ

Select a random element from IList<> other than this one

I have this pattern where I need to select any random element from a list, other than the current one (passed as argument). I came up with this method:
public Element GetAnyoneElseFromTheList(Element el)
{
Element returnElement = el;
Random rndElement = new Random();
if (this.ElementList.Count>2)
{
while (returnElement == el)
{
returnElement = this.ElementList[rndElement.Next(0,this.ElementList.Count)];
}
return returnElement;
}
else return null;
}
But that while loop has been bothering me for days and nights and I need to get some sleep. Any other good approaches to this? ie. something that returns in a fixed-number-of-steps ?
Edit : In my case, the list is guaranteed to contain the "el" Element to avoid, and the list contains no duplicates, but it would be interesting to see some more general cases aswell.
public Element GetAnyoneElseFromTheList(Element el)
{
if(this.ElementList.Count < 2) return null;
Random rndElement = new Random();
int random = rndElement.Next(0,this.ElementList.Count -1);
if(random >= this.ElementList.indexOf(el)) random += 1;
return this.ElementList[random];
}
Get a random number between 0 and the list length minus 2.
If that number is greater or equal to the index of your element add one to that number.
Return the element at the index of that number
Edit:
Someone mentioned in a comment which they then deleted that this doesn't work so well if you have duplicates.
In which case the best solution would actually be.
public Element GetAnyoneElseFromTheList(int elId)
{
if(elId >= this.ElementList.Count) throw new ArgumentException(...)
if(this.ElementList.Count < 2) return null;
Random rndElement = new Random();
int random = rndElement.Next(0,this.ElementList.Count -1);
if(random >= elId) random += 1;
return this.ElementList[random];
}
Edit 2: Another alternative for duplicate elements is you could use an optimised shuffle (random sort) operation on a cloned version of the list and then foreach through the list. The foreach would iterate up to the number of duplicate elements there were in the list. It all comes down to how optimised is your shuffle algorithm then.
public Element GetAnyoneElseFromTheList(Element el)
{
Random rndElement = new Random();
int index;
if (this.ElementList.Count>1)
{
index = rndElement.Next(0,this.ElementList.Count-1);
if (this.ElementList[index] == el)
return this.ElementList[this.ElementList.Count-1];
else
return this.ElementList[index];
}
else return null;
}
I have had to resolve a similar problem. Here's what I would do:
public Element GetAnyoneElseFromTheList(Element el)
{
// take a copy of your element list, ignoring the currently selected element
var temp = this.ElementList.Where(e => e != el).ToList();
// return a randomly selected element
return temp[new Random().Next(0, temp.Count)];
}
First count how many matching elements there are to avoid. Then pick a random number based on the remaining items and locate the picked item:
public Element GetAnyoneElseFromTheList(Element el) {
int cnt = this.ElementList.Count(e => e != el);
if (cnt < 1) return null;
Random rand = new Random();
int num = rand.Next(cnt);
index = 0;
while (num > 0) {
if (this.ElementList[index] != el) num--;
index++;
}
return this.ElementList[index];
}
public Element GetAnyoneElseFromTheList(Element el)
{
// first create a new list and populate it with all non-matching elements
var temp = this.ElementList.Where(i => i != el).ToList();
// if we have some matching elements then return one of them at random
if (temp.Count > 0) return temp[new Random().Next(0, temp.Count)];
// if there are no matching elements then take the appropriate action
// throw an exception, return null, return default(Element) etc
throw new Exception("No items found!");
}
If you're not using C#3 and/or .NET 3.5 then you'll need to do it slightly differently:
public Element GetAnyoneElseFromTheList(Element el)
{
// first create a new list and populate it with all non-matching elements
List<Element> temp = new List<Element>();
this.ElementList.ForEach(delegate(int i) { if (i != el) temp.Add(i); });
// if we have some matching elements then return one of them at random
if (temp.Count > 0) return temp[new Random().Next(0, temp.Count)];
// if there are no matching elements then take the appropriate action
// throw an exception, return null, return default(Element) etc
throw new Exception("No items found!");
}
How about:
public Element GetAnyoneElseFromTheList(Element el)
{
Random rand = new Random();
Element returnEle = ElementList[rand.Next(0, this.ElementList.Count];
return returnEle == el ? GetAnyoneElseFromTheList(Element el) : el;
}
Or in the case you do not like the possibility of a loop:
public Element GetAnyoneElseFromTheList(Element el)
{
Random rand = new Random();
List<Element> listwithoutElement = ElementList.Where(e=>e != el).ToList();
return listwithoutElement[rand.Next(listwithoutElement.Count)];
}

Categories

Resources