題目
給定單鏈表頭指針和一個結點指針,定義一個函數在O(1)時間內刪除該結點。

分析
對於上圖實例鏈表(a)刪除指針p有兩種方式
- 思路1:(b)找到前一個指針pre,賦值pre->next = p->next,刪掉p
- 思路2:(c)目的是刪除p,但是不刪p,直接用p->next的值賦值給p,把p->next刪除掉(好處:不用遍歷找到p的前一個指針pre,O(1)時間內搞定)
於是,定位到思路2,但是思路2有兩個特例:
- 刪除的是尾指針,需要遍歷找到前一個指針
- 整個鏈表就一個結點(屬於刪尾指針,但沒法找到前面的指針,需要開小灶單獨處理)
大體算法思路
待刪指針不是尾指針:
待刪指針的值用待刪指針的下一個指針的值覆蓋
刪掉待刪指針的下一個指針
待刪指針是尾指針:
待刪指針同時是頭指針:
刪掉頭指針
待刪指針不是頭指針
找到待刪指針的前一個指針
刪掉待刪指針,前一個指針的next賦值為空
參考代碼
bool deleteNode(ListNode *&head, ListNode *p) { if(!p || !head) return false; if(p->m_pNext != NULL) //不是尾指針 { ListNode *del = p->m_pNext; p->m_nValue = del->m_nValue; p->m_pNext = del->m_pNext; delete del; del = NULL; } else if(head == p) //是尾指針,同時只有一個結點 { delete p; head = NULL; } else //是尾指針,同時有多個結點 { ListNode *tmp = NULL, *pre = head; while(pre->m_pNext != p) { pre = pre->m_pNext; } delete p; p = NULL; pre->m_pNext = NULL; } return true; }
完整代碼1
#include <iostream> using namespace std; struct ListNode { int m_nValue; ListNode* m_pNext; }; bool deleteNode(ListNode *&head, ListNode *p) { if(!p || !head) return false; if(p->m_pNext != NULL) { ListNode *del = p->m_pNext; p->m_nValue = del->m_nValue; p->m_pNext = del->m_pNext; delete del; del = NULL; } else if(head == p) { delete p; head = NULL; } else { ListNode *tmp = NULL, *pre = head; while(pre->m_pNext != p) { pre = pre->m_pNext; } delete p; p = NULL; pre->m_pNext = NULL; } return true; } void createList(ListNode *&head) { head = new(ListNode); head->m_nValue = 1; head->m_pNext = NULL; ListNode *p2 = new(ListNode); p2->m_nValue = 2; p2->m_pNext = NULL; head->m_pNext = p2; ListNode *p3 = new(ListNode); p3->m_nValue = 3; p3->m_pNext = NULL; p2->m_pNext = p3; ListNode *p4 = new(ListNode); p4->m_nValue = 4; p4->m_pNext = NULL; p3->m_pNext = p4; } void deleteList(ListNode *p) { ListNode *next = NULL; while(p != NULL) { cout << p->m_nValue << endl; next = p->m_pNext; delete p; p = NULL; p = next; } } int main() { ListNode *head = NULL; createList(head); ListNode *p = head->m_pNext->m_pNext->m_pNext; deleteNode(head, p); deleteList(head); }
完整代碼2
#include <iostream> using namespace std; struct ListNode { int m_nValue; ListNode* m_pNext; }; bool deleteNode(ListNode **head, ListNode *p) { if(!p || !head) return false; if(p->m_pNext != NULL) { ListNode *del = p->m_pNext; p->m_nValue = del->m_nValue; p->m_pNext = del->m_pNext; delete del; del = NULL; } else if(*head == p) { delete p; *head = NULL; } else { ListNode *tmp = NULL, *pre = *head; while(pre->m_pNext != p) { pre = pre->m_pNext; } delete p; p = NULL; pre->m_pNext = NULL; } return true; } void createList(ListNode *&head) { head = new(ListNode); head->m_nValue = 1; head->m_pNext = NULL; ListNode *p2 = new(ListNode); p2->m_nValue = 2; p2->m_pNext = NULL; head->m_pNext = p2; ListNode *p3 = new(ListNode); p3->m_nValue = 3; p3->m_pNext = NULL; p2->m_pNext = p3; ListNode *p4 = new(ListNode); p4->m_nValue = 4; p4->m_pNext = NULL; p3->m_pNext = p4; } void deleteList(ListNode *p) { ListNode *next = NULL; while(p != NULL) { cout << p->m_nValue << endl; next = p->m_pNext; delete p; p = NULL; p = next; } } int main() { ListNode *head = NULL; createList(head); ListNode *p = head->m_pNext->m_pNext; deleteNode(&head, p); deleteList(head); }
分析
刪除非尾結點時間復雜讀為O(1),刪除尾結點的時間復雜讀為O(n), 平均時間復雜度為[(n-1)*O(1) + O(N)] / N = O(1)
還有刪除函數並不能處理待刪結點就是該鏈表中的指針,因此需要人為調用時控制,如果得驗證的話,那就沒必要做這些處理了,直接O(N)
技術細節——傳值操作
#include <iostream> using namespace std; struct ListNode { int m_nValue; ListNode* m_pNext; }; void createList(ListNode *head) { head = new(ListNode); head->m_nValue = 1; head->m_pNext = NULL; } void deleteList(ListNode *p) { ListNode *next = NULL; while(p != NULL) { cout << p->m_nValue << endl; next = p->m_pNext; delete p; p = NULL; p = next; } } int main() { ListNode *head = NULL; createList(head); cout << head << endl; deleteList(head); }
主函數中的指針head為傳值調用,傳到函數並沒有改變主函數中的值,圖示

改進的措施就是引用傳值,直接操縱原指針。
