2.7. Intersection
This commit is contained in:
parent
1a1bbb1e27
commit
33be3373e0
|
|
@ -0,0 +1,302 @@
|
|||
# 2.7. Intersection
|
||||
|
||||
> Given 2 singly linked lists, determine if the 2 lists intersect. Return the intersecting node. The intersection is defined based on the reference, not value. If the kth node of the first linked list is the exact same node (by reference) as the jth node of the second linked list, then True
|
||||
|
||||
## Initial idea
|
||||
|
||||
Two nodes in a singly linked list are similar by reference (?) if all values about the object are the same, right? That is, if `.next` and `.data` are the same.
|
||||
|
||||
```java
|
||||
public static LinkedListNode intersect(LinkedListNode a, LinkedListNode b){
|
||||
while(a != null && b != null){
|
||||
|
||||
if(a.data == b.data && a.next.data == b.next.data){
|
||||
return a;
|
||||
}
|
||||
a = a.next;
|
||||
b = b.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
how to evaluate if `a.next == b.next`? because `a.next == c`, and c on itself has `c.next`, and so on. Changing by `a.next.data == b.next.data`
|
||||
|
||||
## Hint 1
|
||||
|
||||
> You can do this in O(A + B) time and O(1) additional space, no need to use a hash table
|
||||
|
||||
The previous code doesn't compare all nodes... I should do a nested loop, then it'd be O(AB).
|
||||
|
||||
```java
|
||||
public static LinkedListNode intersect(LinkedListNode a, LinkedListNode b){
|
||||
LinkedListNode b_reset = b;
|
||||
while(a != null){
|
||||
b = b_reset;
|
||||
while(b != null){
|
||||
if(a.data == b.data && a.next.data == b.next.data){
|
||||
return a;
|
||||
}
|
||||
b = b.next;
|
||||
}
|
||||
a = a.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
Still far away from O(A + B). We cannot iterate A and B at the same time then? How can we compare them?
|
||||
|
||||
## Hint 2
|
||||
|
||||
> Draw a picture of intersecting linked lists and two equivalent linked lists (by value) that don't intersect.
|
||||
|
||||
Problem is, I don't understand what by value and by reference are.
|
||||
|
||||
## Hint 3
|
||||
|
||||
> Focus first on just identifying if there's an intersection
|
||||
|
||||
An intersection is two nodes having the same `.data`?
|
||||
|
||||
## Hint 4
|
||||
|
||||
> Two intersecting linked lists always have the same last node. Once they intersect, all the nodes after that will be equal
|
||||
|
||||
Ok, this follows my previous idea that `.next` must also be equal, which means `.next.next` also, until the end. We can iterate to the last node of the list, check if they match, and then find the diverting node. How to find the diverting node is another topic
|
||||
|
||||
```java
|
||||
public static LinkedListNode intersect(LinkedListNode a, LinkedListNode b){
|
||||
|
||||
while(a.next != null){
|
||||
a = a.next;
|
||||
}
|
||||
|
||||
while(b.next != null){
|
||||
b = b.next;
|
||||
}
|
||||
|
||||
if(a.data == b.data){
|
||||
// do things
|
||||
LinkedListNode ane = null;
|
||||
return ane;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
## Hint 5
|
||||
|
||||
> You can determine if two linked lists intersect by traversing to the end of each and comparing their tails
|
||||
|
||||
Gotcha
|
||||
|
||||
## Hint 6
|
||||
|
||||
> Now need to find the intersection, how to do this if len(A) == len(B)?
|
||||
|
||||
Ideally I would iterate backwards, but that's not possible... Maybe with two pointers. What about:
|
||||
|
||||
1. Iterate through both
|
||||
2. If `a.data == b.data`, save `a`.
|
||||
3. Keep checking the condition. If it's not met, reset.
|
||||
4. If `a.data == b.data`, save `a`.
|
||||
5. Reach the end, with saved `a` node.
|
||||
|
||||
Of course, they need to have the same length. This would be O(3A) time and O(1) space.
|
||||
|
||||
```java
|
||||
public static LinkedListNode intersect(LinkedListNode a, LinkedListNode b){
|
||||
|
||||
LinkedListNode a_head = a;
|
||||
LinkedListNode b_head = b;
|
||||
|
||||
// iterate through A
|
||||
while(a_head.next != null){
|
||||
a_head = a_head.next;
|
||||
}
|
||||
|
||||
// iterate through B
|
||||
while(b_head.next != null){
|
||||
b_head = b_head.next;
|
||||
}
|
||||
|
||||
// last node inequal, return null
|
||||
if(a_head.data != b_head.data) return null;
|
||||
|
||||
// there's intersection
|
||||
LinkedListNode inters = null;
|
||||
while(a.next != null && b.next != null){
|
||||
// nodes with same data found
|
||||
if(a.data == b.data){
|
||||
// no intersecting node, set to a
|
||||
if(inters == null){
|
||||
inters = a;
|
||||
}
|
||||
// if nodes are sme and reset = true, keep iterating
|
||||
}
|
||||
// nodes aren't the same, reset inters
|
||||
else{
|
||||
inters = null;
|
||||
}
|
||||
a = a.next;
|
||||
b = b.next;
|
||||
}
|
||||
|
||||
if(inters == null) return a;
|
||||
return inters;
|
||||
}
|
||||
```
|
||||
|
||||
This should work, in the worst case the intersecting node is the tail itself. If they have a different length:
|
||||
|
||||
1. Get both lengths
|
||||
2. Get minimum length, and advance the pointer of the longest list minlen positions. Then start algorithm
|
||||
|
||||
```java
|
||||
public static LinkedListNode intersect(LinkedListNode a, LinkedListNode b){
|
||||
|
||||
LinkedListNode a_head = a;
|
||||
LinkedListNode b_head = b;
|
||||
int a_len, b_len = 1;
|
||||
|
||||
// iterate through A
|
||||
while (a_head.next != null){
|
||||
a_head = a_head.next;
|
||||
a_len += 1;
|
||||
}
|
||||
|
||||
// iterate through B
|
||||
while (b_head.next != null){
|
||||
b_head = b_head.next;
|
||||
b_len += 1;
|
||||
}
|
||||
|
||||
// last node inequal, return null
|
||||
if (a_head.data != b_head.data) return null;
|
||||
|
||||
// compare lengths, adjust node
|
||||
if (a_len == 1 or b_len == 1) return a_head;
|
||||
|
||||
if (a_len < b_len){
|
||||
while (a_len > 0){
|
||||
b = b.next;
|
||||
}
|
||||
}
|
||||
else if (b_len < a_len){
|
||||
while (b_len > 0){
|
||||
a = a.next;
|
||||
}
|
||||
}
|
||||
|
||||
// there's intersection
|
||||
LinkedListNode inters = null;
|
||||
while(a.next != null && b.next != null){
|
||||
// nodes with same data found
|
||||
if(a.data == b.data){
|
||||
// no intersecting node, set to a
|
||||
if(inters == null){
|
||||
inters = a;
|
||||
}
|
||||
// if nodes are sme and reset = true, keep iterating
|
||||
}
|
||||
// nodes aren't the same, reset inters
|
||||
else{
|
||||
inters = null;
|
||||
}
|
||||
a = a.next;
|
||||
b = b.next;
|
||||
}
|
||||
|
||||
if(inters == null) return a;
|
||||
return inters;
|
||||
}
|
||||
```
|
||||
|
||||
And this should work for lists of all possible lengths.
|
||||
|
||||
## Hint 7
|
||||
|
||||
> If the two linked lists had the same length, you could trasverse forward in each until you found an element in common. How do you adjust this for lists of different lengths?
|
||||
|
||||
Get the length difference, move the pointer of the longer one minlen times.
|
||||
|
||||
## Hint 8
|
||||
|
||||
> Try using the difference between the lengths of the two linked lists
|
||||
|
||||
Yep.
|
||||
|
||||
## Hint 9
|
||||
|
||||
> If you move a pointer in the longer linked list forward by the difference in lengths, you can then apply a similar approach to the scenario when the linked lists are equal.
|
||||
|
||||
Already got that.
|
||||
|
||||
## Solution
|
||||
|
||||
The algorithm is the same as mine
|
||||
|
||||
1. Get lengths and tails of each list
|
||||
2. Compare the tails, if they are different (**by reference, not by value**) return null
|
||||
3. Set two pointers to the start
|
||||
4. On the longer linked list, advance its pointer by the difference in lengths
|
||||
5. Trasverse on each linked list until the pointers are the same
|
||||
|
||||
Takeaways:
|
||||
|
||||
* `size++` is possible in java, no `size += 1` necessary
|
||||
* When comparing nodes, compare `a == b`, not `a.data == b.data`
|
||||
* Get a new class for the tail and lengths
|
||||
|
||||
Improved algorithm:
|
||||
|
||||
```java
|
||||
|
||||
public static LinkedListNode intersect(LinkedListNode a, LinkedListNode b){
|
||||
|
||||
if (a == null || b == null) return null;
|
||||
|
||||
LinkedListNode a_head = a;
|
||||
LinkedListNode b_head = b;
|
||||
int a_len, b_len = 1;
|
||||
|
||||
// iterate through A
|
||||
while (a_head.next != null){
|
||||
a_head = a_head.next;
|
||||
a_len += 1;
|
||||
}
|
||||
|
||||
// iterate through B
|
||||
while (b_head.next != null){
|
||||
b_head = b_head.next;
|
||||
b_len += 1;
|
||||
}
|
||||
|
||||
// last node inequal, return null
|
||||
if (a_head != b_head) return null; // changed from before
|
||||
|
||||
// compare lengths, adjust node
|
||||
if (a_len == 1 or b_len == 1) return a_head;
|
||||
|
||||
// improved
|
||||
LinkedListNode shorter = a_len < b_len ? a : b;
|
||||
LinkedListNode longer = b_len < a_len ? b : a;
|
||||
int diff = Math.abs(a_len - b_len);
|
||||
|
||||
while(k > 0 && longer != null){
|
||||
longer = longer.next;
|
||||
k--;
|
||||
}
|
||||
|
||||
// there's intersection
|
||||
while(shorter != longer){
|
||||
shorter = shorter.next;
|
||||
longer = longer.next;
|
||||
}
|
||||
|
||||
return shorter;
|
||||
}
|
||||
```
|
||||
|
||||
Because we're comparing by reference not value, the condition in `while` won't be met if two nodes have the same `.data` but different `.next`.
|
||||
Loading…
Reference in New Issue