I am a newbie to data structure. When you make a linked list with head and tail nodes, why do I have to link new node to tail? Isn't it enough for tail.next to be linked to newNode?
like this:
public void add( Object data ) {
ListNode newNode = new ListNode( data );
if ( isEmpty() ) {
head = newNode;
} else {
tail.setNext( newNode );
}
tail = newNode;
length = length + 1;
}
or like this:
if(head == null) {
head = tail = newNode;
} else {
tail.next = newNode;
tail = newNode;
}
I looked up the basic of LinkedList but I still can't understand this so well. I am not sure how this lets head link to tail and how tail can keep previous value if you replace tail with newNode. Could you please explain this to me? Thank you so much!
Head does not link to tail. You should think of them as separate items. Head points to the start of the list and tail to the end. Let's assume you are only adding and never deleting just to keep things simple.
Head and tail start out empty (pointing to NULL). With the first newNode (let's call it A) added both head and tail are set to A... since it is the only entry it is by definition both the start and end.
When you add a second node (B) then the head stays the same, the current tail.next is set to B (which also sets head.next). And after that the tail is set to B.
Which the third node (C) is added, the head does not change at all. But the tail.next is set to C and the tail moved to C. So we now have A.next = B, B.next =C and C.next is NULL. The head points at A and the tail at C.
When the list has only one node, head and tail point to the same node, so changes to what either point to changes what both point to (until you change head or tail). So in this case, having tail.next point to the new node also makes head.next point to it. When the list is longer, changes to tail won't affect head (and you wouldn't want them to!)
Let's see an illustration:
You have A in your list. You want to add B. When you do so, A points to B as its successor and B turns into the last node (tail).
That is exactly what is going on in your code every time you add a new node. The previous tail passes its place to a new tail but has to keep a reference to the new one so the list can remain interconnected.
I hope it helps,
Best Regards,
Related
I am often wanting to take one list and cons every element into an existing list.
MyList = [3,2,1],
MyNewElements = [4,5,6],
MyNewList = lists:foldl(fun(A, B) -> [A | B] end, MyList, MyNewElements).
%% [6,5,4,3,2,1]
Assuming MyList has 1M elements and MyNewElements only has a few, I want to do this efficiently.
I couldn't figure out which of these functions- if any- did what I was trying to do:
https://www.erlang.org/doc/man/lists.html
Adding a short list to the beginning of a long list is cheap - the execution time of the ++ operator is proportional to the length of the first list. The first list is copied, and the second list is added as the tail without modification.
So in your example, that would be:
lists:reverse(MyNewElements) ++ MyList
(The execution time of lists:reverse/1 is also proportional to the length of the argument.)
Another option, aside from those already provided, would be just to have
NewDeepList = [MyList | DeepList]
and modify the reading/traversing to be able to handle [[element()]] instead of [element()].
Because erlang is function language and is different from c, javascript, it copy variable and modify it, not just modify it. Therefore it is impossible compression to o(A).length(A) is length of new added elements.
I have a linked list. I am writing a function that returns False if there is no loop in my linked list and True if there is.
When I perform the algorithm, I need to have 2 pointers: one is slow and the other is fast.
Algorithm used:
slow will move one step
fast will move two steps
if slow==fast, then loop exists
and to break the loop :
set fast at head, move fast=fast.next and slow=slow.next till fast.next is not equal to slow.next, and then set slow.next as None.
But in the following case my head becomes None. Can you help fixing this?
def detectAndRemoveCycle(head):
if head is None:
return
slow=head
fast=head
while slow and fast and fast.next:
slow=slow.next
fast=fast.next.next
if slow==fast:
fast=head
while fast.next != slow.next:
fast=fast.next
slow=slow.next
slow.next=None
return True
return False
Here is the case when my code fails, as it points my head to None
You are right, this algorithm will not always work when the linked list is circular, i.e. when the head node is part of the cycle.
There are several ways to resolve this. A simple solution is to create a temporary new head node that is prepended to the given head node. Then it will also work for this special case:
def detectAndRemoveCycle(head):
if head is None:
return
head = Node(None, head) # <--- add this
# ...rest of your code
This assumes your Node class is defined with a constructor that takes a second argument, like this:
class Node:
def __init__(self, value, nxt=None):
self.value = value
self.next = nxt
I understand other approaches such as using stack and reversing the second half of the linked list. But, what is wrong with my approach.
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if(head.next==null){return true;}
while(head!=null){
ListNode ptr=head, preptr=head;
while(ptr.next!=null){ptr=ptr.next;}
if(ptr==head){break;}
while(preptr.next.next!=null){preptr=preptr.next;}
if(head.val==ptr.val){
preptr.next=null;
head=head.next;
}
else{return false;}
}
return true;
}
}```
The following can be said about your solution:
It fails with an exception if head is null. To avoid that, you could just remove the first if statement. That case does not need a separate handling. When the list is a single node, then the first iteration will execute the break and so you'll get true as return value. But at least you will not access ->next when head is null
It mutates the given list. This is not very nice. The caller will not expect this will happen, and may need the original list for other purposes even after this call to isPalindrome.
It is slow. Its time complexity is quadratic. If this is part of a coding challenge, then the test data may be large, and the execution of your function may then exceed the allotted time.
Using a stack is indeed a solution, but it feels like cheating: then you might as well convert the whole list to an array and test whether the array is a palindrome using its direct addressing capabilities.
You can do this with just the list as follows:
Count the number of nodes in the list
Use that to identify the first node of the second half of the list. If the number of nodes is odd, let this be the node after the center node.
Apply a list reversal algorithm on that second half. Now you have two shorter lists.
Compare the values in those two lists are equal (ignore the center node if there was one). Remember the outcome (false or true)
Repeat step 3 so the reversal is rolled back, and the list is back in its original state.
Return the result that was found in step 4.
This takes linear time, and so for larger lists, this should outperform your solution.
In linked list implementation, the insertion of a new node to the linked list is usually written like this:
void push(struct node** head_ref, int new_data)
/* 1. allocate node */
struct node* new_node = (struct node*) malloc(sizeof(struct node));
/* 2. put in the data */
new_node->data = new_data;
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
(I took this code from http://quiz.geeksforgeeks.org/linked-list-set-2-inserting-a-node/)
and the node struct is:
struct node
{
int data;
struct node *next;
};
What I don't understand is the 3. and 4. of the insertion part. So you make the next pointer of new_node pointed to the head, and then the head points to the new_node? So that means the next pointer points to new_node?
It seems like a stupid question but I'm having trouble understanding it, so I hope someone can explain it to me. Thank you.
Well basically in a linked list all nodes are connected to each other. It depends upon u that where do u insert a new node either at end or start. Each time we insert a new node we will check the head pointer.
if(head == NULL) //it means that the node is empty
{
head = newNode; //so we will assign the new node to the head
}
else
{
node* temp = head; //creating a temp pointer that will go
// to the end of the linked list
while(temp -> next != NULL) { temp = temp->next; }
temp = newNode; //This will add the new node to the end
newNode->next = NULL;enter code here
}
If I understood correctly this is your scenario?
http://www.kkhsou.in/main/EVidya2/computer_science/comscience/313.gif
Your list is just a linked list with next pointer until list's last item that has null as pointer
Step 3 makes your new node to point to 2nd item that was at beginning of the list before this operation
Step 4 makes the list head to point to the new node
Hope this helps
/* 1. allocate node /
struct node new_node = (struct node*) malloc(sizeof(struct node));
/* 2. put in the data */
new_node->data = new_data;
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
In Step1 and 2, a new node is created and data is assigned to it.
When you list is empty, your *head_ref would be null.
or else if it has any elements, it would be pointing to that
Lets take an example
Now
*head_ref is null
when input is 1
newnode.data=1
newnode.next=null
*headref=newnode
now your *headref points to the latest node that is added ,this happens with step4
When you insert 2
newnode.data=2
newnode.next=*headref(to the node which is 1)
newnode=*headref
now your list is
1->2(*headref)
now if you add 3 here
it becomes
1->2->3(*headref)
Hope you understand
Rather than explaining it to you, I'm going to suggest a technique that will help you work out the answer for yourself.
Get a piece of paper, a pencil and an eraser.
Draw a box on the paper to represent each variable in your algorithm
Draw the initial linked list:
Draw a box to represent each existing node in the initial linked list.
Divide each box into sub-boxes representing the fields.
In each field write either a value, or a dot representing the "from" end of a pointer.
For each pointer, draw a line to the thing (e.g. node) that is pointed to, and put an arrowhead on the "to" end. A NULL pointer is a just a dot.
Now execute the algorithm.
Each time you allocate a new node, draw a new box.
Each time you assign something to a variable, or a field, rub out the current value and write / draw in the new value or the new dot / arrow.
If you do this carefully and systematically, you will be able to visualize exactly what the list insertion algorithm is doing.
This same technique can be used to visualize any list / tree / graph algorithm ... modulo your ability to get it all onto a sheet of paper, and the paper's ability to survive repeated rub-outs.
(This pencil and paper approach is very "old school". As in, this is what we were taught to do when I learned to program in the 1970's. A slightly more modern approach would be to use a whiteboard ...)
First of all head pointer is pointing to first node in list.
In (1) new node is created.
In (2) data is saved to new node.
In (3) The new node is pointing where the head is pointing(means to the first node)
In (4) now the head is made to point to new node, so therefore the new node is now the first node. thats it.
Disclaimer: The author is a newbie in Erlang.
Imagine, we have a graph consisting of 1M nodes, and each node has 0-4 neighbours (the edges are emanating from each node to those neighbours, so the graph is directed and connected).
Here is my choice of data structures:
To store the graph I use digraph, which is based on ETS tables. This allows fast (O(1)) access to the neighbours of a node.
For the list of unvisited nodes, I use gb_sets:take_smallest (the node is already sorted, and it is simultaneously deleted after fetching).
For the list of predecessors I use the dict structure, which allows to store the predecessors in the following way: {Node1,Node1_predecessor},{Node2,Node2_predecessor}.
For the list of visited nodes I use a simple list.
Problems:
The code becomes very hard to read and maintain when I try to update the weight of a node both in the digraph structure and in the Unvisited_nodes structure. It doesn't seem the right way to keep one 'object' with the 'fields' that need to be updated in two data structures simultaneously. What is the right way to do that?
The same question is about predecessors list. Where should I store the predecessor 'field' of a node 'object'? Maybe in the Graph (digraph structure)?
Maybe I should rethink the whole Dijkstra's algorithm in terms of processes and messages instead of objects (nodes and edges) and their fields(weights)?
UPD:
Here is the code based on the recommendations of Antonakos:
dijkstra(Graph,Start_node_name) ->
io:format("dijkstra/2: start~n"),
Paths = dict:new(),
io:format("dijkstra/2: initialized empty Paths~n"),
Unvisited = gb_sets:new(),
io:format("dijkstra/2: initialized empty Unvisited nodes priority queue~n"),
Unvisited_nodes = gb_sets:insert({0,Start_node_name,root},Unvisited),
io:format("dijkstra/2: Added start node ~w with the weight 0 to the Unvisited nodes: ~w~n", [Start_node_name, Unvisited_nodes]),
Paths_updated = loop_through_nodes(Graph,Paths,Unvisited_nodes),
io:format("dijkstra/2: Finished searching for shortest paths: ~w~n", [Paths_updated]).
loop_through_nodes(Graph,Paths,Unvisited_nodes) ->
%% We need this condition to stop looping through the Unvisited nodes if it is empty
case gb_sets:is_empty(Unvisited_nodes) of
false ->
{{Current_weight,Current_name,Previous_node}, Unvisited_nodes_updated} = gb_sets:take_smallest(Unvisited_nodes),
case dict:is_key(Current_name,Paths) of
false ->
io:format("loop_through_nodes: Found a new smallest unvisited node ~w~n",[Current_name]),
Paths_updated = dict:store(Current_name,{Previous_node,Current_weight},Paths),
io:format("loop_through_nodes: Updated Paths: ~w~n",[Paths_updated]),
Out_edges = digraph:out_edges(Graph,Current_name),
io:format("loop_through_nodes: Ready to iterate through the out edges of node ~w: ~w~n",[Current_name,Out_edges]),
Unvisited_nodes_updated_2 = loop_through_edges(Graph,Out_edges,Paths_updated,Unvisited_nodes_updated,Current_weight),
io:format("loop_through_nodes: Looped through out edges of the node ~w and updated Unvisited nodes: ~w~n",[Current_name,Unvisited_nodes_updated_2]),
loop_through_nodes(Graph,Paths_updated,Unvisited_nodes_updated_2);
true ->
loop_through_nodes(Graph,Paths,Unvisited_nodes_updated)
end;
true ->
Paths
end.
loop_through_edges(Graph,[],Paths,Unvisited_nodes,Current_weight) ->
io:format("loop_through_edges: No more out edges ~n"),
Unvisited_nodes;
loop_through_edges(Graph,Edges,Paths,Unvisited_nodes,Current_weight) ->
io:format("loop_through_edges: Start ~n"),
[Current_edge|Rest_edges] = Edges,
{Current_edge,Current_node,Neighbour_node,Edge_weight} = digraph:edge(Graph,Current_edge),
case dict:is_key(Neighbour_node,Paths) of
false ->
io:format("loop_through_edges: Inserting new neighbour node ~w into Unvisited nodes~n",[Current_node]),
Unvisited_nodes_updated = gb_sets:insert({Current_weight+Edge_weight,Neighbour_node,Current_node},Unvisited_nodes),
io:format("loop_through_edges: The unvisited nodes are: ~w~n",[Unvisited_nodes_updated]),
loop_through_edges(Graph,Rest_edges,Paths,Unvisited_nodes_updated,Current_weight);
true ->
loop_through_edges(Graph,Rest_edges,Paths,Unvisited_nodes,Current_weight)
end.
Your choice of data structures looks OK, so it is mostly a question of what to insert in them and how to keep them up to date. I'd suggest the following (I have changed the names a bit):
Result: A dict mapping Node to {Cost, Prev}, where Cost is the total cost of the path to Node and Prev is its predecessor on the path.
Open: A gb_sets structure of {Cost, Node, Prev}.
A graph with edges of the form {EdgeCost, NextNode}.
The result of the search is represented by Result and the graph isn't updated at all. There is no multiprocessing or message passing.
The algorithm goes as follows:
Insert {0, StartNode, Nil} in Open, where Nil is something that marks the end of the path.
Let {{Cost, Node, Prev}, Open1} = gb_sets:take_smallest(Open). If Node is already in Result then do nothing; otherwise add {Cost, Node, Prev} to Result, and for every edge {EdgeCost, NextNode} of Node add {Cost + EdgeCost, NextNode, Node} to Open1, but only if NextNode isn't already in Result. Continue with Open1 until the set is empty.
Dijkstra's algorithm asks for a decrease_key() operation on the Open set. Since this isn't readily supported by gb_sets we have used the workaround of inserting a tuple for NextNode even if NextNode might be present in Open already. That's why we check if the node extracted from Open is already in Result.
Extended discussion of the use of the priority queue
There are several ways of using a priority queue with Dijkstra's algorithm.
In the standard version of Wikipedia a node v is inserted only once but the position of v is updated when the cost and predecessor of v is changed.
alt := dist[u] + dist_between(u, v)
if alt < dist[v]:
dist[v] := alt
previous[v] := u
decrease-key v in Q
Implementations often simplify by replacing decrease-key v in Q with add v to Q. This means that v can be added more than once, and the algorithm must therefore check that an u extracted from the queue hasn't already been added to the result.
In my version I am replacing the entire block above with add v to Q. The queue will therefore contain even more entries, but since they are always extracted in order it doesn't affect the correctness of the algorithm. If you don't want these extra entries, you can use a dictionary to keep track of the minimum cost for each node.