/**
* 输入两个链表,找出它们的第一个公共结点
*/
package javabasic.nowcoder;
import java.util.HashMap;
import java.util.Map;
public class Main40 {
/*分析:两个单链表如果存在第一个公共结点,则后续结点一定都公共,
因为结点里包含next指针,如果第一个公共结点相同,则next必然相同,
所以第一个公共结点后链表合并。
思路1:设表1长度n,表2长度m,暴力法嵌套遍历两个链表需要O(mn)的时间复杂度,
可以采用hash的思想将其中一个转存为哈希表结构,这样建哈希表时间O(m),
而遍历链表时间O(n),而遍历时查找哈希表的时间为O(1),因此复杂度降为O(m+n),
但需要辅助空间。(这种哈希优化的策略是种一般性的思路,谨记!)
*/
public ListNode FindFirstCommonNodeI(ListNode pHead1, ListNode pHead2) {
if(pHead1==null||pHead2==null)return null;
ListNode p = pHead1;
ListNode q = pHead2;
Map<ListNode,Integer> record = new HashMap<ListNode,Integer>();
while(p!=null) {
record.put(p, null);
p=p.next;
}
while(q!=null) {
if(record.containsKey(q)) {
return q;
}
q = q.next;
}
return null;
}
/*
思路2:开始遍历两遍链表获取两个表的长度,比较长度让长的一个先走差值个步长,
再两个一起走。(快慢指针思想,也是链表问题的一般性思路)
*/
public ListNode FindFirstCommonNodeII(ListNode pHead1, ListNode pHead2) {
if(pHead1==null||pHead2==null)return null;
ListNode p = pHead1;
ListNode q = pHead2;
int length1 = getLength(p);
int length2 = getLength(q);
//如果链表1的长度大于链表2的长度
if(length1>=length2) {
int len = length1-length2;
//先遍历链表1,遍历的长度就是两链表的长度差
while(len>0) {
p=p.next;
len--;
}
}else if(length1<length2) {
//如果链表2的长度大于链表1的长度
int len = length2-length1;
//先遍历链表2,遍历的长度就是两链表的长度差
while(len>0) {
q=q.next;
len--;
}
}
//开始齐头并进,直到找到第一个公共结点
while(p!=q) {
p=p.next;
q=q.next;
}
return p;
}
//求指定链表的长度
private int getLength(ListNode q) {
int length = 0;
ListNode current = q;
while(current!=null) {
length++;
current=current.next;
}
return length;
}
/**
* 思路:如果有公共节点,1)若两个链表长度相等,那么遍历一遍后,在某个时刻,p1 == p2
* 2)若两个链表长度不相等,那么短的那个链表的指针pn(也就是p1或p2)
* 必先为null,那么这时再另pn = 链表头节点。经过一段时间后,
* 则一定会出现p1 == p2。
* 如果没有公共节点:这种情况可以看成是公共节点为null,顾不用再考虑。
*/
public ListNode FindFirstCommonNodeIII(ListNode pHead1, ListNode pHead2) {
if(pHead1==null||pHead2==null)return null;
ListNode p = pHead1;
ListNode q = pHead2;
while(p!=q) {
if(p!=null) {
p=p.next;
}
if(q!=null) {
q=q.next;
}
if(p!=q) {
if(p==null) {
p=pHead1;
}
if(q==null) {
q=pHead2;
}
}
}
return p;
}
}