I was browsing the .NET Reference to see the internal workings of a LinkedList<T> and its corresponding LinkedListNode<T> elements when I noticed that I didn't quite understand how the AddLast(T item) and AddLast(LinkedListNode<T> node) methods worked. I will consider AddLast(T item) to address my confusion.
The way I see it, the method first creates a new node that refers to the current list and holds the required value to be added. Then it checks if the list is empty by looking at head, the supposed first item ever added to the list.
A) head is null
Calls a method called InternalInsertNodeToEmptyList(LinkedListNode<T> newNode) where newNode in this case will be result from the scope surrounding this method. It checks if the list is actually empty and then modifies the node to be added to link to itself in both directions, before setting it as the head of the list. Also updates the count and the version of the list.
private void InternalInsertNodeToEmptyList(LinkedListNode<T> newNode)
{
Debug.Assert( head == null && count == 0, "LinkedList must be empty when this method is called!");
newNode.next = newNode; // i.e. result.next = result
newNode.prev = newNode; // i.e. result.prev = result
head = newNode; // i.e. head = result
version++;
count++;
}
B) head is NOT null
Calls a method called InternalInsertNodeBefore(LinkedListNode<T> node, LinkedListNode<T> newNode) where node is head and newNode is result from the scope surrounding this method. First it checks if the list isn't empty, but then the actual logic of adding the new node follows:
private void InternalInsertNodeBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
newNode.next = node; // result.next = head
newNode.prev = node.prev; // result.prev = head.prev (but isn't head.prev just head?)
node.prev.next = newNode; // head.prev.next = result (so result gets added after head?)
node.prev = newNode; // head.prev = result (why does head link back to result?)
version++;
count++;
}
I really don't understand how this results in inserting a new link after the last known link of the list. Why does this not result in adding a link after head rather than after the last known link?
LinkedList<T> is a doubly-linked list: each node has references to the nodes before and after.
head is the first element in the list, but its prev member points to the last element in the list. You can see this from the Last property:
public LinkedListNode<T> Last {
get { return head == null? null: head.prev;}
}
This relationship between the last element of the list and head is two-way: head.prev is the last element in the list, and the last element in the list's next is head.
So:
// Remember that 'node' is the 'head'. I've replaced 'node' with
// 'head' to make this clearer.
// The new node's 'next' points to 'head', as it should
newNode.next = head;
// The new node's 'prev' points to the old last element in the list
// (remember that 'head.prev' is the last element in the list)
newNode.prev = head.prev;
// 'head.prev' is the old last element in the list. This is now the second-last
// element in the list, and its 'next' should be our new node. This
// is the reciprocal of 'newNode.prev = head.prev'
head.prev.next = newNode;
// head.prev now points to the new node, as it should. This is the reciprocal of
// 'newNode.next = head'
head.prev = newNode;
To attempt to draw the 'before' state:
LinkedList
|
v
oldLastNode <-- prev -- head
-- next -->
After:
LinkedList
|
v
oldLastNode <-- prev -- newNode <-- prev -- head
-- next --> -- next -->
So we want to do:
oldLastNode.next = newNode
newNode.prev = oldLastNode
newNode.next = head
head.prev = newNode
From the first diagram, we can see that oldLastNode is the old head.prev. So as long as we make changes to head.prev before we re-assign head.prev, we can write this as:
head.prev.next = newNode
newNode.prev = head.prev
newNode.next = head
head.prev = newNode
These are the same operations as in your question, but re-arranged.
Related
I am trying to make an enumerator that iterates over a sublist within a linked list. To do this, I'm explicitly keeping track of the two nodes Head and Tail that bound the sublist. My goal is to be able to call a foreach loop over the linked list, to iterate over all the elements of the sublist.
What foreach wants to do:
The foreach loop first initializes an enumerator, in my case with the required list list and a variable linked list node node from which it will ask the Value during the loop.
Once the loop has been initialized, it repeats the following pattern:
Call the MoveNext() method, which 'increments' the list
Retrieve Current and return it to wherever it's called
Repeat until MoveNext() == false.
How I've currently written my enumerator
Since the first step is to move, rather than to return a value, I figured I could initialize my enumerator like this:
this.list = list;
this.node = list.Head.Previous // Initialize node to be the element before Head
Where Head is the first element of the sublist to be shown.
Then, I tried to implement the logic for MoveNext() and Current like this:
public T Current => node.Value;
object IEnumerator.Current => Current;
public bool MoveNext()
{
node = node.Next;
return node != list.Tail.Next;
}
Where Tail is the last element of the sublist to be shown.
But in some cases, because this implementation of a linked list is circular, list.Tail.Next can be list.Head, meaning that MoveNext() will return false without even getting the chance to return a single value.
Is there a way to refactor the enumerator to make sure it iterates over the entire sublist instead of just short-circuiting, both when list.Tail.Next does and doesn't point to list.Head?
From your description, there seems to be no cases where the first call to MoveNext would return false.
You could just add an extra piece of state called Moved:
private bool Moved { get; set; } = false;
public bool MoveNext()
{
node = node.Next;
var retVal = !Moved || node != list.Tail.Next;
Moved = true;
return retVal;
}
An extra state is definitely needed here, because if you only have one element, we have no way to tell whether we have iterated over it or not, as node = node.Next does not change the state of the enumerator.
Did you try:
public bool MoveNext()
{
bool returnValue = node != list.Tail;
node = node.Next;
return returnValue;
}
I have been struggling trying to develop my own singly linked list, I cant understand how a node is inserted at the end of a Linked List?
Here's the code:
class LinkedList
{
private Node head;
public void AddLast(int value)
{
if (head == null)
{
head = new Node();
head.value = value;
head.next = null;
}
else
{
Node temp = new Node();
temp.value = value;
Node current = head;
while (current.next != null)
{
current = current.next;
}
current.next = temp;
}
}
public void PrintAll()
{
Node current = head;
while (current != null)
{
Console.WriteLine(current.value);
current = current.next;
}
}
}
Here is the main method
static void Main(string[] args)
{
LinkedList list = new LinkedList();
list.AddLast(3);
list.AddLast(5);
list.AddLast(4);
}
1) I totally get the first part list.AddLast(3). Since head is null, we create a new node head and assign values to it.
2) When list.AddLast(5) is called, head is no more null, and thus we create a new temporary node, assign values to it. NOW we create a new node current which holds values of Head, it is to be noted Head.Next was null.
Now we iterate through current and place our temp node to current.Next.
3) Now, Upon calling list.AddLast(5) shouldn't the current be again overwritten with the contents of Head? which was Head.Value = 3 and Head.Next = Null.
So shouldn't it current.Value = 3 and Current.Next = Null? And if not then why?
Node current = head;
When the above statement executes, current is assigned only the reference of head temporarily and not the value. So current only points to head at that moment. current.value will give you the value for head if executed after this statement.
while (current.next != null)
{
current = current.next;
}
Now, when the above while loop executes, it iterates through the linked list and takes the current node to the last node in the linked list which will have current.next = null.
current.next = temp;
When the above statement executes, the new node is added to the last of the linked list.
You create a new instance of LinkedList.
list.AddLast(3); --> head is null, you create a new Node and assign it to head.
list.AddLast(5); --> Create a new Node temp and assigns 5 to the value. Keep in mind that head's value = 3 and next is null. A new variable is created called current that points to head. The while then traverses the tree and if current's next is not null, then reassign current's next to that node. In this case, the while loop is not run because head's next is null, so all it does is just assign head.next to your current node. At this point we have head.value = 3, head.next = new Node(5, null);
list.AddLast(4); --> Same as above but the while loop now runs. It traverses all the way to find a Node that has no next value and assigns 4 to it. At this point we have head.value = 3, head.next = new Node(5, new Node(4, null))
There are oodles of implementations of linked lists. However for a simple singlely Linked list we think of it like thus
If i understand you correctly, this is the part you don't understand
// All this part of the method does is add the new value to the end of the list
// create node
Node temp = new Node();
// assign its value
temp.value = value;
// now we want to start with the head and follow
// the chain until we find the last node
Node current = head;
// has it got a next element
while (current.next != null)
{
// if so change current to next and look again
current = current.next;
}
// yay no more nexts, lets add temp here
current.next = temp;
// you have effectively added your new element to the end of the list
On saying that another common implementation is to save the Last/Current Node in the class (just like you did with head).
In that case, instead of iterating through the chain we can reference that Node and just add to its next and update Last reference in the class. However when we have to remove the last item, we have to remember not only to null Last in the class but null its parents Next reference as well.
The only way to do this is once again iterate the chain.
To make that easier we can implement a doubly linked list so its easier to find its parent
Update
what I don't understand is that why isn't the link to next nodes lost
when current who holds that info is updated every time with content of
Head.
In the above, think of current as just a temporary variable, its sole job in life is just like a temporary marker of the next link in the chain,
When we call this
current = current.next;
All its doing its grabbing whats in current.next and holding on to it for the loop condition again. We can call this without destroying whats in current.next
On the last time, when we have found nothing in current.next
i.e when the loop checks that current.next== null we can safely put our temp node into it current.next = temp; <== it was empty anyway
I have problem with method MoveNext in my enumerator. I need iterator for binary search tree. In construcotr of my enumerator I initialize Node to root of tree.
Current is value that I ened to return for next item. This code for method moveNext return wrong values.
public bool MoveNext()
{
if (Current == null)
Current = node.Value;
else if (node.Left != null)
{
node = node.Left;
Current = node.Value;
}
else if (node.Right != null)
{
node = node.Right;
Current = node.Value;
}
else
{
node.Value = Current;
do
{
if (node.Parent == null)
return false;
node = node.Parent;
} while (node.Right == null);
Current = node.Value;
}
return true;
}
I see a few issues with that code. First, in the else-branch, you are changing the value of a node in the tree - You probably meant to write Current = node.Value; instead of node.Value = Current;.
However, that's not the main issue. Your iterator will get stuck in an infinite loop really easily. Everything looks reasonable for traversing down, you take the leftmost path down to a leaf node.
Then you backtrack up until you find an ancestor node which has a Right child and yield the value of that node. However, this value was already returned by the iterator on the way down. Also, you don't remember which path you already traversed down, so on the next step you will inevitably follow the same path down again that you took before, then you'll backtrack up again and so on, ad infinitum.
In order to fix this, don't stop at the parent node when you backtrack - take the first step down the next path already. It is true that this will always be the Right child of some node, but it is not necessarily the Right child of the first ancestor that has one, because you might already be backtracking up from that branch.
So to summarize: If you can't go down any further, backtrack up one level. Check if you are coming from the Left or the Right child node. If you came from the left one, go down the right one if it exists, and set Current to its value. If it doesn't, or if you already come from the right child, recurse upwards another level.
Your enumerator modifies the tree:
Node.Value = Current;
Enumerators shouldn't do that.
In the last else you are changing the value of the node to the same value as the current node:
node.Value = Current;
I think that you tried to put the current node in the node variable, but that's not done by putting the current value in the value of the current node, and it's not needed as the node variable already contains the current node.
As Medo42 pointed out, if you are coming from the Right node, all children of that parent has already been iterated, so you should check for that when looking for a parent to continue iterating:
} while (node.Right == null || node.Right == last);
When you have looped up the parent chain to find the right parent, you are using the parent instead of getting the child:
node = node.Right;
So:
public bool MoveNext() {
if (Current == null)
Current = node.Value;
else if (node.Left != null)
{
node = node.Left;
Current = node.Value;
}
else if (node.Right != null)
{
node = node.Right;
Current = node.Value;
}
else
{
Node last;
do
{
if (node.Parent == null)
return false;
last = node;
node = node.Parent;
} while (node.Right == null || node.Right == last);
node = node.Right;
Current = node.Value;
}
return true;
}
I posted my solution here
Binary Search Tree Iterator java
Source code
https://github.com/yan-khonski-it/bst/blob/master/bst-core/src/main/java/com/yk/training/bst/iterators/BSTIterator.java
Here is algorithm how to do so (you can implement it in any language you like then).
ArrayIterator
BSTITerator what uses an array under the hood.
Perform inorder traversal and collect visited nodes into a list.
ArrayIterator. Now it works similar to list iterator, which is easy to implement.
next() and hasNext() - have constant time complexity; however, this iterator requires memory for N elements of the tree.
StackIterator
All nodes to be returned in next() call are stored in stack. Each time, you return next node, you remove element from the stack (let's name it currentNode).
If currentNode has a right child (you have already returned left), you put the right child into the stack. Then you need to iterate left subtree of the right child and put all left elements into the stack.
First off we have the almighty code!
List nodes = new List();
TreeNode Temp = new TreeNode();
TreeNodeCollection nodeList = treeViewTab4DirectoryTree.Nodes;
while (nodeList.Count != 0)
{
Temp = nodeList[0];
while (Temp.FirstNode != null)
{
Temp = Temp.FirstNode;
}
if (!nodes.Contains(Temp.FullPath))
{
nodes.Add(Temp.Text);
}
nodeList.Remove(Temp);
}
Now the problem: I have written the code above with the intent of creating a List containing the text from all the nodes in the tree. That works perfectly. The problem I am having is when I remove the nodes from my variable they are also being removed from the actual list. The question would be how can I make a copy of the list of nodes so I can play with them without messing with the ACTUAL list. How do I make a copy of it without just making a reference to it? Any help will be greatly appreciated!
Your problem arises because "nodeList" is a reference to treeViewTab4DirectoryTree.Nodes, rather than a copy of it.
The solution depends entirely on the what type of TreeNodeCollection you're using (WinForms, ASP.net, something else?), as you'll need to look for a .Copy(), .Clone(), .ToArray() method or similar to take a copy of the contents of the collection, rather than a reference to the existing collection.
If, for example, you're using asp.net and thus the System.Web.UI.WebControls.TreeNodeCollection, you could use the .CopyTo method in a way similar to this:
TreeNode[] x = null;
treeViewTab4DirectoryTree.Nodes.CopyTo(x, 0);
Updated to show stack based approach:
List<String> result = new List<String>();
Stack<IEnumerator> nodeColls = new Stack<IEnumerator>();
IEnumerator nodes = treeViewTab4DirectoryTree.Nodes.GetEnumerator();
nodeColls.Push(null);
while (nodes != null)
{
while (nodes.MoveNext())
{
result.add(nodes.Current.FullPath);
if (nodes.Current.FirstNode != null)
{
nodeColls.Push(nodes);
nodes = nodes.Current.Nodes.GetEnumerator();
}
}
nodes = nodeColls.Pop();
}
The code below does not work as was mentioned in comments, because it doesn't traverse the entire tree, but only takes the first leaf node of each top-level branch.
I actually thought the original code (in the question) did so too, because I thought the Remove would actually remove the top-level node after finding the first leaf node under it; but instead, it tries to remove the leaf node from the collection of top-level nodes, and just ignores it if it can't find it.
Original post, non-functioning code
First of all, why do you need to remove the items from your list?
List<string> nodes = new List<string>();
foreach (TreeNode tn in treeViewTab4DirectoryTree.Nodes)
{
TreeNode temp = tn;
while (Temp.FirstNode != null)
{
Temp = Temp.FirstNode;
}
if (!nodes.Contains(Temp.FullPath))
{
nodes.Add(Temp.Text);
}
}
To answer your concrete question, assuming the Nodes collection implements IEnumerable, use:
List<TreeNode> nodeList = new List<TreeNode>(treeViewTab4DirectoryTree.Nodes);
If you do decide to stick with your while loop, you can save an instatiation by changing
TreeNode Temp = new TreeNode();
to
TreeNode Temp = null;
... you're never actually using the object you create, at least in the part of the code you've shown.
I have made my own single chained/linked list.
Now, if I want to delete/remove a node/item from my list, I'd have to do something like this:
public void Delete(PARAMETERS)
{
Node previousNode = null,
currentNode = f;
while (currentNode != null)
{
if (SOMECONDITION)
{
if (previousNode == null)
{
f = currentNode.Next;
}
else
{
previousNode.Next = currentNode.Next;
}
}
else
{
previousNode = currentNode;
}
currentNode = currentNode.Next;
}
}
If SOMECONDITION is true, you simply skip the currentNode and therefor effectively "deleting" the node, as nothing points to it anymore.
But, I am really wondering, why can I not do something like this:
(...)
while ()
{
if (SOMECONDITION)
{
currentNode = currentNode.Next;
}
currentNode = currentNode.Next;
}
(...)
OR perhaps:
(...)
while ()
{
if (SOMECONDITION)
{
currentNode = currentNode.Next.Next;
}
else
{
currentNode = currentNode.Next;
}
}
(...)
What fundamental understanding do I lack?
Doing:
currentNode = currentNode.Next.Next;
Is a prime candidate for a NullReferenceException
EDIT:
Here's a list implementation with some pictures that may help you understand.
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=176
There is nothing to say you can't do Next.Next.
The only issue is what if currentNode.Next is null? Then you would get an error.
PreviousNode works because you are doing a NULL check before using it.
currentNode is just a temporary pointer variable (reference) that ceases to exist at the end of the scope (that is by the next closing brace). When you change what that reference points to, you don't change any other references; changing currentNode doesn't magically change what the previous node's Next reference points to.
currentNode = currentNode.Next // only changes the temporary reference
You have to actually reach into the linked list and change a referende inside the list, which is what you do when you change previousNode.Next - you change what node the previous node considers its next node. You basically tell it "This is your new Next node, forget about the old one".
Also, as the others have stated, you should check for null references throughout. if currentNode.Next is the last node in the list, its Next will point at nothing, and you'll get a NullReferenceException.
Perhaps if you re-write the original a bit you would see better what you are really doing to the list.
public void Delete(PARAMETERS)
{
var previous = FindPreviousNode(PARAMETERS);
if( previous == null && Matches(f, PARAMETERS)) {
f = f.Next;
} else if(previous != null ) {
previous.Next = previous.Next.Next;
} // u could add "else { throw new NodeNotFound() }" if that's appropiate
}
private Node FindPreviousNode(PARAMETERS) {
Node currentNode = f;
while (currentNode != null) {
if (Matches(currentNode.Next, PARAMETERS)) {
return currentNode;
}
currentNode = currentNode.Next;
}
return null;
}
You have asked around in the comments to understand more what's up with the list and the Next's properties, so here it goes:
Lets say the list is: 1|3|5|7, first points to 1, 1's Next property points to 3, 5's Next points to 7, and 7's Next points to null. That's all you keep track of to store the list. If you set the 5's Next property to null, you are deleting the 7. If instead you set 3's Next property to 7, you are deleting the 5 from the list. If you set first to 3, you are deleting the 1.
Its all about the first and the Next properties. That's what makes the list.
The assignments to currentNode and previousNode do not alter the structure of the linked list. They're merely used to step through the structure.
The assignment to previousNode.Next is what changes the structure. Doing currentNode = currentNode.Next.Next will skip over the next node (if currentNode.Next is not null) but it won't alter the structure of the list.
You should really sketch a picture of the linked list if you're wondering about problems like this. It's far easier to see what needs to be done to accomplish some linked list mutation, than it is to reason it out.
Honestly, I don't follow the posted code at all.
If this is a standard linked list (each node has a Next, but that's it), follow these steps to run a deletion of a single item:
Step 1: Find the target node you want to delete, but keep track of the previous node visited.
Step 2: prevNode.Next = targetNode.Next
Note: special checks for deleting the head of the list need to be done.
How do you know in both cases that currentNode.Next is not null and thus that you can apply .Next on it? You are only checking for the != null in the loop condition.