Find the common nodes of two intersecting linked lists - c

I was asked in the interview that if we have two linked list which intersect in more that one node then how can we find the common nodes in which the linked list meet. Also find the solution with minimum complexity.
e.g.
![Linked List example][1]
Linked List 1 = 11->12->13->14->15->16->17->54
Linked List 2 = 23->24->13->26->14->15->35->16->45
I answered him that we can store addresses of one linked list in hashmap and compare every node's address in second list with the hashmap. This way we can achieve O(n) complexity. But the interviewer was not satisfied.
Please suggest any better solution. Thanks in advance.

it can be achieved in a better way listen if two linked list are intersecting at some node so we can traverse ones both the list find the length of each list then move the pointer of one list upto the distance between the two &then move both the pointer simultaneouly int his way whenever u get the that node both the pointers are equal..
Given two singly linked list, find if they are intersecting. Do this in single iteration.
a. traverse list1 and find the last element
b. traverse list2 and find the last element
c. check if last element of list1 == last element of list2 , if equal intersecting else not
here we have parsed the list only once :-)
Also find the intersecting node in O(n) time and O(1) space
here they have asked to do it in O(1) space so we need to use only one variable :-)
a. create a variable(int) diff=0
b. parse list1 and increment diff for each node
c. parse list2 and decrement diff for each node
d. if diff is > 0 list1 is bigger so push the pointer of list1 by diff times
else list2 is bigger so push the pointer of list2 by mod(diff) times
e. Now check if both the pointers are equal till we reach end

If the values are integers and you have unlimited memory, you can perform the following:
Traverse each list once and find the global maximal value MAX
Allocate a boolean array A with the size of MAX
Traverse one list, for each value X in the list set A[X] = true
Traverse the second list, for each value Y in the list if A[Y] = true then Y is a list intersection
This runs with O(N) time (which I believe you can't do better as the lists are not sorted)

my suggested solution:
you create a hashmap,
iterate the first list, and for each element you do:
hashMap.put({value}, "firstList");
on O(n) you get a map of elements.
iterate the second list,
for each element ask:
hash_map.containsKey({number}) ?
if so, intersectionCounter ++;
the best is O(N) i think.
this is what i would do.

Related

Binary vs Linear search in sorted doubly linked lists

I am doing a college project on library management using doubly linked lists. The doubly linked list holds the ID'S of books whilst being sorted.
I have tried to calculate time elapsed for the worst case for linear vs binary search. Following results:
Binary search: 0.311ms
Linear search: 0.228ms
[Number of inputs(id's): 10000000]
MY QUESTION:
Even though binary search takes O(logn) comparisons, time elapsed was more due to the fact, it took O(n) traversals until the middle value is found. Is there any better search algorithm for a sorted doubly linked list rather than cumbersome linear search?
My implementation for finding middle value required for binary search:
struct node* middle(node* start, node* last)
{
if (start == NULL)
return NULL;
struct node* slow = start;
struct node* fast = start -> next;
while (fast != last)
{
fast = fast -> next;
if (fast != last)
{
slow = slow -> next;
fast = fast -> next;
}
}
return slow;
}
Your compare would have to to spectacularly slow to justify all that navigation. As it stands I cannot think of a better way than a linear search. If you can alter the structures and CRUD you can certainly index key points ("A" starts here, "B" starts here etc.) and that would allow you to better guess the start and direction of your linear search.
I think you'll find that a linked list, doubly or otherwise, is not a great choice for random lookups or updating in order. Use a B-Tree which seems to be a better fit for the situations you've outlined in your question and comments.
time elapsed was more due to the fact, it took O(n) traversals until the middle value is found.
When you insert new elements in the linked list you could also track the middle element like you're doing with the first and last one. Although the insert function will be more complex.
I would use a struct for the linked list with 4 fields:
start node
middle node
last node
length
Binary search achieves O(log N) complexity in the number of comparisons. When used with an array, accessing the i-th element of the array is performed in constant time, hence not affecting the overall time-complexity.
With a list, singly or doubly linked, accessing the i-th element takes i steps. In your example, accessing the middle element takes a number of steps proportional to the length of the list. As a consequence, the complexity of this search is still O(log N) comparisons but O(n) for the selection of the items to compare, which becomes the dominating factor.

K way merge of sorted arrays implementation using a min heap

I've been trying to figure out how to implement the following algorithm for a K way merge.
Algorithm:
1)Initialize an array of size n*k.
2) Initialize a min heap of size k, to hold the smallest element of each array.
3) Add the smallest element from the minHeap into the output array.
4)Move the next element from the array from which the min element was derived onto the heap. // How would one implement this?
5) Repeat step 4 until all the arrays are empty and the minHeap is empty.
I've been able to implement all but Step 4 of my algorithm. How would one track the array from which the smallest element has been extracted?
Try keeping element and array in pair. Element would be key, array (pointer to it) would be value. Pair should be sortable by it's keys.
When you extract the smallest pair from the heap, you take the key from the pair as the element you wanted, and value in that pair will be array containing that element.
Important: depending of the language you're working with, don't copy the arrays as values, but save only pointers to them (let's say in C++), or their references (i.e. Java).

Comparing nodes of a list with a string array

If i have an array that contains some char such as [a,b,c] and i have another array that contains the respective frequency of each char such as [2,1,1]. I would like to now go through a linked list which has nodes which have some string to see if they also have the chars i have in my original array with same frequency.
My approach
I was thinking i need
One loop that will start at index 0 of original array and another loop inside that will check all nodes for that string and if my temp pointer hits null it means all of them have it and if not then they don't and i move on to the next one. However i am not sure how to quite implement this approach as i am very new to c and also i was wondering is it possible to do this in O(N) TIME as my approach would be O(N2).
Sample Output: i apologize for the confusion
so if you have 3 nodes and each has a char array containing "nba" "tba" "rba"
the output should then return b a . since both them appear equal number of times in each node.
So you start both your char array and freqarray at index 0 and then check all the nodes for strings matching the same frequency of a character . I presume you use some kind of function to return frequency of particular char in a string .
Also your problem requires you to go through all of the nodes hence O(N^2) is implied.

Efficiency of an unsorted vs sorted linked list in C

For a programming project, I created two linked list programs: an unsorted linked list and a sorted linked list. The unsorted linked list program adds values to the end of the list as long as the value is not found in the list. If the value is found in the list, the node containing the value is removed. The only difference in the sorted linked_list program is that if a value is not found in the list, instead of just adding the value to the end, the program looks for the proper space to insert the value so that the repository is consistently maintained in sorted order. I have a "stepcounter" variable that basically increments each time a pointer in my program is reassigned to a point to a different pointer, even during traversal of the linked list. I output this variable to the screen to give me an idea of the efficiency of my program. What's strange is that if I run the same operation on the sorted list and on the unsorted list, the number of steps, or, effort of the unsorted list is MORE than the sorted list. This seems very counter-intuitive to me but I looked through my code and I'm pretty sure I incremented in all the same places, so I can't come up with an explanation as to why the unsorted linked list operations would have more steps than the sorted. Is there something I'm missing?
If you are really keeping track of pointer assignments, then walking like
while (p && (p.value != input) && (p.next != NULL)) p = updatePointer(p.next);
(assuming updatePointer takes care of your counting) performs one of those for each node you examine.
To know if a item is in the unsorted list you have to look at every node in the list. (That is, you have to use the code I had above)
To do the same thing on the sorted list you only have to keep looking until you pass the space where the item is questions would have been. This implies code like
while (p && (p.value < input) && (p.next != NULL)){
p = updatePointer(p.next);
}
if (p.value == input) //...
Assuming randomly distributed (i.e. unordered input) you expect the second to case to require about 1/2 as many comparisons.
Suppose you have 1000 data to insert in both lists and the data is pure random order but
of the values of 1 up to 1000.
Additionally suppose both lists are filled already with 500 data items of pure random order for the unsorted list and of sorted order in case of the sorted list.
For the unsorted list you have to check each item to find possible doubles, which lead
to a pointer stepping forward for each visited node.
For the sorted list you only have to search forward this way until the first element
appears in the list which has a greater value.
The chance of such hits is 50% by 1000 elements being inserted into a list already filled
with 500 items for a total range of 1 to 1000 for the values.
This creates 50% of all operations being inserts for replaces, which let the unsorted list
being checked for additional items compared to the sorted list.
Insertion itself is more cheap with the unsorted list (1 step instead of 4 steps).

merged linked list in C

This question was asked to me in an interview:
There are two header of two linked lists.
There is a merged linked list in c where in the second linked list is merged into the first one at some point.
How could we identify the merging point and what is the complexity of finding that point ?
Could anybody please help?
O(n)
search = list1->header;
if (mixed->header == list1->header) search = list2->header;
while (mixed->next != search) mixed = mixed->next;
Edit: new name for variables and a few comments
/* search is what we want to find. Here it's the head of `list2` */
search = list2->header;
/* unless the merging put `list2` first; then we want to search for `list1` */
if (mixed->header == list2->header) search = list1->header;
/* assume (wrongly) that the header for the mixed list is the merge point */
mergepoint = mixed->head;
/* traverse the mixed list until we find the pointer we're searching */
while (mergepoint->next != search) mergepoint = mergepoint->next;
/* mergepoint now points to the merge point */
Update: This assumes the Y-shaped joining of two linked lists as described better in Steve Jessop's post. But I think the description of the problem is sufficiently ambiguous that various interpretations are possible, of which this is only one.
This can be done with a single pass through one list plus a partial pass through the other. In other words, it's O(n).
Here's my proposed algorithm:
Create a hashmap. (Yes, this is busywork in C if you don't have a library handy for it).
The keys will be pointers to the items in List1 (i.e. the head pointer and each link).
The values will be integers denoting the position, i.e. distance from the head of List1.
Run through List1, keeping track of the position, and hash all your pointers and positions.
Run through List2, keeping track of the position, and find the first pointer that occurs in the hashmap.
At this point, you'll know the position in List2 of the first node common to both lists.
The hashmap entry will also contain the position in List1 of that same node.
That will nicely identify your merge point.
Do you mean you have a Y-shape, like this:
list1: A -> B -> C -> D -> E -> F
list2: X -> Y -> Z -> E -> F
Where A .. Z are singly-linked list nodes. We want to find the "merge point" E, which is defined to be the first node appearing in both lists. Is that correct?
If so, then I would attach the last node of list2 (F) to the first node of list2 (X). This turns list2 into a loop:
list2 : X -> Y -> Z -> E -> F -> X -> ...
But more importantly:
list1 : A -> B -> C -> D -> E -> F -> X -> Y -> Z -> E -> ...
This reduces the question to a previously-solved problem, which can be solved in O(n) time and O(1) additional storage.
But reading your question, another possibility is that by "merge" you mean "insert". So you have two lists like this:
list1: A -> B -> C
list2: D -> E -> F
and then another completely separate list:
list3: A -> B -> D -> E -> F -> C
where this time, A .. F are the values contained in the list, not the nodes themselves.
If the values are all different, you just need to search list3 for D (or for the later of D and A, if you don't know which list it was that was copied into the other). Which seems like a pointless question. If values can be repeated, then you have to check for the full sequence of list2 inside list3. But just because you find "DEF" doesn't mean that's where list2 was inserted - maybe "DEF" already occurred several times in list1 beforehand, and you've just found the first of those. For instance if I insert "DEF" into "ABCDEF", and the result is "ABCDEFDEF", then did I insert at index 3 or at index 6? There's no way to tell, so the question can't be answered.
So, in conclusion, I don't understand the question. But I might have answered it anyway.
If the question means list2 contained in list1 (that is list2 points somewhere in the middle of list1), then it is easy - just walk list1 and compare pointers until you reach list2.
However such interpretation does not make much sense, because by inserting list2 into the list1 (like 1 1 2 2 1), you would also modify list2 - the last 1 becomes part of list2.
So I will assume the question is about the Y shape:
list1: A -> B -> C -> D -> E -> F
list2: X -> Y -> Z -> E -> F
This can be solved using hashtable as Carl suggested.
Solution without a hashtable would be this:
Walk list1 and disconnect all its pointers as you go
Walk list2. When it ends, you've reached the junction point
Repair the pointers in list1
Disconnecting and repairing pointers in list1 can be done easily using recursion:
Diconnect(node)
{
if (node->next == NULL)
walk list2 to its end, that is the solution, remember it
else
{
tmp = node->next;
node->next = NULL;
Disconnect(tmp);
node->next = tmp; // repair
}
}
Now call Disconnect(list1).
That is recurse down list1 and disconnect pointers. When you reach end, execute step 2 (walk list2 to find junction), repair pointers when returning back from recursion.
This solution modifies list1 temporarily, so it is not thread safe and you should use a lock around the Disconnect(list1) call.
//try this code for merge
void mergeNode(){
node *copy,*current,*current1;
free(copy);
merge=NULL;
current=head;
current1=head1;
while(current!=NULL){
if(merge==NULL){
node *tmp;
tmp=(node*)malloc(sizeof(node));
tmp->data=current->data;
tmp->link=NULL;
merge=tmp;
}
else{
copy=merge;
while(copy->link!=NULL)
copy=copy->link;
node *tmp;
tmp=(node*)malloc(sizeof(node));
tmp->data=current->data;
tmp->link=copy->link;
copy->link=tmp;
}
current=current->link;
}
while(current1!=NULL){
copy=merge;
while(copy->link!=NULL)
copy=copy->link;
node *tmp;
tmp=(node*)malloc(sizeof(node));
tmp->data=current1->data;
tmp->link=copy->link;
copy->link=tmp;
current1=current1->link;
}
display(merge);
}
Sorry if my answer seems too simple, but if you have two linked list which are identified by a header and you join them, so that
A -> B -> C -> D is the first list, and
1 -> 2 -> 3 -> 4 is the second, then suppose
A -> B -> C -> 1 -> 2 -> 3 -> 4 -> D is the result
then to find the merging point you need to go through the final list until you find the second header (the 1). Which goes in O(n1) worst case, where n1 is the number of elements of the first list (this happens if the second list is merged at the end).
That's how I would intend the question. The reference to the C language would probably mean that you have no 'object' or pre-packaged data structure, unless specified.
[update] as suggested by Sebastian, if the two list above have the same elements my solution won't work. I suspect that this is where the C language comes into action: you can search for the address of the first element of the second list (the head). Thus the duplicates objection won't hold.
Well, there are several approaches to solve this problem.
Note that i am only discussing the approaches[corner cases may need to be handled separately] starting from brute force to the best one.
Considering N: number of nodes in first linked list
M: number of nodes in second linked list
Approach 1:
Compare each node of first linked list with every other node of second list. Stop when you find a matching node, this is the merging point.
while(head1)
{
cur2=head2;
while(cur2)
{
if(cur2==head1)
return cur2;
cur2=cur2->next;
}
head1=head1->next;
}
Time Complexity: O(N*M)
Space Complexity: O(1)
Approach 2:
Maintain two stacks. Push all the nodes of he first linked list to first stack. Repeat he same for second linked list.
Start popping nodes from both the stacks until both popped nodes do not match. The last matching node is the merging point.
Time Complexity: O(N+M)
Space Complexity: O(N+M)
Approach 3:
Make use of hash table. Insert all the nodes of the first linked list into hash.
Search for the first matching node of he second list in the hash.
This is the merging point.
Time Complexity: O(N+M)
Space Complexity: O(N)
Note that the space complexity may vary depending upon the hash function used[talking about C where you are supposed to implement your own hash function].
Approach 4:
Insert all the nodes of first linked list[by nodes, i mean addresses] into an array.
Sort the array with some stable sorting algorithm in O(N logN) time[Merge sort would be better].
Now search for the first matching node from the second linked list.
Time Complexity: O(N logN)
Space Complexity: O(N)
Note that this approach may be better than Approach 3 [in terms of space]as it doesn't use a hash.
Approach 5:
1. Take an array of size M+N.
2. Insert each node from the first linked list, followed by inserting each node from the second linked list.
3. Search for the first repeating element[can be found in one scan in O(M+N) time].
Time Complexity: O(N+M)
Space Complexity: O(N+M)
Approach 6: [A better approach]
1. Modify the first linked list & make it circular.
2. Now starting from the head of the second linked list, find the start of the loop using Floyd- Warshall cycle detection algorithm.
3. Remove the loop[can be easily removed as we know the last node].
Time Complexity: O(N+M)
Space Complexity: O(1)
Approach 7: [Probably the best one]
1. Count the number of nodes in first linked list[say c1].
2. Count the number of nodes in second linked list[say c2].
3. Find the difference[Lets say c1>c2] diff=c1-c2.
4. Take two pointers p1 & p2, p1 pointing to the head of the first linked list & p2 pointing to the head of the second linked list.
5. Move p1 diff times.
6. Move both p1 & p2 each node at a time until both point to the same node.
7. p1 or p2 indicates the merging point.
Time Complexity: O(N+M)
Space Complexity: O(1)
Trivial solution is obviously O(N+M). Hm.. What could be better. You can go from start to end of the list or vice versa. When you have a threads, you can go these directions at the some time, so should be a litter bit quicker.

Resources