日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

链表的经典问题

發布時間:2024/7/19 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 链表的经典问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

鏈表的經典問題

?

?

如何判斷兩個單鏈表是否相交,如果相交,找出交點(兩個鏈表都不存在環)

如果兩個單鏈表相交,那應該呈“Y”字形,也就是從交點以后的部分是兩個鏈表的公共節點。

所以,判斷是否相交只要看兩個鏈表的最后一個節點是否為同一個即可。

那么如何找到交點呢?設兩個單鏈表的長度分別為L1、L2,(假設L1>L2),則(L1-L2)的值就是交匯之前兩個鏈表的長度差;

因此,只有讓更長的鏈表先走L1-L2步,然后兩個鏈表開始一起走,如果某次走到一個相同的節點,該節點即為交點。

?

C代碼實現:

typedef struct _ListNode {int data;struct _ListNode *next; } ListNode;static int GetListLength(ListNode *T) {int n;for(n=0; T; n++) {T = T->next; } return n; }static ListNode* FindFirstCommonNode(ListNode *T1, ListNode *T2) {int i;int n1 = GetListLength(T1);int n2 = GetListLength(T2);// T1 always own longer listif (n1 < n2) {return FindFirstCommonNode(T2, T1);} for (i=0; i<n1-n2; i++) {T1 = T1->next;} while (T1 && T1 != T2) {T1 = T1->next;T2 = T2->next; } return T1; }

?

該問題還有一種思路,就是將其中一個鏈表首尾相連,然后檢測另外一個鏈表是否有環,如果存在環,則兩個鏈表相交。

?

判斷一個鏈表是否有環,并找到環的入口點

如果一個單鏈表有環,那應該呈“6”字形。

設置兩個指針(fast, slow),初始值都指向頭節點,slow每次前進一步,fast每次前進二步,如果鏈表存在環,則fast必定先進入環,而slow后進入環,兩個指針必定 相遇:如果鏈表是呈"O"字形,則slow剛好遍歷完一次的時候,與fast相遇;如果呈“6”字形,則更早相遇。

當fast若與slow相遇時,slow還沒有遍歷完鏈表,而fast已經在環內循環了n圈(1<=n)。假設slow走了s步,則 fast走了2s步(fast步數還等于s 加上在環上多轉的n圈),設環長為r,則:

2s = s + nr,簡化為?s= nr

s = x + y,x為鏈表起點到環入口點的距離,y是slow在環內走過的距離;

可以得到 x = y - s = y - nr,從鏈表頭、相遇點分別設一個指針(p1, p2),每次各走一步,當p1走過距離x時到達入口點,而p2走過的距離為y-nr,y是相遇點與入口點的距離,因此y也走到了入口點,也就是說p1、p2在環入口點相遇了。

?

C代碼實現:

static ListNode* FindLoopPort(ListNode *Head) { ListNode *slow = Head; ListNode *fast = Head; // 找到相遇點while ( fast && fast->next ) { slow = slow->next; fast = fast->next->next; if ( slow == fast ) break; } if (fast == NULL || fast->next == NULL) return NULL; // 找到環入口點slow = Head; while (slow != fast) { slow = slow->next; fast = fast->next; } return slow; }

?

?

求一個單鏈表(無環)的中間節點

設置兩個指針(fast, slow),初始值都指向頭節點,slow每次前進一步,fast每次前進二步,當fast走到末尾時,slow剛好指向中間節點。

?

假如鏈表長度為N,如何返回鏈表的倒數第K個結點

思路:用兩個指針,指針P1先走K-1步,然后指針P2才開始走,當指針P1遍歷完鏈表時,P2還剩K個結點沒有遍歷。

?

實現如下:

ListNode *FindLastKNode(ListNode *Head, int K) {if (NULL == Head)return NULL;ListNode *T1 = Head;ListNode *T2 = Head;while (T1 && --K) T1 = T1->next; if (K) // here, K must be 0 return NULL; while (T1->next) { T1 = T1->next; T2 = T2->next; } return T2; }

?

?

在O(1)時間刪除鏈表結點

在鏈表中刪除一個結點,最常規的做法是遍歷鏈表,找到要刪除的結點后再刪除,這種做法的時間復雜度是O(n);
換一種思路,根據待刪除結點A,可以知道其下一個結點是B=A->next,將結點B值拷貝給A,然后刪除B即可。
這種方法需要考慮一種特殊情況,A如果是尾結點,則B不存在,此時仍需要遍歷鏈表一次。

?

C代碼實現:

void DeleteNode(ListNode* Head, ListNode *pDel) {if (NULL == pDel || NULL == Head)return;ListNode *p = Head;if (NULL == pDel->next) { // pDel is the last nodewhile (pDel != p->next)p = p->next;p->next = NULL;free(pDel);
}
else {p = pDel->next;pDel->next = p->next;pDel->data = p->data;free(p);} }

?

?

如何逆序輸出一個單鏈表

方法一:從頭到尾遍歷鏈表,每經過一個結點的時候,把該結點放到一個棧中;當遍歷完整個鏈表后,再從棧頂開始輸出結點的值。
該方法需要維護一個額外的棧,實現起來比較麻煩。我們注意到遞歸本質上就是一個棧結構,所以,也可以用遞歸來實現反向輸出鏈表。


方法二:也就是說,每訪問到一個結點的時候,先遞歸輸出它后面的結點,再輸出該結點自身。

?

C代碼實現:

void ReversePrint(ListNode *Head) {if (Head) {if (Head->next) {ReversePrint(Head->next);}printf("%d ", Head->data);} }

?

?

?

如何反轉一個單鏈表

?利用輔助指針就地修改節點的next域,代碼如下:

static ListNode *ReverseList(ListNode *Head) {ListNode *pNode = Head;ListNode *pNext = NULL;ListNode *pPrev = NULL;while (pNode) {pNext = pNode->next; if (NULL == pNext) // meet the endHead = pNode;pNode->next = pPrev;pPrev = pNode;pNode = pNext;} return Head; }

?

遞歸 的實現方法:

static void ReverseList2(ListNode** Head) {ListNode *p = *Head;if (!p) return;ListNode* rest = p->next;if (!rest) return;ReverseList2(&rest);rest->next = p;p->next = NULL; }

?

?

鏈表的排序

歸并排序實現的時間復雜度為 nlgn,

struct ListNode* Merge(struct ListNode* p1, struct ListNode *p2) {if (!p1) return p2;if (!p2) return p1;if (p1->val > p2->val) {p2->next = Merge(p1, p2->next);return p2;} else {p1->next = Merge(p1->next, p2);return p1;}}struct ListNode* sortList(struct ListNode* head) {if (!head || !head->next) return head;struct ListNode *h1, *h2;struct ListNode *p1 = head, *p2 = head, *p = head;while (p2 && p2->next) {p = p1;p1 = p1->next;p2 = p2->next->next;}p->next = NULL;h1 = sortList(p1);h2 = sortList(head);return Merge(h1, h2); }

以上遞歸的實現會占用lgN的空間(遞歸壓棧),非遞歸的實現如下:

?

?

復雜鏈表的復制

假設有一個復雜鏈表,它除了有一個next指針外,還有一個other指針,指向鏈表中的任一結點或者NULL,

typedef struct _ListNode {int data;struct _ListNode *next;struct _ListNode *other; } ListNode;

?

如下圖,是一個含義5個節點的該類型的復雜鏈表,實線表示next指針,虛線表示other指針,NULL指針未標出。

?

最簡單的方法是,先復制所有節點,并用next指針鏈接起來,然后假設原始鏈表的某節點N的other指針指向節點S,由于S的位置可能在N的前面,也可能在N的后面,所以要定位N的位置需要從原始鏈表的頭節點開始找,直到確認節點S在鏈表中的位置為s;然后在復制鏈表上節點N的other指針也要指向距離鏈表頭的第s個節點。這種方法的時間復雜度是O(n2)。

上面這種方法的主要缺點在于無法快速定位N節點的other所指向的S節點的位置,

?

下面將介紹一種時間復雜度是O(n)的方法,首先把復制的節點串到原節點后面,如下圖:

?

然后設置復制節點的other指針(例如 A'->other = A->other->next),如下圖

最后,把偶數順序的節點和奇數節點的指針分開。

?

// 逐個節點復制,并串到原節點后面 static void CloneNodes(ListNode *Head) {ListNode *p = Head;while (p) {ListNode *pCloned = malloc(sizeof(ListNode));pCloned->data = p->data;pCloned->next = p->next;pCloned->other = NULL;p->next = pCloned;p = pCloned->next;} }// 設置新節點的other指針 static void ConnectNodes(ListNode *Head) {ListNode *pCloned;ListNode *p = Head;while(p){pCloned = p->next;if (p->other) {pCloned->other = p->other->next;}p = pCloned->next;} }

?

轉載于:https://www.cnblogs.com/chenny7/p/4113552.html

總結

以上是生活随笔為你收集整理的链表的经典问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。