面試題13:給定單向鏈表的頭指針和一個結點指針,定義一個函數在O(1)時間刪除該結點。node
只要涉及到鏈表的增、刪操做,就須要考慮如下三種場景:面試
場景1:(特殊場景)只有一個頭結點,對頭結點進行操做
場景2:(普通場景)有多個結點,操做中間節點
場景3:(特殊場景)對尾節點進行操做
複製代碼
首先考慮普通場景,再考慮特殊場景。bash
普通場景:
因爲要求的時間複雜度爲O(1),若是採用常規思惟,從頭結點遍歷到目標節點,則時間複雜度爲O(n),沒法知足要求。
刪除某節點,意味着將某個目標值 date 對應的節點從鏈表中抹去,能夠是直接刪除,也能夠是用新的值來覆蓋舊的值,而後用
新的後序節點覆蓋舊的後序節點。
若是採用直接刪除的方式,須要從頭結點遍歷起,時間複雜度爲 O(n), 沒法知足 O(1) 的要求。
所以須要採起覆蓋抹去目標節點的方式來達到刪除節點的目的。
解決方案:
Step 1: 將目標節點的 next 節點的值賦給目標節點
Step 2:將目標節點的 next 的後序節點賦給目標節點的 next
此時,目標節點被其後序節點 next 徹底覆蓋,同時目標節點原來的後序節點 next 沒指向它,即被刪掉。
實現刪除目標節點的目的,時間複雜度爲 O(1)。
特殊場景:
1. 刪除頭節點
直接刪除頭結點,時間複雜度 O(1)。
2. 刪除尾節點
須要從頭結點開始遍歷到尾部,對於 n-1 箇中間節點,時間複雜度爲 O(n)。
總的平均時間複雜度:
[(n-1) * O(1) + O(n)] / n
結果仍是 O(1)
複製代碼
public static void deleteNodeInO1(Node head, Node target){
if (head == null || target == null) {
return;
}
//若是刪除的是頭結點,無論當前鏈表是否只有一個頭結點,都直接賦 head.next,只有一個頭結點時,
//head.next 爲 null
if (head == target) {
head = head.next;
} else if (target.next != null) { //普通場景:覆蓋抹除法
Node nextNode = target.next;
target.data = nextNode.data;
target.next = nextNode.next;
} else { //刪除尾節點,只能從頭遍歷到導數第二個節點,再刪除最後節點
Node node = head;
//注意,此處只能遍歷到尾節點以前的一個節點,即倒數第二個節點,由於刪除尾節點須要其前一個節點做爲操做入口
while (node.next != target) {
node = node.next;
}
node.next = null; //此處 node.next 就是最後一個節點,將最後一個節點置空,就至關於刪除最後節點
}
}
複製代碼
刪除節點也可使用 覆蓋抹除法(自創詞彙方便記憶),時間複雜度爲 O(1)函數