I was simply wondering if the following is the right way to go about it deleting nodes in specific locations in a linked list.
So if my "target" node is located between the head and the tail (middle):
1. set a "curr" node equal to the head
2. Iterate using "curr = curr.next" until curr.next = "target"
3. set curr.next = curr.next.next
If my target is located at the tail I do:
1. set "curr" node equal to the head
2.Iterate until curr.next.next = null
3. set curr.next = null
I also struggle to understand how changing a "curr" node i set equal to "head" can modify the actual linked list associated with "head" and not just the linked list attached to "curr".
Thank you I really need help with this:)
curr references the actual links in the linked list, not just copies of them, so modifying curr also modifies the LinkedList. This is because all java objects are passed by reference.
By the way I don't think thats the right way to distinguish the last item, should be like:
set a "curr" node equal to the head
Iterate using "curr = curr.next" until curr.next .value equals target
if target was found (the previous loop actually fails if target is not in the list)
3.1. if curr.next is the last item: curr.next = null
3.2. else curr.next = curr.next.next
This algorithm is more similar to real linkedlists because usually you don't know in advance if the target link is going to be the last one or not (or even if it's in the list)
Related
This is a common problem of partitioning a linked list into two parts. The list with nodes smaller than x will come first and the list of nodes larger than x will come after.
My question is - why do i need to set after.next to null after creating my two separate linked lists? Without setting it to null, I enter an infinite loop when trying to print this list.
My debugger shows that before_head.next has a never-ending list of nodes attached to it...
public Node partition(Node head, int x){
Node before_head=new Node(0);
Node before=before_head;
Node after_head=new Node(0);
Node after=after_head;
while(head != null){
if(head.val < x){
before.next=head;
before=before.next;
else{
after.next=head;
after=after.next;
}
head=head.next;
}
after.next=null;
before.next=after_head;
return before_head.next;
}
why do i need to set after.next to null after creating my two separate linked lists?
The last node of a linked list doesn't have a next node. In a linked list representation such as yours, that takes the form of the last node's next reference being null.
You are rearranging the existing nodes of the list by changing their next references. At the end of that process, after is a reference to the last node, therefore its next reference needs to be null. If it was also the last node in the original order then all is well -- its next reference is already null. If after was not the last node in the original order, however, then after.next will refer to one of the other nodes until you set it null. And whatever other node that is, it comes before after in the new order, forming a loop.
Note also that
before.next=after_head;
appears to be wrong. after_head is the dummy head node of the second partition, so you do not want to include it in the new list. I think you want
before.next = after_head.next;
I am going through the doubly linked list in Java, I am reading about Sentinels in the doubly linked list from Book. Which states that
In order to avoid some special cases when operating near the
boundaries of a doubly-linked list, it helps to add special nodes at
both ends of the list: a header node at the beginning of the list, and
a trailer node at the end of the list. These “dummy” nodes are known
as sentinels (or guards), and they do not store elements of the
primary sequence
What are those special cases? Why we need Sentinels approach? Is it compulsory? If we use the normal approach (without sentinels) for doubly linked list wouldnt that save the memory of these extra nodes? When making double linked list with circularly approach in that way we have to remove sentinels?
The Wikipedia notes briefly mention using a sentinel node to simplify the implementation of linked lists.
A sentinel node is a dummy node that goes at the front of a list.
In a doubly-linked list, the sentinel node points to the first and last elements of the list. We no longer need to keep separate pointers for the head and tail of the list, like we had to do with singly-linked lists.
We also do not have to worry about updating the head and tail pointers, since as we shall see, this happens automatically if we insert after a sentinel node, hence prepending an item to the list, or insert before a sentinel node, hence appending an item to the list.
We could eliminate the container object that we used for singly linked lists, since the sentinel node can keep track of both the first and last elements in the list. If we did so, then we would return a pointer to the sentinel node to the user.
However, data structures are generally designed with a container object that mediates the communication between the user of the data structure and the implementation of the data structure, so we will retain the container object.
An answer by #6502 on How does a sentinel node offer benefits over NULL? is very helpful.
The following is the code for node deletion in a doubly-linked list of nodes where NULL is used to mark the end of the list and where two pointers first and last are used to hold the address of first and last node:
// Using NULL and pointers for first and last
if (n->prev) n->prev->next = n->next;
else first = n->next;
if (n->next) n->next->prev = n->prev;
else last = n->prev;
and this is the same code where instead there is a special dummy node to mark the end of the list and where the address of first node in the list is stored in the next field of the special node and where the last node in the list is stored in the prev field of the special dummy node:
// Using the dummy node
n->prev->next = n->next;
n->next->prev = n->prev;
The same kind of simplification is also present for node insertion; for example to insert node n before node x (having x == NULL or x == &dummy meaning insertion in last position) the code would be:
// Using NULL and pointers for first and last
n->next = x;
n->prev = x ? x->prev : last;
if (n->prev) n->prev->next = n;
else first = n;
if (n->next) n->next->prev = n;
else last = n;
and
// Using the dummy node
n->next = x;
n->prev = x->prev;
n->next->prev = n;
n->prev->next = n;
As you can see the dummy node approach removed for a doubly-linked list all special cases and all conditionals.
I tried to implement BFS algorithm in JAVA based on what I study but I'm confused a little, I'm not sure if I'm checking if the node is goal or I'm adding nodes to explored list in the appropriate place. Here's the code:
frontier.add(nodes.getFirst());//node.isGoal is only true if the node is desired node
if(frontier.getFirst().isGoal)//Checking if the root node is goal
{
explored.add(frontier.getFirst());
prev.put(frontier.getFirst(), null);
goalNode = explored.getFirst();
frontier.poll();
}
while (!frontier.isEmpty())
{
currentNode = frontier.poll();
explored.add(currentNode);
for (Node node : currentNode.children) {//adding node children to fronier and checking if they are goal
if (!frontier.contains(node) || !explored.contains(node)) {
frontier.add(node);
prev.put(node, currentNode);//mapping nodes to parent node to return sequence of nodes
if (node.isGoal) {
goalNode = node;
break;
}
}
}
}
Thanks in advance.
Based on how I understand your code it seems you're doing it wrong: you check the first node in your initial set (e.g. tree level) and if it is not the goal node you add any children that are not already in frontier and that have not been visited yet. That's ok but probably unnecessary. What's wrong is that you're checking the child before checking any of the parent's siblings so your search is not exactly breadth-first.
So what could/should you do?
Let's assume your data represents a tree (breadth-first indicates that) and you're starting at some level (e.g. the root node). Since the data is a tree in a breadth-first approach any child not cannot have been visited already and is also probably not in your frontier list so there's no need to check for that (if you have a more general graph that might not be the case).
Thus the algorithm could look like this:
LinkedList<Node> frontier = new LinkedList<>();
//assuming you always have a root node just add it
frontier.add( root );
Node goal = null;
//loop until the list is empty, if we found a goal node we'll break the loop from the inside
while( !frontier.isEmpty() ) {
//get the node at the start of the list and remove it
//in the first iteration this will be the root node
Node node = frontier.pop();
//found a goal so we're done
if( node.isGoal ) {
goal = node ;
break; //this breaks the while loop
}
//didn't find a goal yet so add the children at the end
if( node.children != null ) {
frontier.addAll( node.children );
}
}
Doing it this way means you add the nodes of the next level (children) at the end and pop the nodes higher up the tree from the front until you've found what you're searching for. That means you should always have only one or two levels of your tree in the list, i.e. the current level as well as the immediate children of any already processed current level node.
As you also can see there's no need to keep a explored set since we're operating on a tree.
Additionally you might want to think about whether you really need to build that prev map during iteration since you might have to remove elements again and a map is not really suited to get the path from your goal node to the root. You might want to keep a link to the parent in each node instead and thus once you've found the goal node you'd just iterate up until you reach the root.
In my lecture noes its given that if doubly linked list is empty then
header.next==tail;
Or
tail.prev==header;
But I feel like this is the case when there's exactly two nodes. Shouldn't the empty case be
head==null.
I don't know if I am correct.I am sill new to this subject.Can someone please clarify this
There are two common ways to store a double linked-list:
Let head and tail refer to actual nodes in the tree.
head variable tail variable
| |
V V
first -> second -> third -> null
In this case, you are correct - head will be null.
Let head and tail be special nodes (called "sentinel nodes", as pointed out in the other answer) which don't actually contain any data and point to the first and last nodes in the list through next and prev respectively.
head variable tail variable
| |
V V
head -> first -> second -> third -> tail -> null
node node
In this case, header.next == tail will mean the list is empty.
One might use this approach instead of the above one to simplify the implementation a bit and make the code a bit faster - there's no need to make special provision for removing or inserting the first or last nodes.
This doubly-linked list is using both head and tail sentinel nodes. These are nodes that don't actually contain elements of the list, but help to make certain algorithms slightly nicer by making the head and tail cases work more like the middle of the list.
If the list weren't using sentinels, then the empty case would be head==null, and the two-element case would have header.next==tail and tail.prev==header.
If I have created a linked list where order of insertion is 5,4,3. I use the head reference so the linked list gets stored as 3->4->5->null.
When I want to reverse the linked list == original order of insertion so it will be
5->4->3->null. Now if this is how my new list looks like, which node should my head reference be referring to so the new elements I add to the list will still have O(1) insertion time?
I think head, by definition, always points to the first element of a list.
If you want to insert to the end of a linked list in O(1) then keep two references, one to the first element and one to the last element. Then adding to the end is done by following the last reference to the last element, add the new element beyond the last element, update the last reference to point to the new last element.
Inserting to an empty list becomes a special case because you have to set both first and last references not just the last reference. Similarly for deleting from a list with one element.
If you want to the back of a singly linked list, you should keep a reference to the last element. When you want to insert a new element, you create a new link object with the new element as head, the tail of the element you insert it after as the tail, and the new link object as the new tail of the element you insert it afterward. This takes three pointer movements and thus constant time.
For any linked list to have an O(1) insertion time, you have to insert into the front of the list or some other arbitrary location completely disregarding any order. Being a singly linked list means that you can't point to the last element in the list because the list up until the last element will be inaccessible. A fix to this might be as Shannon Severance has stated in that you keep two pointers, a head and a tail. You can use the head to access the elements and the tail to arbitrarily add elements to the list.
Think of it this way:
The reverse of a list consisting of HEAD -> [the rest of the list] is precisely: reverse([the rest of the list]) -> HEAD.
Your base case would be if the list contains a single element. The reverse of such a list is just itself.
Here's an example (using executable pseudo-code, aka Python):
class Node(object):
def __init__(self, data, next_=None):
self._data = data
self._next = next_
def __str__(self):
return '[%s] -> %s' % (self._data,
self._next or 'nil')
def reverse(node):
# base case
if node._next is None:
return node
# recursive
head = Node(node._data) # make a [HEAD] -> nil
tail_reverse = reverse(node._next)
# traverse tail_reverse
current = tail_reverse
while current._next is not None:
current = current._next
current._next = head
return tail_reverse
if __name__ == '__main__':
head = Node(0,
Node(1,
Node(2,
Node(3))))
print head
print reverse(head)
Note that this is not in O(1) due to the lack of a last-element reference (only HEAD).