題目一:判斷單鏈表中是否有環
描述:
1.有環的定義:鏈表的尾結點指向了鏈表中的某個結點
兩種解決方案
【方法一】
- 使用
p
、q
兩個指針,p
總是向前走,但q
每次都從頭開始走,對于每個結節點,看p
走的步數是否和q
一樣。如果不一樣,就存在環。如圖,當p
從6走到3時,總共需要6步,此時q
從頭head出發,只需要兩步就到3,步數不等,存在環。
【方法二】利用快慢指針
- 使用
p
、q
兩個指針,p
每次向前走一步,q
每次向前走兩步,若在某個時候,p == q
,則存在環。
代碼
第一步:創建有環單鏈表
實際上在前面單鏈表創建中有兩種整表創建的方法(頭插法和尾插法):數據結構學習-線性表之單鏈表
這里只是最后一個有所改變,讓終端結點指向第二個結點,就構成了一個環。
/**
* 隨機產生n個元素的值,建立帶頭結點的單鏈表L(尾插法)
*/
void CreatListTail(LinkList * L, int n){
LinkList p,r;
int i;
srand(time(0)); /* 初始化隨機數種子 */
*L = (LinkList)malloc(sizeof(Node)); /* L為整個線性表 */
r = *L; /* r為指向尾部的結點 */
for (i = 0; i < n; i++) {
p = (LinkList)malloc(sizeof(Node)); /* 生成新節點 */
p->data = rand()%100 +1; /* 隨機生成100以內的數字 */
r->next = p; /* 將表尾終端結點的指針指針指向新節點 */
r = p; /* 將當前的新節點定義為表尾終端結點 */
}
r->next = (*L)->next->next; /* 尾部指向第二個結點(如果無環r->next = null) */
}
第二步:用比較步數的方法【方法一】判斷是否有環
用兩個while
循環實現步數的判斷
/**
* 比較步數的方法
*/
int HasLoop(LinkList L){
LinkList cur1 = L; /* 定義結點cur1 */
int pos1 = 0; /* cur1的步數 */
while (cur1) { /* 結點cur1存在 */
LinkList cur2 = L; /* 定義結點cur2 */
int pos2 = 0; /* cur2的步數 */
while (cur2) { /* 結點cur2存在 */
if (cur2 == cur1) { /* 當cur2和cur1到達相同的結點時 */
if (pos1 == pos2) /* cur2和cur1走過的步數一樣,說明沒有環 */
break;
else{ /* 有環并返回1*/
printf("環的位置在第%d個結點處.\n\n",pos2);
return 1;
}
}
cur2 = cur2->next; /* 若果沒有環,繼續下一個結點 */
pos2++; /* cur2的步數自增 */
}
cur1 = cur1->next; /* cur1繼續向后一個結點 */
pos1++; /* cur1的步數自增 */
}
return 0;
}
第三部:用快慢指針【方法二】
快慢指針在很多地方都有用到,前面一篇單鏈表練習題中也用過用快慢指針尋找中間節點
/**
* 用快慢指針的方法
*/
int HasLoop2(LinkList L){
LinkList p = L;
LinkList q = L;
while (p != NULL && q != NULL && q->next != NULL) {
p = p->next; /* p每次走一步*/
if (q->next != NULL) {
q = q->next->next; /* p每次走兩步*/
}
printf("p:%d, q:%d \n", p->data,q->data);
if (p == q) {
return 1; /* 當p和q相等,則表示有環 */
}
}
return 0;
}