面试类链表题目汇总

来源:互联网 发布:淘宝网老年80岁女冬装 编辑:程序博客网 时间:2024/05/29 21:29

注意:红色区域为错误点 !黄色区域为注意点,重点!

1 链表末未添加尾节点;

//因为插入一个尾节点,如果为空的话就需要修改头结点,多以**;
 void AddToTail(ListNode **pHead, int value)
 {
     ListNode *pAdd = new ListNode();
     pAdd->m_nValue = value;
     pAdd->m_pNext = NULL;
     if (pHead == NULL || *pHead == NULL)
     {
         *pHead = pAdd;
     }
     else
     {
         ListNode *pCurr = *pHead;
         while (pCurr->m_pNext != NULL)
         {
             pCurr = pCurr->m_pNext;
         }
         pCurr->m_pNext = pAdd;
     }
 }


2 在链表中找到第一个含有某值的节点,删除该节点

//影响头结点,加**,删除节点有可能删除头结点;
 //(*pHead)->要加括号!
 void RemovNode(ListNode **pHead, int value)
 {
     if (*pHead == NULL || pHead == NULL)
     {
         return;
     }
     if ((*pHead)->m_nValue == value)
     {
         ListNode *pNext = (*pHead)->m_pNext;
         delete *pHead;
         (*pHead) = pNext;        
     }
     else
     {
         ListNode *pNode = *pHead;
         while (pNode->m_pNext!=NULL)
         {
             if (pNode->m_pNext->m_nValue == value)
             {
                 ListNode *pNextTmp = pNode->m_pNext->m_pNext;
                 delete (pNode->m_pNext);
                 pNode->m_pNext = pNextTmp;
                 break;
             }
             pNode = pNode->m_pNext;
         }
     }    
 }



3  在O(1)时间删除特定地址节点;

 //影响头结点,加**,删除节点有可能删除头结点;
 void DeleteNode(ListNode **pListHead, ListNode *pToBeDelete)
 {
     if (pListHead == NULL || *pListHead == NULL||pToBeDelete==NULL)
         return;
     if (pToBeDelete->m_pNext != NULL)
     {
         ListNode *pDel = pToBeDelete->m_pNext;
         pToBeDelete->m_nValue = pToBeDelete->m_pNext->m_nValue;
         pToBeDelete->m_pNext = pToBeDelete->m_pNext->m_pNext;
         delete pDel;
     }
     else if (pToBeDelete ==(*pListHead))
     {
         delete pToBeDelete;
         pToBeDelete = NULL;
         (*pListHead) = NULL;
     }
     else
     {
         ListNode *pNode = (*pListHead);
         while (pNode->m_pNext != pToBeDelete)
         {
             pNode = pNode->m_pNext;
         }
         pNode->m_pNext = NULL;
         delete pToBeDelete;
         pToBeDelete = NULL;
     }
 }


4 从尾到头打印链表:

注意细节,while里面不要忘记递进关系,而且方法2栈不要忘记pop;

方法一:

void PrintReverse(ListNode *pHead)
 {
     if (pHead == NULL)
         return;
     ListNode *pNode = pHead;
     if (pNode->m_pNext != NULL)
     {
         PrintReverse(pNode->m_pNext);
     }
     cout << pNode->m_nValue << " ";
 }

方法二:
 void PrintReverse1(ListNode *pHead)
 {
     if (pHead == NULL)
         return;
     ListNode *pNode = pHead;
     stack<ListNode*> s;
     while (pNode != NULL)
     {
         s.push(pNode);
         pNode = pNode->m_pNext;
     }
     while (!s.empty())
     {
         ListNode *tmp = s.top();
         cout << tmp->m_nValue << " ";
         s.pop();
     }
 }


5.1  链表的面试问题(判断一个单链表中是否有环

 bool IsLoop(ListNode *pHead)
 {
     ListNode *pFast = pHead;
     ListNode *pSlow = pHead;
     while (pFast != NULL&&pFast->m_pNext != NULL)
     {
        
         pFast = pFast->m_pNext->m_pNext;
         pSlow = pSlow->m_pNext;
         if (pFast == pSlow)
         {
             break;
         }
     }
     if (pFast == NULL || pFast == NULL)
         return false;
     else

         return true;
 }


5.2

判断链表中环的长度


方法1

int loopLength(LinkList *head)
{
    LinkList *slow = head, *fast = head;
    LinkList *p;
    int length = 0;

    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
            break;
    }

    if (fast == NULL || fast->next == NULL})
        return 0;

    while (true)
    {
        slow = slow->next;
        fast = fast->next->next;
        length++;
        if (slow == fast)
            break;
    }
    return length;
}

方法2

int LoopLen(ListNode *pHead)
 {
     if (!IsLoop(pHead))
         return 0;

     ListNode *pFast = pHead;
     ListNode *pSlow = pHead;
     int begin = 0;
     int again = 0;
     int res = 0;
     while (pFast != NULL&&pFast->m_pNext != NULL)
     {
         pFast = pFast->m_pNext->m_pNext;
         pSlow = pSlow->m_pNext;
         if (pFast == pSlow && again == 1)
         {
             break;
         }
         if (pFast == pSlow && again==0)
         {
             begin = 1;
             again = 1;
         }
         if ( begin == 1)
         {
             res++;
         }

     }
     return res;
 }


5.3 判断环的入口节点

关于找到找到环的入口点的问题,当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:

 
12s = s + nr
2s= nr

设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。

 
1a + x = nr
2a + x = (n – 1)r +r = (n-1)r + L - a
3a = (n-1)r + (L – a – x)

(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。程序描述如下:

ListNode *LoopEnter(ListNode *pHead)
 {
     ListNode *pFast = pHead;
     ListNode *pSlow = pHead;
     while (pFast != NULL&&pFast->m_pNext != NULL)
     {
         pFast = pFast->m_pNext->m_pNext;
         pSlow = pSlow->m_pNext;
         if (pFast == pSlow)
         {
             break;
         }
     }
     if (pFast == NULL || pFast->m_pNext == NULL)
         return NULL;

     pSlow = pHead;
     while (pSlow != pFast)
     {
         pFast = pFast->m_pNext;
         pSlow = pSlow->m_pNext;
     }
     return pSlow;
 }


5.5 判断两个无环单链表是否相交的几种思想

1.问题描述

给出两个单向链表的头指针,比如h1、h2,判断两个链表是否相交。编程之美为了简化问题,假设两个链表均不带环。

如下图:

 

 

2.分析与解法

解法一:直观法,先判断第一个链表的每个节点是否在第二个链表中,这种方法时间复杂度为O(Length(h1)*Length(h2))

 

解法二:hash表计数法,首先将第一个链表的所有节点地址进行hash排序,建立hash表,然后针对第二个链表的每个节点的地址,查询hash表,如果它在hash表中存在,那么说明两个链表有交点。这个方法时间复杂度O(Length(h1)+Length(h2)),空间复杂度O(Length(h1))。

 

解法三:转换为是否有环问题,可以将第一个链表的的结尾接到第二个链表表头,然后遍历第二个链表,测试其是否有关,若有环,则表示两个链表相交。方法时间复杂度O(Length(h1)+Length(h2))

 

解法四:先遍历第一个链表,遍历到最后一个节点,然后遍历第二个链表,到最后一个接节点,然后对比两个最后一个节点,相同则相交,不相同,则不相交。方法时间复杂度O(Length(h1)+Length(h2))。



5.6 判断两个有环单链表是否相交的思路

题目描述:判断两个单链表是否相交,如果相交,给出相交的第一个点(链表中可能有环的情况下)
注:有环链表的长度可以根据5.2链表环的长度 5.3链表环的入口节点  两者之和自然是有环链表长度。

分析:
①:判断链表1是否有环,并记录其长度L1,尾节点b1(无环),环入口点a1(有环)
②:判断链表2是否有环,并记录其长度L2,尾节点b2(无环),环入口点a2(有环)
③:若一个有环,另一个没环,则判断不相交,结束
④:若两个都没环,则两个尾节点一点是一样的,所以比较两个尾节点是否相同。若不相同则判断不相交,结束;若相同,则到⑥。
⑤:若两个都有环,有如下图三种情况:a1与a2不相等,且不在一个环中,则不相交,结束;a1等于a2,跳到⑥;a1与a2不相等,但在一个环中,对于链表1来说a1是它的相交首节点,对于链表2来说a2是它的相交首节点。




















⑥:对于两个链表重头开始遍历,长链表节点先出发前进(Max(L1,L2)-Min(L1,L2))步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点



6 判断两个链表的第一个公共结点

 ListNode * FirstCom(ListNode * pHead1, ListNode * pHead2)
 {
     int len1 = GetListLength(pHead1);
     int len2 = GetListLength(pHead2);
     ListNode *pLong = pHead1;
     ListNode *pShort = pHead2;
     int nlen = len1 - len2;
     if (len1 < len2)
     {
         ListNode *pLong = pHead2;
         ListNode *pShort = pHead1;
         int nlen = len2 - len1;
     }
     while (nlen>0)
     {
         pLong = pLong->m_pNext;
         nlen--;
     }
     while ((pLong!=NULL)&&(pShort!=NULL)&&(pLong != pShort))//不要忘记异常情况考量
     {
         pLong = pLong->m_pNext;
         pShort = pShort->m_pNext;
     }
     return pLong;

 }


7. 判断两个单链表是否相交

如果两个链表相交于某一节点,那么在这个相交节点之后的所有节点都是两个链表所共有的。也就是说,如果两个链表相交,那么最后一个节点肯定是共有的。先遍历第一个链表,记住最后一个节点,然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则不相交。时间复杂度为O(len1+len2),因为只需要一个额外指针保存最后一个节点地址,空间复杂度为O(1)。参考代码如下:

 bool IsIntersected(ListNode *pHead1, ListNode *pHead2)
 {
     if (pHead1 == NULL || pHead2 == NULL)
         return false;
     ListNode *pTail1 = pHead1;
     ListNode *pTail2 = pHead2;
     while (pTail1->m_pNext != NULL)
     {
         pTail1 = pTail1->m_pNext;
     }
     while (pTail2->m_pNext != NULL)
     {
         pTail2 = pTail2->m_pNext;
     }
     return (pTail2 == pTail1);
 }


8 查找单链表的中间结点

也是设置两个指针,只不过这里是,两个指针同时向前走,前面的指针每次走两步,后面的指针每次走一步,前面的指针走到最后一个结点时,后面的指针所指结点就是中间结点,即第(n/2+1)个结点。注意链表为空,链表结点个数为1和2的情况。时间复杂度O(n)。参考代码如下:

正确代码

 ListNode * GetMidNode(ListNode *pHead)
 {
     if (pHead == NULL || pHead->m_pNext == NULL)
         return pHead;
     ListNode *pFast = pHead;
     ListNode *pSlow = pHead;
     while (pFast->m_pNext != NULL)
     {
         pFast = pFast->m_pNext;
         pSlow = pSlow->m_pNext;
         if (pFast->m_pNext != NULL)
         {
             pFast = pFast->m_pNext;
         }
     }
     return pSlow;
 }

错误代码:

 ListNode * GetMidNode(ListNode *pHead)
 {
     if (pHead == NULL || pHead->m_pNext == NULL)
         return pHead;
     ListNode *pFast = pHead;
     ListNode *pSlow = pHead;
     while (pFast->m_pNext != NULL)//四个节点比如,就会出现判断空的节点的下一个节点情况,就会出错!循环链表可以这样是因为是循环的,不会出现空节点!
     {
         pFast = pFast->m_pNext->m_pNext;
         pSlow = pSlow->m_pNext;
     }
     return pSlow;
 }


9  翻转链表

方法1:

 ListNode *RevList(ListNode *pHead)
 {
     ListNode *pPrev = NULL;
     ListNode *pCurr = pHead;
     ListNode *pRevHead = NULL;
     while (pCurr != NULL)
     {
         ListNode*pNext = pCurr->m_pNext;
         if (pNext == NULL)
         {
             pRevHead = pCurr;
         }
         pCurr->m_pNext = pPrev;
         pPrev = pCurr;
         pCurr = pNext;
     }
     return pRevHead;
 }


方法2:
void RevList1(ListNode **pHead)
 {
     ListNode *pPrev = NULL;
     ListNode *pCurr = *pHead;
    // ListNode *pRevHead = NULL;
     while (pCurr != NULL)
     {
         ListNode * pNext = pCurr->m_pNext;
         if (pNext == NULL)
         {
             *pHead = pCurr;
         }
         pCurr->m_pNext = pPrev;
         pPrev = pCurr;
         pCurr = pNext;
     }
 }

10  链表的倒数第k个节点

//要考虑边界条件,比如不足k个节点!

方法1:

ListNode *LastK(ListNode *pHead, int k)
 {
     if (pHead == NULL || k <= 0)
         return NULL;
     ListNode *pNode = pHead;
     for (int i = 0; i < k-1 ; i++)
     {
         if (pNode->m_pNext == NULL)
         {
             return NULL;
         }
         pNode = pNode->m_pNext;
     }
     ListNode *pBack = pHead;
     while (pNode->m_pNext != NULL)
     {
         pNode = pNode->m_pNext;
         pBack = pBack->m_pNext;
     }
     return pBack;
 }

方法2:
 ListNode *LastK1(ListNode *pHead, int k)
 {
     if ( k <= 0)
         return NULL;
     ListNode *pNode = pHead;
     for (int i = 0; i < k - 1; i++)
     {
         if (pNode == NULL)
         {
             return NULL;
         }
         pNode = pNode->m_pNext;
     }
     if (pNode == NULL)
     {
         return NULL;
     }

     ListNode *pBack = pHead;
     while (pNode->m_pNext != NULL)
     {
         pNode = pNode->m_pNext;
         pBack = pBack->m_pNext;
     }
     return pBack;
 }


11 合并两个有序链表

方法一:

 ListNode * Merge(ListNode *pHead1, ListNode *pHead2)
 {
     ListNode *pHeadFirst = NULL;
     if (pHead1 == NULL)
         return pHead2;
     if (pHead2 == NULL)
         return pHead1;
     if (pHead1->m_nValue < pHead2->m_nValue)
     {
         pHeadFirst = pHead1;
         pHead1 = pHead1->m_pNext;
     }
     else
     {
         pHeadFirst = pHead2;
         pHead2 = pHead2->m_pNext;
     }
     ListNode *pMergHead = pHeadFirst;
     while (pHead1 != NULL&&pHead2 != NULL)
     {
         if (pHead1->m_nValue < pHead2->m_nValue)
         {
             pMergHead->m_pNext = pHead1;
             pHead1 = pHead1->m_pNext;
             pMergHead = pMergHead->m_pNext;
         }
         else
         {
             pMergHead->m_pNext = pHead2;
             pHead2 = pHead2->m_pNext;
             pMergHead = pMergHead->m_pNext;
         }
     }
     if (pHead1 == NULL)
     {
         pMergHead->m_pNext = pHead2;
     }
     if (pHead2 == NULL)
     {
         pMergHead->m_pNext = pHead1;
     }
     return pHeadFirst;
 }

方法2

 ListNode * Merge1(ListNode *pHead1, ListNode *pHead2)
 {
    
     if (pHead1 == NULL)
         return pHead2;
     if (pHead2 == NULL)
         return pHead1;
     ListNode *pHeadFirst = NULL;
     if (pHead1->m_nValue < pHead2->m_nValue)
     {
         pHeadFirst = pHead1;
         pHeadFirst->m_pNext = Merge(pHead1->m_pNext, pHead2);
        
     }
     else
     {
         pHeadFirst = pHead2;
         pHeadFirst->m_pNext = Merge(pHead2->m_pNext, pHead1);        
     }
     return pHeadFirst;
 }


错误方法!//错误是因为递归时不需要判断pHead1的next是否为NULL,否则就无法合并了!
 ListNode * Merge2(ListNode *pHead1, ListNode *pHead2)
 {
    
     if (pHead1 == NULL)
         return pHead2;
     if (pHead2 == NULL)
         return pHead1;
     ListNode *pHeadFirst = NULL;
     if (pHead1->m_nValue < pHead2->m_nValue)
     {
         pHeadFirst = pHead1;
         if (pHead1->m_pNext != NULL)
         {

             pHeadFirst->m_pNext = Merge(pHead1->m_pNext, pHead2);
         }
     }
     else
     {
         pHeadFirst = pHead2;
         if (pHead2->m_pNext != NULL)
         {

             pHeadFirst->m_pNext = Merge(pHead2->m_pNext, pHead1);
         }
     }
     return pHeadFirst;
 }



 struct BinaryTreeNode
 {
     int m_nValue;
     BinaryTreeNode *m_pLeft;
     BinaryTreeNode *m_pRight;
 };


12 搜索二叉树转化为双向链表


方法1
 BinaryTreeNode * ConvertNode(BinaryTreeNode *pRootOfTree, BinaryTreeNode *pLToR);
 BinaryTreeNode * Convert(BinaryTreeNode *pRootOfTree)
 {
     BinaryTreeNode * pLToR = NULL;
     pLToR=ConvertNode(pRootOfTree, pLToR);
     BinaryTreeNode * pHead = pLToR;
     while (pHead != NULL&&pHead->m_pLeft!=NULL)
     {
         pHead = pHead->m_pLeft;
     }
     return pHead;
 }
 BinaryTreeNode * ConvertNode(BinaryTreeNode *pRootOfTree, BinaryTreeNode *pLToR)
 {
     if (pRootOfTree == NULL)
         return NULL;
     BinaryTreeNode *pCurr = pRootOfTree;
     if (pCurr->m_pLeft != NULL)
     {
         pLToR=ConvertNode(pCurr->m_pLeft, pLToR);
     }
     if (pLToR != NULL)
         pLToR->m_pRight = pCurr;
     pCurr->m_pLeft = pLToR;
     pLToR = pCurr;
     if (pCurr->m_pRight != NULL)
     {
         pLToR = ConvertNode(pCurr->m_pRight, pLToR);
     }
     return pLToR;
 }
 
方法2
 void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList);
 BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)
 {
     BinaryTreeNode *pLastNodeInList = NULL;
     ConvertNode(pRootOfTree, &pLastNodeInList);

     // pLastNodeInList指向双向链表的尾结点,
     // 我们需要返回头结点
     BinaryTreeNode *pHeadOfList = pLastNodeInList;
     while (pHeadOfList != NULL && pHeadOfList->m_pLeft != NULL)
         pHeadOfList = pHeadOfList->m_pLeft;

     return pHeadOfList;
 }

 void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList)
 {
     if (pNode == NULL)
         return;

     BinaryTreeNode *pCurrent = pNode;

     if (pCurrent->m_pLeft != NULL)
         ConvertNode(pCurrent->m_pLeft, pLastNodeInList);

     pCurrent->m_pLeft = *pLastNodeInList;
     if (*pLastNodeInList != NULL)
         (*pLastNodeInList)->m_pRight = pCurrent;

     *pLastNodeInList = pCurrent;

     if (pCurrent->m_pRight != NULL)
         ConvertNode(pCurrent->m_pRight, pLastNodeInList);
 }
 


13 复杂链表的复制

注意是复制,不是剪切,也就意味着要开辟新的内存空间,新的地址;之前的内存空间链表也不能去掉析构!

 struct CompLexListNode
 {
     int m_nValue;
     CompLexListNode *m_pNext;
     CompLexListNode *m_pSib;
 };
 void CloneNodes(CompLexListNode * pHead)
 {
     if (pHead == NULL)
         return;
     CompLexListNode * pNode = pHead;
     while (pNode != NULL)
     {
         CompLexListNode * pCloned = new CompLexListNode();
         pCloned->m_nValue = pNode->m_nValue;
         pCloned->m_pNext = pNode->m_pNext;
         pCloned->m_pSib = NULL;
         pNode->m_pNext = pCloned;
         pNode = pCloned->m_pNext;
     }
 }

 void ConnectNodes(CompLexListNode * pHead)
 {
     if (pHead == NULL)
         return;
     CompLexListNode * pNode = pHead;
     while (pNode!= NULL)
     {
         CompLexListNode *pCloned = pNode->m_pNext;
         if (pNode->m_pSib != NULL)
         {
             pCloned->m_pSib = pNode->m_pSib->m_pNext;
         }        
         pNode = pCloned->m_pNext;
     }
 }


 CompLexListNode * Remove(CompLexListNode *pHead)
 {
     if (pHead == NULL)
         return NULL;
     CompLexListNode *pNode = pHead;
     CompLexListNode *pCopyHead = pNode->m_pNext;
     while (pNode->m_pNext->m_pNext != NULL)
     {    
        
         CompLexListNode *pCloned = pNode->m_pNext;
         CompLexListNode *pNodeNext= pCloned->m_pNext;
         CompLexListNode *pClonedNext= pNodeNext->m_pNext;        
         pNode->m_pNext = pNodeNext;
         pCloned->m_pNext = pClonedNext;
         pNode = pNodeNext;
     }
     pNode->m_pNext = NULL;
     return pCopyHead;
 }

 CompLexListNode *Clone(CompLexListNode *pHead)
 {
     CloneNodes( pHead);
     ConnectNodes(pHead);
     return Remove(pHead);
 }
 

 

原创粉丝点击