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


    首先统一链表的数据结构为:

    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

  • 相关阅读:
    (转)Shell中read的用法详解
    How to install OpenResty
    MYSQL随机抽取查询 MySQL Order By Rand()效率问题
    NGINX、PHP-FPM开机自动启动
    Nginx和PHP-FPM的启动/重启脚本 [转发]
    绕过 <?PHP exit('Access Denied'); ?> 限制
    OpenResty(Nginx)+Lua+GraphicsMagick实现缩略图功能
    ImageMagick资料
    MySQL5.5 RPM安装的默认安装路径
    PHP编译支持mysqli
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/6811187.html
Copyright © 2020-2023  润新知