面试编程题:(三)单链表

来源:互联网 发布:酷宝数据 作假 编辑:程序博客网 时间:2024/05/18 06:45

(一)链表结构

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

(二)链表操作

    (1) 链表中倒数第k个结点。(滴滴)

        要求:只遍历一遍链表

       思路:定义两个指针,第一个指针从链表头指针开始前走k-1步,第二个指针保持不动;从第k步开始,第二个指针也开始从头遍历。由于两个指针的距离保持在k-1,因此当第一个指针到达链表尾结点时,第二个指针指向的正好是倒数第k个节点。

ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {        if(pListHead==NULL||k<=0)            return NULL;        ListNode* first=pListHead;        ListNode* second=pListHead;        for(unsigned int i=0;i<k-1;i++){            if(first->next!=NULL)                first=first->next;            else                return NULL;        }        while(first->next!=NULL){            first=first->next;            second=second->next;        }        return second;             }
 (2)求单链表中间节点

       如果链表中节点总数是奇数,返回中间节点;如果节点总数是偶数,返回两个节点中的任意一个。

        定义两个指针,同时从链表头节点出发,一个指针一次走一步,另一个指针一次偶两步。当走的快的指针走到链表的末尾时,走的慢的指针正好在链表的中间。

  (3)判断一个单链表中是否有环

      定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上走得慢的指针,那么链表就是环形链表;如果走得快的指针到达链表的末尾都没有追上链表的第一个指针,那么链表就不是环形链表。

 (4)如果一个单链表中有环,判断环的入口点(滴滴)

      定义两个指针P1和P2指向链表的头节点,如果链表中环有k个节点,指针P1先在链表中向前移动k步,然后两个指针以相同的速度向前移动。当第二个指针指向环的入口点时,第一个指针已经绕环走了一圈回到了入口点。两个指针的相遇点即为环的入口点。(原因:假设链表总共有n个节点,则P2到环的入口需走n-k步;P1已经走了k步,到达环的入口点也只需再走n-k步)

       单链表中环长度的计算:在(3)的基础上,让慢指针每次往前移动一步,并计数。当指针再次到达当前节点时,计数个数即为环的长度

ListNode* EntryNodeOfLoop(ListNode* pHead)    {        if(pHead==NULL)            return NULL;        ListNode* pSlow=pHead->next;        if(pSlow==NULL)            return NULL;        ListNode* pFast=pSlow->next;        while(pSlow!=NULL&&pFast!=NULL)        {            if(pSlow==pFast)                break;//如果链表中有环,则停止            pSlow=pSlow->next;                        pFast=pFast->next;            if(pFast!=NULL)//防止链表无环时,非法访问NULL节点的next                pFast=pFast->next;        }        if(pFast==NULL)//如果到达NULL节点,说明无环            return NULL;        ListNode* pNode=pSlow->next;        int count=1;//防止下面循环开始即pNode==pSlow,让pNode=pSlow->next,count初始为1        while(pNode!=pSlow)//统计环中的节点数         {            count++;            pNode=pNode->next;        }        pFast=pHead;        pSlow=pHead;        for(int i=0;i<count;i++)            pFast=pFast->next;        while(pFast!=pSlow)        {            pFast=pFast->next;            pSlow=pSlow->next;        }        return pFast;    }

 (5)反转单链表(华为)

    1)非递归实现

ListNode* ReverseList(ListNode* pHead) {        if(pHead==NULL)            return NULL;        ListNode* pNode=pHead;        ListNode* reverseHead=NULL;        ListNode* pPrev=NULL;        while(pNode!=NULL){            ListNode* pNext=pNode->next;//保存链表下一节点,防止断链            if(pNext==NULL)                reverseHead=pNode;            pNode->next=pPrev;            pPrev=pNode;            pNode=pNext;                    }        return reverseHead;    }
     2)递归实现

ListNode* ReverseList(ListNode* pHead) {       if(pHead==NULL||pHead->next==NULL){           return pHead;              }        ListNode* prevHead=ReverseList(pHead->next);        pHead->next->next=pHead;        pHead->next=NULL;               return prevHead;    }

 (6)合并两个有序的单链表,合并后链表有序。(瑞晟)

      1)非递归实现

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)    {        if(pHead1==NULL)            return pHead2;        else if(pHead2==NULL)            return pHead1;        ListNode* mergerList=NULL;        ListNode* mergerListHead=NULL;        //处理头节点        if(pHead1->val<pHead2->val)        {             mergerListHead=pHead1;             pHead1=pHead1->next;             mergerList=mergerList->next;        }        else        {             mergerListHead=pHead2;             mergerList=mergerList->next;             pHead2=pHead2->next;        }        mergerList=mergerListHead;      //处理若两个链表都不为空时的情况        while(pHead1!=NULL&&pHead2!=NULL)        {          if(pHead1->val<=pHead2->val)          {            mergerList->next=pHead1;            mergerList=mergerList->next;            pHead1=pHead1->next;                      }          else          {            mergerList->next=pHead2;            mergerList=mergerList->next;            pHead2=pHead2->next;          }       }        //处理其中一个链表为空时的情况        while(pHead1!=NULL)        {            mergerList->next=pHead1;            mergerList=mergerList->next;            pHead1=pHead1->next;        }        while(pHead2!=NULL)        {            mergerList->next=pHead2;            mergerList=mergerList->next;            pHead2=pHead2->next;        }        return mergerListHead;    }

   2)递归实现

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)    {        if(pHead1==NULL)            return pHead2;        else if(pHead2==NULL)            return pHead1;        ListNode* mergerListHead=NULL;        if(pHead1->val<pHead2->val)        {            mergerListHead=pHead1;            mergerListHead->next=Merge(pHead1->next,pHead2);        }        else        {            mergerListHead=pHead2;            mergerListHead->next=Merge(pHead1,pHead2->next);        }                return mergerListHead;    }

(7)两个单链表的第一个公共结点

      思路:因为是单链表,每个节点只有一个next节点。因此从第一个公共节点开始,之后的节点都是重合的,不可能在出现分叉。

       方法一:首先遍历两个链表得到它们的长度,就知道哪个链表比较长,以及长链表比短链表多的节点数n。第二次遍历的时候,在较长的链表上先走n步,接着再同时在两个链表上遍历,找到第一个相同的点就是它们的第一个公共点。

ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2)     {        if(pHead1==NULL||pHead2==NULL)            return NULL;        ListNode* pNode1=pHead1;        ListNode* pNode2=pHead2;        int count1=0;        int count2=0;        while(pNode1!=NULL)//遍历统计链表1的长度        {          pNode1=pNode1->next;          count1++;        }        while(pNode2!=NULL)//遍历统计链表2的长度        {            pNode2=pNode2->next;            count2++;        }        int distance=(count1>count2)?(count1-count2):(count2-count1);        for(int i=0;i<distance;i++)//链表1,2中比较长的先移动distance距离        {            if(count1>count2)                pHead1=pHead1->next;            else                pHead2=pHead2->next;        }        while(pHead1!=pHead2)//然后同时移动直到找到第1个公共点        {            pHead1=pHead1->next;            pHead2=pHead2->next;        }        return pHead1;    }

     方法二:利用双栈;把链表中的节点分别插入栈中。从链表的尾部开始比较,最后一个相同的节点就是要找的节点。

ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2)     {        if(pHead1==NULL||pHead2==NULL)            return NULL;        ListNode* pNode1=pHead1;        ListNode* pNode2=pHead2;        stack<ListNode*> ListStack1;        stack<ListNode*> ListStack2;        while(pNode1!=NULL)        {          ListStack1.push(pNode1);          pNode1=pNode1->next;        }        while(pNode2!=NULL)        {            ListStack2.push(pNode2);            pNode2=pNode2->next;        }        ListNode* firstCommNode=NULL;        while(!ListStack1.empty()&&!ListStack2.empty())        {            if(ListStack1.top()==ListStack2.top())            {               firstCommNode=ListStack1.top();               ListStack1.pop();               ListStack2.pop();              }            else break;        }        return firstCommNode;         }




    



0 0