您的位置:首页 > Web前端

剑指offer——链表相关问题总结

2015-07-26 19:07 513 查看
首先统一链表的数据结构为:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: