有一個單鏈表,提供了頭指針和一個結點指針,設計一個函數,在 O(1)時間內刪除該結點指針指向的結點。
眾所周知,鏈表無法隨機存儲,只能從頭到尾去遍歷整個鏈表,遇到目標節點之后刪除之,這是最常規的思路和做法。
如圖所示,刪除結點 i,那么只需找到 i 的前驅 h,然后連 h 到 j,再銷毀i 即可。雖然可以安全的刪除 i 結點,但是是順序查找找到 i,之后刪除,時間復雜度是 O(n)級別的。具體做法就是:順序查找整個單鏈表,找到要刪除結點 i 的直接前驅 h,把 h額 next 指向i 的 next ,再刪除 i 結點即可
現在假設的是我們知道這個單鏈表里一定存在我們需要刪除的結點(也就是存在目標結點),因為是單鏈表,結點沒有前驅,那么找到前驅,我們只能從頭結點開始掃描整個鏈表,那么,我們可不可以去找到要刪除結點的下一個結點呢?
時間復雜度為 O(1)的刪除單鏈表結點的方法
如果我們直接找到要刪除結點的下一個結點,是非常方便的,不用去遍歷整個鏈表,只需把刪除結點 i 的下一個結點j 的內容復制到 i 上,然后把 i 的指針指向 j 的next,然后再刪除j 結點。等價於刪除了 i 結點。整個過程無需遍歷整個鏈表,時間復雜度是 O(1)級別
繼續思考這個思路:這個方法,依靠的是結點的后繼指針,如果要刪除的結點在末尾,也就說,結點沒有后繼,怎么處理?
本題目沒有給出尾指針,那么此種特殊情況,還是需要從頭到尾遍歷整個鏈表,得到該結點的前驅,然后刪除該結點。
繼續思考,如果單鏈表只有一個結點,那么刪除該結點需要做什么處理?
如果單鏈表只有一個結點,現在需要刪除這個結點,也就是鏈表的頭結點(尾結點),那么在刪除之后,需要把頭指針指向 NULL處理。
代碼實現如下:
1 typedef struct Node{ 2 int data; 3 Node *next; 4 } Node, *List; 5 6 void deleteNode(List *head, Node *del) 7 { 8 Node *p = NULL; 9 //判斷鏈表是否為空 10 if (!head) { 11 cout << "鏈表為空!" << endl; 12 exit(1); 13 } 14 //如果刪除的結點是尾結點 15 if (del->next == NULL) { 16 p = *head; 17 //仍然需要遍歷鏈表 18 while (p->next != del) { 19 //順次后移 20 p = p->next; 21 } 22 //找到前驅,斷鏈 23 p->next = NULL; 24 //刪除結點 25 delete del; 26 //預防野指針 27 del = NULL; 28 } 29 //如果單鏈表只有一個結點 30 else if(*head == del){ 31 //無需斷鏈,直接刪除 32 delete del; 33 del = NULL; 34 //頭指針處理 35 *head = NULL; 36 } 37 //如果鏈表里有多個結點,且刪除的結點不是尾結點 38 else{ 39 //無須遍歷鏈表 40 p = del->next; 41 del->data = p->data; 42 del->next = p->next; 43 delete p; 44 p = NULL; 45 } 46 }
時間復雜度分析:
如果是刪除非末位的結點,且結點有n(n>1)個,那么時間復雜度是 o(1),對於尾結點,是 o(n),總得來說,是【(n-1)o(1)+o(n)】/ n,結果還是 o(1),復合要求。
注意
1、考慮問題要全面,不要丟三落四,以為出了結果,就算完成任務了。需要嚴謹的求學問。
2、這個題目的思路其實是建立在客戶事先知道本鏈表里存在需要刪除的結點。如果不知道,我們還是需要先遍歷整個鏈表進行查找!那樣時間復雜度還是 o(n)。
3、關於刪除結點,往往都是只能想到找到前驅,刪除,但是逆向思路看,不一定說,刪除結點,就一定要刪除這個結點,我們可以找到結點的后繼,然后通過內容復制,刪除該結點的后繼結點,來達到刪除該結點一樣的效果。
歡迎關注
dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!