數據結構(一)線性表循環鏈表相關補充
阿新 • • 發佈:2018-08-06
width hide cli 機器 都是 實時 思路 在外 for循環 方法一:使用兩個指針,循環嵌套,A指針在外層循環,一步一步向下走,B指針在內層循環,循環到A的位置,當兩者的位置相同時判斷走的步數是否一致,不一致則代表有環。且能夠得到準確的環路節點。其中A是要將鏈表從頭走到尾,B是一直在內層進行循環,時間復雜度為O(n^2)
(一)合並兩個循環鏈表
p = rearA->next; //A的頭結點,一會還要使用 rearA->next = rearB->next->next; //是A的尾結點指向B的第一個結點 q = rearB->next; //存放B的頭結點,需要釋放 rearB->next = p; //使B的尾結點指向A的頭結點 free(q); //釋放B的頭結點
(二)判斷單鏈表中是否有環
方法一:使用兩個指針,循環嵌套,A指針在外層循環,一步一步向下走,B指針在內層循環,循環到A的位置,當兩者的位置相同時判斷走的步數是否一致,不一致則代表有環。且能夠得到準確的環路節點。其中A是要將鏈表從頭走到尾,B是一直在內層進行循環,時間復雜度為O(n^2)
//兩層循環進行判斷 Status HasCircle01(List L,int *seq) { List la, lb; int stepa, stepb; la = lb = L; //la在外層循環,lb在內層循環 stepa = stepb = 1; while (la) { while (lb!=la) { lb = lb->next; stepb++; } if (stepa != stepb)break; stepa++; la = la->next; lb = L; stepb = 1; } if (la!=NULL) { *seq = stepb; return TRUE; } return FALSE; }
方法二:使用快慢指針若是有環那麽快指針會一直在環中循環,當慢指針進入環中節點後,一定會出現快指針在慢指針後面(或者相等)的情況,就可以判斷是否有環,不過這種方法不容易獲取到環路節點位置,時間復雜度按照慢指針來算,為O(n)
//快慢指針進行判斷Status HasCircle02(List L) { List high, low; high = low = L; while (low&&high&&high->next) { if (high->next) high = high->next->next; low = low++; if (high == low) return OK; } return FALSE; }
方法三:判斷地址的大小
1.棧的地址是由高向低增長的. 2.堆得地址增長方向是由低到高向上增長的
我們創建鏈表時,一般是使用堆區進行,所以一般機器都是地址向上增長,若是有環,則地址會減小,我們可以使用一個指針,或者一個快指針,將每次的結點地址比較,這樣時間復雜度為O(n/2),若是環足夠大,我們設置的指針增長步長夠大,也會優化更多。
不過有限制,就是我們創建的鏈表需要地址增長是單向的,就是只能使用尾插法或者頭插法,不能使用中間插入或者聯合使用
//地址字節進行判斷,為了這種方法實現,上面無論是創建直鏈表還是循環鏈表都是使用的尾插法 Status HasCircle03(List L) { List high=L; int MaxAddr = 0; while (high&&high->next) { if (high->next) { high = high->next->next; if (MaxAddr < high) MaxAddr = high; else break; } } if (high&&high->next) return TRUE; return FALSE; }
//判斷堆增長方向 int StackGrow() { int *a,*b; int flag; a = (int *)malloc(sizeof(int)); b = (int *)malloc(sizeof(int)); if (a > b) flag = 0; else flag = 1; free(a); free(b); return flag; }使用一個小例子來判斷堆的增長方向
其他方法還需要再繼續回顧知識後才有思路.....
全部實現代碼
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int ElemType; typedef int Status; typedef struct Node { ElemType data; struct Node* next; }Node; typedef struct Node* List; //創建一個列表,有無環,若是有環,將單鏈表和循環鏈表合並即可 Status InitList(List* L, int flag, int* sep); //創建一個單鏈表 Status CreateList(List* L, int n); //創建一個循環鏈表 Status CreateCList(List* L, int n); //開始進行判斷是否有環 //兩層循環進行判斷 Status HasCircle01(List L,int *seq); //快慢指針進行判斷 Status HasCircle02(List L); //地址字節進行判斷,為了這種方法實現,上面無論是創建直鏈表還是循環鏈表都是使用的尾插法 Status HasCircle03(List L); //用來打印鏈表 void PrintList(List L,int flag, int seq); int main() { List L = NULL; int seq = 0; //分割點 int flag = 1; printf("create cList?(1/0):"); scanf("%d", &flag); if (!InitList(&L, flag, &seq)) //現在L指向第一個結點 return 0; PrintList(L,flag,seq); if (HasCircle01(L, &seq)) printf("has Circle:%d\n", seq); else printf("no Circle\n"); if (HasCircle02(L)) printf("has Circle\n"); else printf("no Circle\n"); if (HasCircle03(L)) printf("has Circle\n"); else printf("no Circle\n"); system("pause"); return 0; } //創建一個列表,有無環,若是有環,將單鏈表和循環鏈表合並即可,這裏單鏈表和循環鏈表都沒有頭結點 Status InitList(List* L, int flag,int* sep) { int n; List SL,CL; List q; srand(time(0)); printf("please enter the length of list:"); scanf("%d", &n); *sep = n; if (!CreateList(&SL, n)) //鏈表創建失敗,直接退出 return ERROR; if (flag) //創建一個有環鏈表 { printf("please enter the length of Clist:"); scanf("%d", &n); if (!CreateCList(&CL, n)) //CL是循環鏈表頭指針 return ERROR; q = SL; for (n = 1; n < *sep; n++) q = q->next; //直接指向單鏈表的末尾,下面開始合並 q->next = CL; } *L = SL; return OK; } //創建一個單鏈表 Status CreateList(List* L, int n) { int i; List q,p; if (n < 1) return ERROR; *L = (List)malloc(sizeof(Node)); (*L)->data = rand() % 100; q = *L; for (i = 1; i < n; i++) { p = (List)malloc(sizeof(Node)); p->data = rand() % 100; q->next = p; q = p; } q->next = NULL; return OK; } //創建一個循環鏈表 Status CreateCList(List* L, int n) { List q,p; ElemType item; if (n < 1) return ERROR; *L = (List)malloc(sizeof(Node)); if (*L == NULL) return ERROR; (*L)->data = rand() % 100; p = *L; p->next = p; //形成回環 for (int i = 1; i < n;i++) { //生成新的節點,根據尾指針添加節點,並實時更新尾指針。註意這裏數據插入是尾插法 q = (List)malloc(sizeof(Node)); q->data = rand()%100; q->next = p->next; p->next = q; p = q; } return OK; } //兩層循環進行判斷 Status HasCircle01(List L,int *seq) { List la, lb; int stepa, stepb; la = lb = L; //la在外層循環,lb在內層循環 stepa = stepb = 1; while (la) { while (lb!=la) { lb = lb->next; stepb++; } if (stepa != stepb) break; stepa++; la = la->next; lb = L; stepb = 1; } if (la!=NULL) { *seq = stepb; return TRUE; } return FALSE; } //快慢指針進行判斷 Status HasCircle02(List L) { List high, low; high = low = L; while (low&&high&&high->next) { if (high->next) high = high->next->next; low = low++; if (high == low) return OK; } return FALSE; } //地址字節進行判斷,為了這種方法實現,上面無論是創建直鏈表還是循環鏈表都是使用的尾插法 Status HasCircle03(List L) { List high=L; int MaxAddr = 0; while (high&&high->next) { if (high->next) { high = high->next->next; if (MaxAddr < high) MaxAddr = high; else break; } } if (high&&high->next) return TRUE; return FALSE; } //用來打印鏈表 void PrintList(List L, int flag, int seq) { List CHead; List q = L; //獲取頭指針 int i; if (!flag) { while (q) { printf("%d ", q->data); q = q->next; } } else { for (i = 1; i <= seq; i++) { printf("%d ", q->data); q = q->next; } //for循環退出就進入了循環鏈表範圍內 printf("-|- "); CHead = q; while (q->next != CHead) { printf("%d ", q->data); q = q->next; } printf("%d", q->data); } printf("\n"); }
測試結果
數據結構(一)線性表循環鏈表相關補充