[算法] - 兩個單鏈表相交的一系列問題【鏈表中最難的題目】

兩個單鏈表相交的一系列問題
【 題目】 給定兩個可能有環也可能無環的單鏈表, 頭節點head1和head2。 請實現一個函數, 如果兩個鏈表相交, 請返回相交的 第一個節點。 如果不相交, 返回null
【 要求】 如果兩個鏈表長度之和爲N, 時間複雜度請達到O(N), 額外空間複雜度請達到O(1)。

前言:

肯定不可能一個有環,一個無環,然後相交了。

首先,我們判斷是有環還是無環,如果有環,返回入環的第一個節點。無環null。

簡單方法:採用hashset,不斷next往裏面賦值,如果hashset裏有重複的node出現,則這個節點就是,如果next出現了null就無環。

省空間的做法:快慢指針當快指針與慢指針相遇,則有環,此時將快指針=head,然後快指針和慢指針都變成1步長,再相遇的地方就是入環節點。中途兩個指針如果遇到null就是無環的結構。

	public static Node getLoopNode(Node head) {
		if (head == null || head.next == null || head.next.next == null) {
			return null;
		}
		Node n1 = head.next; // n1 -> slow
		Node n2 = head.next.next; // n2 -> fast
		while (n1 != n2) {
			if (n2.next == null || n2.next.next == null) {
				return null;//無環
			}
			n2 = n2.next.next;
			n1 = n1.next;
		}
		n2 = head; // n2 -> walk again from head
		while (n1 != n2) {
			n1 = n1.next;
			n2 = n2.next;
		}
		return n1;
	}

第二點學習:如果兩個無環,找相交

先判斷是否相交,兩個都遍歷到最後一個非null的node,如果相等則相交,如果不等肯定不相交。

相交之後,把長的那一個,先走完(長鏈表-短鏈表)插值的長度,然後和慢的一起走,當第一次相遇,返回。

 public static Node noLoop(Node head1, Node head2) {
		if (head1 == null || head2 == null) {
			return null;
		}
		Node cur1 = head1;
		Node cur2 = head2;
		int n = 0;
//首先得到兩個鏈表的最後一個節點,如果不相等,那肯定 不相交,相等,先走長的鏈表將兩個鏈表的差值走完,然後一起走,找
		while (cur1.next != null) {//當cur1變成最後一個就出來了
			n++;//cur的總數
			cur1 = cur1.next;
		}
		while (cur2.next != null) {
			n--;//得到差值
			cur2 = cur2.next;
		}
		if (cur1 != cur2) {//如果兩個無環鏈表的最後一個節點不相等,那麼不相交
			return null;
		}
		cur1 = n > 0 ? head1 : head2;//誰長誰的head變成cur1
		cur2 = cur1 == head1 ? head2 : head1;//誰短誰的head變成cur2
		n = Math.abs(n);
		while (n != 0) {//先走兩個鏈表的差值
			n--;
			cur1 = cur1.next;
		}
		while (cur1 != cur2) {//然後一起走,第一次相交的時候就是那個點
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	}

第三點:如果兩個有環,怎麼求第一個點,以下是三大情況。

第一種:不相交,

第二種:環外相交,

第三種:如你所見,如果遇到了,那麼兩個點都是最近的點,任意返回一個都可以,

如果兩個環的入環節點相等,就是第二個,不是就是1或3。區分1和3,選一個鏈表的入環節點開始next遍歷,如果在再次遇到這個節點之前沒有遇到另一個入環節點就是1,如果相遇了就是3,此時任選一個返回就好了。

做法:直接以入環節點爲終止條件,其本身就是一個無環相交就第一個節點的問題。

	public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
		Node cur1 = null;
		Node cur2 = null;
		if (loop1 == loop2) {
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1) {
				n++;
				cur1 = cur1.next;
			}
//情況2,和單鏈表的相交差不多,原先是遇到 null結尾處終止 ,現在是loop處就終止
			while (cur2 != loop2) {
				n--;
				cur2 = cur2.next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = Math.abs(n);
			while (n != 0) {
				n--;
				cur1 = cur1.next;
			}
			while (cur1 != cur2) {
				cur1 = cur1.next;
				cur2 = cur2.next;
			}
			return cur1;
		} else {//看能不能遇到loop2
			cur1 = loop1.next;
			while (cur1 != loop1) {//如果都轉回自己,還沒有遇到loop2,就是情況一
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章