單鏈表是否相交(Java版),單鏈java
題目:有兩個帶頭結點的單鏈表L1,L2,判斷它們是否相交(它們中是否有相同的結點)
解題思路:
L1,L2中任何一個鏈表為空白它們不相交
因為單鏈表中可能有環,所以分3種情況討論
1、L1,L2中都沒有環,那麼可以通過判斷它們中最後一個結點是否相同,來判斷它們是否相交
2、L1,L2中一個有環,一個沒有環,那麼它們一定不相交
3、L1,L2中都有環,那麼只有當它們的環相同時,它們才相交,當它們在進入環之前有相同的結點時,它們交點的個數大於環中結點的個數,當它們進入環時才有第一個相同的結點時,它們交點的個數等于于環中結點的個數,這兩種情況下,都可以通過判斷它們環的入口結點是否相同來判斷它們是否相交。
ADT定義:
//單鏈表的結點類class LNode{//為了簡化訪問單鏈表,結點中的資料項目的存取權限都設為publicpublic int data;public LNode next;}
演算法實現:
public class LinkListUtli {// 判斷單鏈表是否有環public static boolean hasCircle(LNode L) {if (L == null)return false;// 單鏈表為空白時,單鏈表沒有環if (L.next == null)return false;// 單鏈表中只有頭結點,而且頭結點的next為空白,單鏈表沒有環LNode p = L.next;// p表示從頭結點開始每次往後走一步的指標LNode q = L.next.next;// q表示從頭結點開始每次往後走兩步的指標while (q != null) // q不為空白執行while迴圈{if (p == q)return true;// p與q相等,單鏈表有環p = p.next;q = q.next.next;}return false;}// 當單鏈表中沒有環時返回null,有環時返迴環的入口結點public static LNode searchEntranceNode(LNode L) {if (L == null)return null;// 單鏈表為空白時,單鏈表沒有環if (L.next == null)return null;// 單鏈表中只有頭結點,而且頭結點的next為空白,單鏈表沒有環LNode p = L.next;// p表示從頭結點開始每次往後走一步的指標LNode q = L.next.next;// q表示從頭結點開始每次往後走兩步的指標while (q != null) // q不為空白執行while迴圈{if (p == q)break;// p與q相等,單鏈表有環p = p.next;q = q.next.next;}if (q == null)return null;// 這裡之所以沒有向上面一樣,先讓p,q走一步再進入迴圈判斷,是因為頭結點可能就是環的入口結點q = L;while (q != null) {if (p == q)return p;// 返迴環中入口結點p = p.next;q = q.next;}return null;} //判斷兩個單鏈表是否相交public static boolean isIntersect(LNode L1, LNode L2) {if (L1 == null || L2 == null)return false;// L1,L2中任何一個鏈表為空白它們不相交// L1,L2中都沒有環if (!(hasCircle(L1) || hasCircle(L2))) {LNode p = L1;LNode q = L2;while (p.next != null) {p = p.next;}while (q.next != null) {q = q.next;}if (p == q)return true;// 兩個無環單鏈表,最後一個結點相同時,它們相交return false;}// L1,L2中都有環else if (hasCircle(L1) && hasCircle(L2)) {if (searchEntranceNode(L1) == searchEntranceNode(L2))return true;// 兩個有環單鏈表,它們環的入口結點相同時,它們相交return false;}// L1,L2中一個有環,一個沒有環else {return false;// 兩個單鏈表,一個有環,一個無環,它們一定不相交}}}
拓展:有兩個帶頭結點的單鏈表L1,L2,判斷它們是否相交,並求出首次相交的結點
解題思路:
當L1與L2相交時,若L1與L2中都沒有環,那麼求出兩個單鏈表中結點個數之差的絕對值dCount,讓結點個數多的單鏈表從頭結點開始往下走dCount步,讓結點個數少的單鏈表從頭結點開始往後走,然後同時讓兩個單鏈表每次往後走一步,直到他們相等,那麼這個結點就是首次相交的結點。若L1與L2中都有環,求出單鏈表L1與L2從頭結點到環入口結點的結點總個數count,求出兩者之差的絕對值,讓count大的單鏈表從頭結點開始往下走dCount步,讓count大的的單鏈表從頭結點開始往後走,然後同時讓兩個單鏈表每次往後走一步,直到他們相等,那麼這個結點就是首次相交的結點。
演算法實現:
public class LinkListUtli {// 判斷單鏈表是否有環public static boolean hasCircle(LNode L) {if (L == null)return false;// 單鏈表為空白時,單鏈表沒有環if (L.next == null)return false;// 單鏈表中只有頭結點,而且頭結點的next為空白,單鏈表沒有環LNode p = L.next;// p表示從頭結點開始每次往後走一步的指標LNode q = L.next.next;// q表示從頭結點開始每次往後走兩步的指標while (q != null) // q不為空白執行while迴圈{if (p == q)return true;// p與q相等,單鏈表有環p = p.next;q = q.next.next;}return false;}// 當單鏈表中沒有環時返回null,有環時返迴環的入口結點public static LNode searchEntranceNode(LNode L) {if (L == null)return null;// 單鏈表為空白時,單鏈表沒有環if (L.next == null)return null;// 單鏈表中只有頭結點,而且頭結點的next為空白,單鏈表沒有環LNode p = L.next;// p表示從頭結點開始每次往後走一步的指標LNode q = L.next.next;// q表示從頭結點開始每次往後走兩步的指標while (q != null) // q不為空白執行while迴圈{if (p == q)break;// p與q相等,單鏈表有環p = p.next;q = q.next.next;}if (q == null)return null;// 這裡之所以沒有向上面一樣,先讓p,q走一步再進入迴圈判斷,是因為頭結點可能就是環的入口結點q = L;while (q != null) {if (p == q)return p;// 返迴環中入口結點p = p.next;q = q.next;}return null;}// 判斷兩個單鏈表是否相交public static boolean isIntersect(LNode L1, LNode L2) {if (L1 == null || L2 == null)return false;// L1,L2中任何一個鏈表為空白它們不相交// L1,L2中都沒有環if (!(hasCircle(L1) || hasCircle(L2))) {LNode p = L1;LNode q = L2;while (p.next != null) {p = p.next;}while (q.next != null) {q = q.next;}if (p == q)return true;// 兩個無環單鏈表,最後一個結點相同時,它們相交return false;}// L1,L2中都有環else if (hasCircle(L1) && hasCircle(L2)) {if (searchEntranceNode(L1) == searchEntranceNode(L2))return true;// 兩個有環單鏈表,它們環的入口結點相同時,它們相交return false;}// L1,L2中一個有環,一個沒有環else {return false;// 兩個單鏈表,一個有環,一個無環,它們一定不相交}}// 求兩個鏈表首次相交的結點public static LNode searchFirstIntersectNode(LNode L1, LNode L2) {if (isIntersect(L1, L2))return null;// 單鏈表L1與L2不相交時,返回nullif (!hasCircle(L1)) // L1中沒有環時,L1與L2中都沒有環{int count1 = 1;// 單鏈表L1中結點的個數,包括頭結點int count2 = 1;// 單鏈表L2中結點的個數,包括頭結點LNode p = L1;LNode q = L2;while (p.next != null) {p = p.next;count1++;}while (q.next != null) {q = q.next;count2++;}if (count1 >= count2)// 單鏈表L1比單鏈表L2長或兩者一樣長時{int dCount = count1 - count2;// 單鏈表L1比單鏈表L2多出的結點的個數p = L1;q = L2;while (dCount > 0) {p = p.next;}while (p != null) {if (p == q)return p;p = p.next;q = q.next;}} else {int dCount = count2 - count1;// 單鏈表L2比單鏈表L1多出的結點的個數p = L1;q = L2;while (dCount > 0) {q = q.next;}while (q != null) {if (p == q)return p;p = p.next;q = q.next;}}} else // L1有環時,此時L1與L2都有環{int count1 = 1;// 單鏈表L1中從頭結點到環的入口結點之間所有結點的個數,包括頭結點與環的入口結點int count2 = 1;// 單鏈表L2中從頭結點到環的入口結點之間所有結點的個數,包括頭結點與環的入口結點LNode p = L1;LNode q = L2;while (p != searchEntranceNode(L1)) {p = p.next;count1++;}while (q != searchEntranceNode(L2)) {q = q.next;count2++;}if (count1 >= count2)// 單鏈表L1比單鏈表L2從頭結點到環的入口結點之間所有結點的個數長或兩者一樣長時{int dCount = count1 - count2;// 單鏈表L1比單鏈表L2從頭結點到環的入口結點之間所有結點的個數多出的結點的個數p = L1;q = L2;while (dCount > 0) {p = p.next;}while (p != null) {if (p == q)return p;p = p.next;q = q.next;}} else {int dCount = count2 - count1;// 單鏈表L2比單鏈表L1從頭結點到環的入口結點之間所有結點的個數多出的結點的個數p = L1;q = L2;while (dCount > 0) {q = q.next;}while (q != null) {if (p == q)return p;p = p.next;q = q.next;}}}return null;}}