1. 程式人生 > >劍指offer——鏈表相關問題總結

劍指offer——鏈表相關問題總結

時間復雜度 k個結點 listnode 轉載 pbe push_back loop 結點 art

首先統一鏈表的數據結構為:

struct ListNode 
{
    int val;
    struct ListNode *next;
    ListNode(int x) :val(x), next(NULL) {}
};




題目一:從尾到頭打印鏈表:輸入一個鏈表。從尾到頭打印鏈表每一個節點的值

分析:

難點在於鏈表僅僅有指向後繼的指針,沒有指向前驅的指針。

轉換思路。結合棧後進先出的特點,能夠遍歷鏈表,依次將數據元素存入棧中,然後再依次出棧,即為從尾到頭的順序。

vector<int> printListFromTailToHead(struct ListNode* head) 
    {
        ListNode *p=head;
        stack<int> temp;
        while(p)
        {
            temp.push(p->val);
            p=p->next;
        }
        vector<int>result;
        while(!temp.empty())
        {
            result.push_back(temp.top());
            temp.pop();
        }
        return result;
    }


目二:鏈表中倒數第k個結點:輸入一個鏈表,輸出該鏈表中倒數第k個結點。

分析:

(1)依據上題的啟示。事實上這個題也能夠借助棧來完畢。先從頭到尾依次將結點存入棧。然後取出從棧頂開始的第k個結點就可以。


(2)還有一種方法是使用先後指針來完畢,一個指針先從頭開始向前走k-1步,然後還有一個指針從頭開始走。當第一個指針指向最後一個 結點時,後一個指針指向倒數第k個結點。


邊界條件:要記得考慮k大於鏈表長度的情況和k=0的情況都返回空。


方法一:

ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) 
    {
        ListNode *p=pListHead;
        stack<ListNode*> temp;
        int len=0;
        while(p)
        {
           ++len;
           temp.push(p);
           p=p->next;
        }
        if(len<k||k==0)
            return NULL;
        while(k!=1)
        {
            temp.pop();
            --k;
        }
        return temp.top();
    }


方法二:

ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
    {
        if(pListHead==NULL||k==0)
            return NULL;
        ListNode *pAhead=pListHead;
        ListNode *pBehind=pListHead;
        for(int i=0;i<k-1;i++)
        {
           if(pAhead->next!=NULL)
               pAhead=pAhead->next;
           else
                return NULL;   
        }
        while(pAhead->next!=NULL)
        {
            pAhead=pAhead->next;
            pBehind=pBehind->next;
        }
        return pBehind;
    }


題目三:反轉鏈表(鏈表逆序):輸入一個鏈表,反轉鏈表後,輸出鏈表的全部元素。

ListNode* ReverseList(ListNode* pHead) 
    {
       if(!pHead)
           return pHead;
       ListNode *reverse=NULL;
       ListNode *pre=NULL;
       ListNode *next=NULL;
       ListNode *curr=pHead;
       while(curr)
       {
          next=curr->next;
          if(!next)
              reverse=curr;
          curr->next=pre;
          pre=curr;
          curr=next;
       }
        return reverse;
    }


題目四:合並兩個排序的鏈表:輸入兩個單調遞增的鏈表。輸出兩個鏈表合成後的鏈表,當然我們須要合成後的鏈表滿足單調不減規則。

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        ListNode *result=new ListNode(0);
        ListNode *r=result;
        while(pHead1&&pHead2)
        {
           if(pHead1->val<=pHead2->val)
           {
               r->next=pHead1;
               r=r->next;
               pHead1=pHead1->next;
               
           }
            else
           {
               r->next=pHead2;
               r=r->next;
               pHead2=pHead2->next;
           }
        }
        if(pHead1)
           r->next=pHead1;
        if(pHead2)
           r->next=pHead2;
        return result->next;
    }


題目五:兩個鏈表的第一個公共結點,輸入兩個鏈表,找出它們的第一個公共結點。

分析:兩個鏈表都是單向鏈表。假設他們有公共的結點,那麽這兩個鏈表從某一結點開始,他們的next都指向同一個結點,之後全部的點都重合。不可能再出現分叉。所以它們的拓撲形看起來像一個Y形,而不可能是X形。

方法一:首先遍歷兩個鏈表得到它們的長度,就能知道哪個鏈表長,以及長的鏈表比短的鏈表多幾個結點。在第二次遍歷的時候,在較長的鏈表上先走相差的步數,接著同一時候在兩個鏈表上遍歷。找到的第一個同樣的結點就是它們的公共結點。

時間復雜度O(m+n)。不須要輔助棧。

方法二:分別將兩個鏈表存入兩個輔助棧中,然後比較兩個棧頂的結點是否同樣。假設同樣,則把棧頂彈出,接著比較下一個棧頂,直到找到最後一個同樣的結點。

時間復雜度O(m+n),空間復雜度O(m+n)。

方法一:

ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2)
    {
        int len1=0,len2=0;
        ListNode *p=pHead1,*q=pHead2;
        while(p)
        {
           len1++;
           p=p->next;
        }
        while(q)
        {
           len2++;
           q=q->next;
        }
               
        if(len1==0||len2==0)
            return NULL;
        p=pHead1;q=pHead2;
        while(len1>len2)
        {
            p=p->next;
            len1--;
        }
        while(len1<len2)
        {
            q=q->next;
            len2--;
        }
        while(p!=q)
        {
          p=p->next;
          q=q->next;
        }
        return p;
    }


方法二:

 ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) 
    {
        ListNode *p=pHead1;
        ListNode *q=pHead2;
        stack<ListNode *> temp1;
        stack<ListNode *> temp2;
        while(p)
        {
            temp1.push(p);
            p=p->next;
        }
        while(q)
        {
           temp2.push(q);
           q=q->next;
        }
        ListNode *result=NULL;
        while(!temp1.empty()&&!temp2.empty()&&temp1.top()==temp2.top())
        {
           result=temp1.top();
           temp1.pop();
           temp2.pop();
        }
        return result;
    }


題目六:鏈表中環的入口結點:一個鏈表中包括環,請找出該鏈表的環的入口結點。

分析:有兩個能夠面試的問題:一個題是推斷一個鏈表中。是否有環。

第二個是環的入口結點。

經典方法就是使用快慢指針。快的一次走兩步,慢的一次走一步,假設指針重合,說明鏈表有環。

在此基礎上,能夠想到,快的比慢的剛好多走了一個環的長度。並且速度是慢的二倍,說明快的總共走的是兩個環的長度。慢的總共走了一個環的長度。

所以保持慢指針如今的位置,讓快指針再次從頭走起。每次走一步,當這次兩個指針重合的時候。它們剛好都在環的入口結點上。


推斷是否有環的代碼:

bool HasLoop(ListNode* pHead)
  {
    ListNode *slow=pHead,*fast=pHead;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
          return true;
    }
    return false;
  }


找環的入口結點代碼

ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
    ListNode *slow=pHead,*fast=pHead;
    while(fast&&fast->next)
     {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
        {
            fast=pHead;
            while(fast!=slow)
            {
                fast=fast->next;
                slow=slow->next;
            }
            return slow;
        }
     }
    return NULL;
  }


轉載請註明出處:http://blog.csdn.net/xingyanxiao/article/details/47068509

劍指offer——鏈表相關問題總結