單鏈表面試題系列之帶環連結串列的入口點
阿新 • • 發佈:2019-02-09
***單鏈表操作之帶環連結串列的入口點***
// 本篇博文闡述如何找到帶環連結串列的入口點,那麼,首先有必要闡述一下什麼是帶環連結串列?如何判斷連結串列是否帶環? 帶環連結串列: 即連結串列中有迴圈的部分,通俗的說就是沒有尾節點!例如: 判斷連結串列是否帶環: 那麼知道了什麼是帶環連結串列,接下來就是判斷連結串列是否帶環的判斷問題了,其實也很簡單,首先最簡單的是判斷出不帶環的連結串列,只要可以找到尾結點即連結串列不帶環,那麼,帶環的連結串列怎麼判斷? 這裡就用到前面部落格講到的快慢指標了,定義兩個指標:slow,fast; fast每次走兩步,slow每次走一步,在連結串列帶環的情況下,slow和fast必然會相遇,而#include<stdio.h> #include<stdlib.h> #include<assert.h> typedef int DataType; typedef struct LinkNode { DataType data; struct LinkNode* next; }LinkNode,*pLinkNode;//結點結構體 typedef struct LinkList { LinkNode* pHead;//頭結點指標 }LinkList ,*pLinkList;//連結串列 void PushBack(pLinkList pList,DataType x) { pLinkNode cur = NULL; pLinkNode pvr = NULL; pLinkNode newNode = (pLinkNode)malloc(sizeof(LinkNode )); if(newNode == NULL) { printf("out of memory\n"); exit(0); } assert(pList); cur = pList ->pHead ; newNode ->data = x; newNode ->next = NULL; if(cur == NULL) { pList ->pHead = newNode ; return; } while(cur) { pvr = cur; cur = cur->next ; } pvr->next = newNode ; }//尾插 void InitLinkList(pLinkList pList) { assert(pList); pList->pHead = NULL; }//初始化列表
@ 判斷連結串列是否帶環演算法:
@ 既然已經會判斷連結串列是否帶環了,接下來就得找找這環的入口點,所謂入口點,就是環的開始的那個結點;現在如 何去找這個結點呢? 我再這裡將推理過程列了出來,因為畫工太差,就只有文字描述了; @ 其實,仔細想想的話,無非就是從表頭到入口點距離為 a,當知道相遇點後,從表頭開始一個指標start,而要通過環內指標找入口點,都是一次走一步,則環內指標要與start在入口點相遇,那麼最起碼環內指標走的距離也是a,這樣的話,方向就明確了,只要找到a的關係捋一下, S = a + x; S = nr; 則 a + x = nr;而指標此時開始走肯定是走的是 t 這段距離,那麼看看可不可以找到 a 和 t 的關係,既然環的一圈是r, 那麼 x+t = r,沒問題吧;則前面的 a + x =nr,是不是可以變為 a = (n - 1)r + t; 即從環內指標從 t 開始走,走過 n - 1 圈後,和start從表頭開始走,直到相遇時,走過的距離都是 a(注意:a表示的是從表頭到入口點的距離);即start和環內指標相遇時必然是入口點! 程式碼實現://構造有環連結串列 void MakeRing(pLinkList plist1) { pLinkNode cur = NULL; pLinkNode pvr = NULL; assert(plist1); cur = plist1 ->pHead ; while(cur) { pvr = cur; cur = cur->next ; } if(pvr != NULL) pvr->next = plist1 ->pHead ;//找到尾結點,讓它可以指向前面的結點! } //判斷連結串列是否帶環 int Judge_Ring(pLinkList pList) { pLinkNode slow = NULL; pLinkNode fast = NULL; assert(pList); slow = fast = pList ->pHead ;//剛開始都指向第一個結點; while(fast && fast->next) { slow = slow->next ;//一次走一步 fast = fast->next ->next ;//一次走兩步 if(slow == fast)//如果相遇則直接返回1,代表有環; return 1; } return 0;//否則返回0,代表無環; } void test3() { LinkList List1 ;//自己建立一個連結串列,方便測試; int ret = 0; InitLinkList(&List1);//初始化連結串列 PushBack(&List1, 2);//尾插 PushBack(&List1, 2); PushBack(&List1, 2); MakeRing(&List1); ret = Judge_Ring (&List1);//接收函式返回值並進行判斷; if(ret == 1) printf("yes\n"); else printf("no\n"); } int main() { test3(); system("pause"); return 0; }
//有環則找出入口點
pLinkNode FindEntry(pLinkList pList)
{
pLinkNode start = NULL;//從表頭開始的指標;
pLinkNode slow = NULL;//快慢指標判斷是否有環
pLinkNode fast = NULL;
pLinkNode meet = NULL;//用來表示相遇點的指標;
assert(pList);
//找相遇點指標的和判斷是否有環的部分一樣;
slow = fast = pList ->pHead ;
while(fast && fast->next)
{
slow = slow->next ;
fast = fast->next ->next ;
if(slow == fast)
break;
}
meet = slow;//用meet來存放相遇點;
if(fast == NULL || fast->next == NULL)
return NULL;
start = pList ->pHead ;
while(start != meet)
{
start = start->next ;
meet = meet->next ;
}
return start;//最後返回入口點;
}
void test4()
{
LinkList List1 ;
pLinkNode tmp = NULL;
int ret = 0;
InitLinkList(&List1);
PushBack(&List1, 1);
PushBack(&List1, 2);
PushBack(&List1, 3);
PushBack(&List1, 4);
PushBack(&List1, 5);
PushBack(&List1, 6);
MakeRing(&List1);
tmp = FindEntry (&List1);
if(tmp != NULL)
printf("%d\n", tmp->data );
else
printf("error!\n");
}
畫圖功底比較差,找入口點這裡也比較難理解,所以文字描述的比較詳細;
找入口點的方法還可以用雜湊表儲存指標的方法,後續會補充!
講的比較粗糙,還望諒解!