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.
Related
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.
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'm creating a special tree algorithm and I need a bit of help with the code that I currently have, but before you take a look on it please let me explain what it really is meant to do.
I have a tree structure and I'm interacting with a node (any of the nodes in the tree(these nodes are Umbraco CMS classes)) so upon interaction I render the tree up to the top (to the root) and obtain these values in a global collection (List<Node> in this particular case). So far, it's ok, but then upon other interaction with another node I must check the list if it already contains the parents of the clicked node if it does contain every parent and it doesn't contain this node then the interaction is on the lowest level (I hope you are still with me?).
Unfortunately calling the Contains() function in Umbraco CMS doesn't check if the list already contains the values which makes the list add the same values all over again even through I added the Contains() function for the check.
Can anyone give me hand here if he has already met such a problem? I exchanged the Contains() function for the Except and Union functions, and they yield the same result - they do contain duplicates.
var currentValue = (string)CurrentPage.technologies;
List<Node> globalNodeList = new List<Node>();
string[] result = currentValue.Split(',');
foreach (var item in result)
{
var node = new Node(int.Parse(item));
if (globalNodeList.Count > 0)
{
List<Node> nodeParents = new List<Node>();
if (node.Parent != null)
{
while (node != null)
{
if (!nodeParents.Contains(node))
{
nodeParents.Add(node);
}
node = (Node)node.Parent;
}
}
else { globalNodeList.Add(node); }
if (nodeParents.Count > 0)
{
var differences = globalNodeList.Except<Node>(globalNodeList);
globalNodeList = globalNodeList.Union<Node>(differences).ToList<Node>();
}
}
else
{
if (node.Parent != null)
{
while (node != null)
{
globalNodeList.Add(node);
node = (Node)node.Parent;
}
}
else
{
globalNodeList.Add(node);
}
}
}
}
If I understand your question, you only want to see if a particular node is an ancestor of an other node. If so, just (string) check the Path property of the node. The path property is a comma separated string. No need to build the list yourself.
Just myNode.Path.Contains(",1001") will work.
Small remarks.
If you are using Umbraco 6, use the IPublishedContent instead of Node.
If you would build a list like you do, I would rather take you can provide the Umbraco helper with multiple Id's and let umbraco build the list (from cache).
For the second remark, you are able to do this:
var myList = Umbraco.Content(1001,1002,1003);
or with a array/list
var myList = Umbraco.Content(someNode.Path.Split(','));
and because you are crawling up to the root, you might need to add a .Reverse()
More information about the UmbracoHelper can be found in the documentation: http://our.umbraco.org/documentation/Reference/Querying/UmbracoHelper/
If you are using Umbraco 4 you can use #Library.NodesById(...)
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.
I need to iterate through a LinkedList<T> (in .NET 2.0) and remove all the items according to a given criteria.
It was easy way under Java, since I could do the following:
Iterator<E> i = list.iterator();
while (i.hasNext()) {
E e = i.next();
if (e == x) {
// Found, so move it to the front,
i.remove();
list.addFirst(x);
// Return it
return x;
}
}
Unfortunately, in the .NET behavior of IEnumerator<T> (the equivalent of Iterator<E>) there's no remove method to remove the current element from the collection.
Also, in the LinkedList<T> there's no way to access an element at a given index, to accomplish the task by iterating back from the last to the first.
Have you got any idea on how to do it? Thank you very much!
This will remove all nodes that match a criteria, in one loop through the linked list.
LinkedListNode<E> node = list.First;
while (node != null)
{
var next = node.Next;
if (node.Value == x) {
list.Remove(e);
}
node = next;
}
I believe that's what you're attempting... You also added back in the node at the beginning of the list (so your java code didn't remove all of the nodes, but rather moved the first matching to the beginning of the list). That would be easy to do with this approach, as well.
It's actually a lot easier in C#.
function PlaceAtHead(<T> x)
{
list.Remove(x);
list.AddFirst(x);
return x;
}
One ugly option is to iterate through your list, find all the items that apply and store them in a list. Then iterate through your second list and call remove on your LinkedList...
I'm hoping someone else has a more elegant solution :)
Just a little addition to Reed Copsey's answer with a predicate:
public static T MoveAheadAndReturn<T>(LinkedList<T> ll, Predicate<T> pred)
{
if (ll == null)
throw new ArgumentNullException("ll");
if (pred == null)
throw new ArgumentNullException("pred");
LinkedListNode<T> node = ll.First;
T value = default(T);
while (node != null)
{
value = node.Value;
if (pred(value))
{
ll.Remove(node);
ll.AddFirst(node);
break;
}
node = node.Next;
}
return value;
}