題目要求
給定單鏈表的頭指針和一個結點指針,定義一個函數在O(1)時間刪除該結點。
題目分析
首先我看到這個題目,還是會慣性思維,認爲刪除指定結點,不就是要遍歷該鏈表找到該結點,那就是O(n)呀。。。(無力吐槽我的智商)仔細思考一下,該函數已經給了兩個已知的參數了,頭節點以及待刪除結點。知道待刪除結點,也就等於知道了該結點的下一個結點。分析到這裏,問題似乎可以解決了,刪除該結點,即就是釋放該結點的空間,斷開該結點與它前面以及後面結點的鏈,看到這裏還不明白的話,可以畫畫圖。
先考慮最簡單的一種情況,如果我們要在圖示的這條鏈表中刪除值c所在的結點,那麼知道函數中所給的該結點,就可以將該結點的next結點中的值拷貝給該結點,如下圖所示,將c覆蓋,然後再將要刪除的c結點的next指向原來的d結點的下一結點,圖中即爲尾結點tail。然後再釋放圖中的d結點。這樣就間接的刪除了待刪除的C結點,此時的時間複雜度爲O(1)。 (歡呼,我們做到了。。)
上面的方案似乎是很可行的,但是是不是真的完全正確那?思考一下,1)假如該鏈表中就只有一個頭結點,上述方案就不可行了,若恰好待刪除結點是頭結點,那麼正好我們可以碰巧刪除它,並且記得最後一定要將頭結點置空;2)假如鏈表有頭有尾,我們要刪除尾結點吶,此時也就只能遍歷整個鏈表找到該結點並刪除,時間複雜度爲O(n)。
考慮到這裏,以爲要大功告成了,然而並沒有。有沒有考慮過對整個函數的思考過程完全建立在一種理想的情況下,那就是我們天真的以爲要刪除的結點在我們的鏈表裏,所以我們是不是應該首先判斷該結點有沒有在鏈表中。但是問題又來了(心好累),查找結點必然是要遍歷該鏈表,時間複雜度必定爲O(n),看來這條路我們無法對它做出什麼優化了,所以爲了完成該題目,只能假設用戶輸入的待刪除結點確確實實在我們的鏈表中,這個責任就交給該函數的調用者吧。
綜上核心代碼:
void DeleteNode(NODE **head,NODE *ToBeDelete)
{
if(head == NULL || ToBeDelete == NULL)
return;
if(ToBeDelete->next != NULL)
{
NODE *tmp = ToBeDelete->next;
ToBeDelete->data = tmp->data;
ToBeDelete->next = tmp->next;
delete tmp;
tmp = NULL;
}
else if(ToBeDelete == *head)
{
delete ToBeDelete;
ToBeDelete = NULL;
*head = NULL;
}
else
{
NODE *tmp = *head;
while (tmp != ToBeDelete)
{
tmp = tmp->next;
}
tmp->next = NULL;
delete ToBeDelete;
ToBeDelete = NULL;
}
}
做不到完美,也要做到近似完美。總體的時間複雜度爲【O(N)*(N-1)+O(n)*1】/n,結果爲O(n).
總結
連自己都看不太懂並且無法說服自己的代碼,還怎麼給別人看。我覺得優秀的編程就是寫出來的程序能很容易的讀懂,所以努力嘍