1. 程式人生 > >刪除單鏈表節點O(1)

刪除單鏈表節點O(1)

一,題目

給定連結串列的頭指標和一個結點指標,在O(1)時間刪除該結點。連結串列結點的定義如下:

struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};

函式的宣告如下:
void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);
二,分析

這是一道廣為流傳的Google面試題,能有效考察我們的程式設計基本功,還能考察我們的反應速度,更重要的是,還能考察我們對時間複雜度的理解。
在連結串列中刪除一個結點,最常規的做法是從連結串列的頭結點開始,順序查詢要刪除的結點,找到之後再刪除。由於需要順序查詢,時間複雜度自然就是O(n)了。我們之所以需要從頭結點開始查詢要刪除的結點,是因為我們需要得到要刪除的結點的前面一個結點。
“狸貓換太子法”:可以從給定要刪除的結點得到它的下一個結點。這個時候我們實際刪除的是它的下一個結點,在刪除之前,我們需要需要把給定的結點的下一個結點的資料拷貝到給定的結點中,然後刪除該節點的下一個節點。此時,時間複雜度為O(1)。
上面的思路還有一個問題:如果刪除的結點位於連結串列的尾部,沒有下一個結點,怎麼辦?我們仍然從連結串列的頭結點開始,順便遍歷得到給定結點的前序結點,並完成刪除操作。這個時候時間複雜度是O(n)。
那題目要求我們需要在O(1)時間完成刪除操作,我們的演算法是不是不符合要求?實際上,假設連結串列總共有n個結點,我們的演算法在n-1總情況下時間複雜度是O(1),只有當給定的結點處於連結串列末尾的時候,時間複雜度為O(n)。那麼平均時間複雜度[(n-1)*O(1)+O(n)]/n,仍然為O(1)。

#include <iostream>
using namespace std; 
struct Node
{
      int    data;
      Node*  next;

};
Node *creat()
{
    Node *head=new Node();
    head->data=0;
    head->next=NULL;
    for(int i=10;i>0;--i)
    {
        Node *temp=new Node();
        temp->data=i;
        temp->
next=NULL; temp->next=head->next; head->next=temp; } return head; } Node *findLastNode(Node *head,int data) { while(head!=NULL&&head->data!=data) { head=head->next; } return head; } void deleteNode(Node *head,Node *del) { if
(del->next!=NULL) { Node *p = del->next ; del->data=del->next->data; del->next=del->next->next; delete p; } else { /*這是我們刪除最後節點的辦法*/ Node *pre=findLastNode(head,9); delete pre->next; pre->next=NULL; } } void print(Node *head) { while(head!=NULL) { cout<<head->data<<" "; head=head->next; } } int main() { Node *head=creat(); print(head); /*O(1)策略刪除一個給定元素*/ Node *del=findLastNode(head,8); deleteNode(head,del); /*O(n)策略刪除結尾元素*/ Node *del2=findLastNode(head,10); deleteNode(head,del2); cout<<endl; print(head); }