1.判斷單鏈表是否有環
使用兩個slow, fast指針從頭開始掃描鏈表。指針slow 每次走1步,指針fast每次走2步。如果存在環,則指針slow、fast會相遇;如果不存在環,指針fast遇到NULL退出。
就是所謂的追擊相遇問題:
2.求有環單鏈表的環長
在環上相遇后,記錄第一次相遇點為Pos,之后指針slow繼續每次走1步,fast每次走2步。在下次相遇的時候fast比slow正好又多走了一圈,也就是多走的距離等於環長。
設從第一次相遇到第二次相遇,設slow走了len步,則fast走了2*len步,相遇時多走了一圈:
環長=2*len-len。
3.求有環單鏈表的環連接點位置
第一次碰撞點Pos到連接點Join的距離=頭指針到連接點Join的距離,因此,分別從第一次碰撞點Pos、頭指針head開始走,相遇的那個點就是連接點。
在環上相遇后,記錄第一次相遇點為Pos,連接點為Join,假設頭結點到連接點的長度為LenA,連接點到第一次相遇點的長度為x,環長為R。
第一次相遇時,slow走的長度 S = LenA + x;
第一次相遇時,fast走的長度 2S = LenA + n*R + x;
所以可以知道,LenA + x = n*R; LenA = n*R -x;
4.求有環單鏈表的鏈表長
上述2中求出了環的長度;3中求出了連接點的位置,就可以求出頭結點到連接點的長度。兩者相加就是鏈表的長度。
編程實現:
下面是代碼中的例子:
具體代碼如下:

1 #include <stdio.h> 2 #include <stdlib.h> 3 typedef struct node{ 4 int value; 5 struct node *next; 6 }LinkNode,*Linklist; 7 8 /// 創建鏈表(鏈表長度,環節點起始位置) 9 Linklist createList(){ 10 Linklist head = NULL; 11 LinkNode *preNode = head; 12 LinkNode *FifthNode = NULL; 13 for(int i=0;i<6;i++){ 14 LinkNode *tt = (LinkNode*)malloc(sizeof(LinkNode)); 15 tt->value = i; 16 tt->next = NULL; 17 if(preNode == NULL){ 18 head = tt; 19 preNode = head; 20 } 21 else{ 22 preNode->next =tt; 23 preNode = tt; 24 } 25 26 if(i == 3) 27 FifthNode = tt; 28 } 29 preNode->next = FifthNode; 30 return head; 31 } 32 33 ///判斷鏈表是否有環 34 LinkNode* judgeRing(Linklist list){ 35 LinkNode *fast = list; 36 LinkNode *slow = list; 37 38 if(list == NULL) 39 return NULL; 40 41 while(true){ 42 if(slow->next != NULL && fast->next != NULL && fast->next->next != NULL){ 43 slow = slow->next; 44 fast = fast->next->next; 45 } 46 else 47 return NULL; 48 49 if(fast == slow) 50 return fast; 51 } 52 } 53 54 ///獲取鏈表環長 55 int getRingLength(LinkNode *ringMeetNode){ 56 int RingLength=0; 57 LinkNode *fast = ringMeetNode; 58 LinkNode *slow = ringMeetNode; 59 for(;;){ 60 fast = fast->next->next; 61 slow = slow->next; 62 RingLength++; 63 if(fast == slow) 64 break; 65 } 66 return RingLength; 67 } 68 69 ///獲取鏈表頭到環連接點的長度 70 int getLenA(Linklist list,LinkNode *ringMeetNode){ 71 int lenA=0; 72 LinkNode *fast = list; 73 LinkNode *slow = ringMeetNode; 74 for(;;){ 75 fast = fast->next; 76 slow = slow->next; 77 lenA++; 78 if(fast == slow) 79 break; 80 } 81 return lenA; 82 } 83 84 ///環起始點 85 ///如果有環, 釋放空空間時需要注意. 86 LinkNode* RingStart(Linklist list, int lenA){ 87 if (!list || lenA <= 0){ 88 return NULL; 89 } 90 91 int i = 0; 92 LinkNode* tmp = list; 93 for ( ; i < lenA; ++i){ 94 if (tmp != NULL){ 95 tmp = tmp->next; 96 } 97 } 98 99 return (i == lenA)? tmp : NULL; 100 } 101 102 ///釋放空間 103 int freeMalloc(Linklist list, LinkNode* ringstart){ 104 bool is_ringstart_free = false; //環起始點只能被釋放空間一次 105 LinkNode *nextnode = NULL; 106 107 while(list != NULL){ 108 nextnode = list->next; 109 if (list == ringstart){ //如果是環起始點 110 if (is_ringstart_free) 111 break; //如果第二次遇到環起始點addr, 表示已經釋放完成 112 else 113 is_ringstart_free = true; //記錄已經釋放一次 114 } 115 free(list); 116 list = nextnode; 117 } 118 119 return 0; 120 } 121 122 int main(){ 123 Linklist list = NULL; 124 LinkNode *ringMeetNode = NULL; 125 LinkNode *ringStartNode = NULL; 126 127 int LenA = 0; 128 int RingLength = 0; 129 130 list = createList(); 131 ringMeetNode = judgeRing(list); //快慢指針相遇點 132 133 if(ringMeetNode == NULL) 134 printf("No Ring\n"); 135 else{ 136 printf("Have Ring\n"); 137 RingLength = getRingLength(ringMeetNode); //環長 138 LenA = getLenA(list,ringMeetNode); 139 140 printf("RingLength:%d\n", RingLength); 141 printf("LenA:%d\n", LenA); 142 printf("listLength=%d\n", RingLength+LenA); 143 } 144 145 ringStartNode = RingStart(list, LenA); //獲取環起始點 146 freeMalloc(list, ringStartNode); //釋放環節點, 有環時需要注意. 采納5樓建議 147 return 0; 148 }
執行結果: