面试类链表题目汇总
来源:互联网 发布:淘宝网老年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,则:
1
2s = s + nr
2
s= nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
1
a + x = nr
2
a + x = (n – 1)r +r = (n-1)r + L - a
3
a = (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.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);
}
- 面试类链表题目汇总
- .net面试题目汇总
- java面试题目汇总
- Java面试题目汇总
- 面试题目汇总
- SQL面试题目汇总
- IT面试题目汇总
- 技术面试题目汇总
- Android面试题目汇总
- html5 面试题目汇总
- Android面试题目汇总
- SQL面试题目汇总
- 面试题目汇总
- 面试题目汇总
- 面试题目汇总
- 前端面试题目汇总
- Python面试题目--汇总
- java 面试题目汇总1
- Maven的存储库问题
- 鲁棒性
- java数据结构和算法
- 程序员提升的知识点
- 开发人员准确理解技术需求:用户想得与说的不一样
- 面试类链表题目汇总
- leetcode 628 Maximum Product of Three Numbers
- 网络部分
- iOS App中调用相册中图片及获取最近的一张图片的方法
- 跟我撩fastjson-第三章:Java泛型的序列化和反序列化
- HDU
- 利用for和if编写的1000以内的完数
- Leetcode 210
- 上传自己的库到 pod 的方法步骤