常见的链表面试题大汇总:
源代码下载链接
1,创建一个链表结点
2,遍历链表中的所有结点
3,倒序打印链表
4,往链表末尾添加结点
5,往链表前端添加结点,
6,获取链表的节点数目
7,销毁链表
8,求链表中倒数第K个节点
9,反转链表
10,查找链表中间节点
11,判断链表是否有环
12,判断链表是否有环,并返回环上的节点数目
13,判断链表是否有环,并返回环上的入口节点
14,两个链表中的第一个公共节点
15,合并两个有序链表,
16,用O(1)的时间复杂度删除单链表中的某个节点
17,链表的排序–采用选择排序算法实现 复杂度n^2
18,链表的排序–采用归并排序算法实现 复杂度log(n)
19,链表中结点去重
20,给定链表中间某个节点,将待插入节点插入给定节点之前
#include <vector>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
//链表结构 C语言结构
typedef struct ListNode
{
int m_nValue;
struct ListNode* m_pNext;
}ListNode;
//链表结构 C++语言结构
struct ListNodeCpp
{
int m_nValue;
ListNodeCpp * m_pNext;
ListNodeCpp():m_nValue(),m_pNext(NULL){}
ListNodeCpp(int value):m_nValue(value),m_pNext(NULL){}
};
/*
创建一个链表结点
*/
ListNode* CreateListNode(int value)
{
ListNode *pNode = new ListNode;
pNode->m_nValue = value;
pNode->m_pNext = NULL;
return pNode;
}
/*
遍历链表中的所有结点
*/
void PrintList(ListNode* pHead)
{
ListNode *pNode = pHead;
while(pNode != NULL)
{
cout<<pNode->m_nValue<<" ";
pNode = pNode->m_pNext;
}
cout<<endl;
}
/*
倒序打印链表
*/
void PrintListReverse(ListNode* pHead)
{
if (pHead == NULL)
{
return ;
}
PrintListReverse(pHead->m_pNext);
cout<<pHead->m_nValue<<ends;
}
/*
往链表末尾添加结点
注意这里pHead是一个指向指针的指针,在主函数中一般传递的是引用。
因为如果要为链表添加结点,那么就会修改链表结构,所以必须传递引用才能够保存修改后的结构。
*/
void AddToTail(ListNode* & pHead,int value)
{
ListNode* pNew = new ListNode();//新插入的结点
pNew->m_nValue = value;
pNew->m_pNext = NULL;
if(pHead == NULL)//空链表
{
pHead = pNew;
}
else
{
ListNode* pNode = pHead;
while(pNode->m_pNext!=NULL)
pNode=pNode->m_pNext;
pNode->m_pNext=pNew;
}
}
/*
往链表前端添加结点
*/
void AddToHead(ListNode* & pHead,int value)
{
ListNode *pNew = new ListNode;
pNew->m_nValue = value;
pNew->m_pNext = pHead;
pHead = pNew;
}
/*
获取链表的节点数目
*/
int GetListLength(ListNode *pHead)
{
int len =;
ListNode *pNode = pHead;
while(pNode != NULL)
{
len ++;
pNode = pNode->m_pNext;
}
return len;
}
/*
销毁链表
*/
void ReleaseList(ListNode *&pHead)
{
ListNode *pNode = pHead;
ListNode *pNext;
while(pNode != NULL)
{
pNext = pNode->m_pNext;
delete pNode;
pNode = pNext;
}
pHead = NULL;
}
/*
求链表中倒数第K个节点
设置快慢指针,快指针超前慢指针k-1个节点,然后快慢指针再同时遍历链表,
当快指针遍历到链表结尾时,慢指针是倒数第k个节点。
*/
ListNode * FindKthToTail(ListNode *pHead,int k)
{
if (pHead == NULL || k <= )
{
return NULL;
}
ListNode *p1,*p2;
p1 = pHead;
p2 = pHead;
for (int i = ; i < k - ; i ++)
{
if (p1 == NULL)
{
return NULL;
}
p1 = p1->m_pNext;
}
while (p1->m_pNext != NULL)
{
p1 = p1->m_pNext;
p2 = p2->m_pNext;
}
return p2;
}
/*
反转链表
*/
ListNode * ReverseList(ListNode *&pHead)
{
if (pHead == NULL || pHead->m_pNext == NULL)
{
return pHead;
}
ListNode* pReverseHead = NULL;
ListNode* pNode = pHead;
ListNode* pPrev = NULL;
while(pNode != NULL)
{
ListNode *pNext = pNode->m_pNext;
if (pNext == NULL)
{
pReverseHead = pNode;
}
pNode->m_pNext = pPrev;
pPrev = pNode;
pNode = pNext;
}
pHead = pReverseHead;
return pReverseHead;
}
/*
查找链表中间节点
查找链表中间节点,设置快慢指针,快指针一次走两步,慢指针一次走一步
*/
ListNode *SearchMidNode( ListNode *pHead)
{
if ( pHead == NULL )
return NULL;
ListNode *pFast = pHead;
ListNode *pSlow = pHead;
//若节点为N个, N为奇数,则返回第N/2 +1个节点, N为偶数,则返回第N/2个节点
while( pFast->m_pNext != NULL &&
pFast->m_pNext->m_pNext != NULL)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
}
return pSlow;
}
/*
判断链表是否有环
使用两个指针,快指针一次走两个节点,慢指针一次走一个节点,相遇则表示有环,否则无环。
*/
bool CheckCircle(ListNode *pHead)
{
if (pHead == NULL)
{
return false;
}
ListNode *pFast = pHead;
ListNode *pSlow = pHead;
while( pFast->m_pNext != NULL &&
pFast->m_pNext->m_pNext != NULL)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
if (pFast == pSlow)
{
return true;
}
}
return false;
}
/*
判断链表是否有环,并返回环上的节点数目
*/
int GetCircleNodeNum(ListNode *pHead)
{
if (pHead == NULL)
{
return ;
}
ListNode *pFast = pHead;
ListNode *pSlow = pHead;
while( pFast->m_pNext != NULL &&
pFast->m_pNext->m_pNext != NULL)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
if (pFast == pSlow)
{
int num = ;
pFast = pFast->m_pNext;
while (pFast != pSlow)
{
num ++;
pFast = pFast->m_pNext;
}
return num;
}
}
return ;
}
/*
判断链表是否有环,并返回环上的入口节点
首先获取换上节点的数据K,然后使用间隔K的两个指针,相遇的时候 即为入口
*/
ListNode *GetEnterNodeOfCircle(ListNode *pHead)
{
if (pHead == NULL)
{
return NULL;
}
int len = GetCircleNodeNum(pHead);
if (len == )
{
return NULL;
}
ListNode* p1 = pHead;
ListNode* p2 = pHead;
for (int i = ;i < len ; i ++)
{
if (p1->m_pNext != NULL)
{
p1 = p1->m_pNext;
}
}
while (p1 != p2)
{
p1 = p1->m_pNext;
p2 = p2->m_pNext;
}
return p1;
}
/*
两个链表中的第一个公共节点
首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短
的链表多几个节点。在第二次遍历的时候,先在较长的节点上走若干步,接着同时
在两个链表上遍历,找到的第一个相同的节点就是它们的公共的节点。
*/
ListNode* FindFirstCommonNode(ListNode* pHead1,ListNode *pHead2)
{
//得到两个链表的长度
unsigned int nLength1 = GetListLength(pHead1);
unsigned int nLength2 = GetListLength(pHead2);
int nLengthDif = nLength1 - nLength2;
ListNode *pHeadLong = pHead1;
ListNode *pHeadShort = pHead2;
if(nLength2 > nLength1 )
{
ListNode *pHeadLong = pHead2;
ListNode *pHeadShort = pHead1;
nLengthDif = nLength2 - nLength1;
}
//先在长链表上走几步,再同时在两个链表上遍历。
for(int i = ;i < nLengthDif;i++)
pHeadLong = pHeadLong->m_pNext;
while((pHeadLong != NULL)&&(pHeadShort != NULL)
&&(pHeadLong != pHeadShort ))
{
pHeadLong = pHeadLong->m_pNext;
pHeadShort = pHeadShort->m_pNext;
}
//得到第一个公共节点
ListNode *pFirstCommonNode = pHeadLong ;
return pFirstCommonNode;
}
/*
合并两个有序链表,
非递归方法
*/
ListNode *MergeTwoList(ListNode *pListOneHead, ListNode *pListTwoHead)
{
if (pListOneHead == NULL)
{
return pListTwoHead;
}
if (pListTwoHead == NULL)
{
return pListOneHead;
}
ListNode *pNode1 = pListOneHead;
ListNode *pNode2 = pListTwoHead;
ListNode *pMergeListHead = NULL;
ListNode *pCurLastNode = NULL;
if (pNode1->m_nValue < pNode2->m_nValue)
{
pMergeListHead = pListOneHead;
pNode1 = pNode1->m_pNext;
pCurLastNode = pMergeListHead;
}
else
{
pMergeListHead = pListTwoHead;
pNode2 = pNode2->m_pNext;
pCurLastNode = pMergeListHead;
}
while (pNode1 != NULL && pNode2 != NULL)
{
if (pNode1->m_nValue < pNode2->m_nValue)
{
pCurLastNode->m_pNext = pNode1;
pCurLastNode = pNode1;
pNode1 = pNode1->m_pNext;
}
else
{
pCurLastNode->m_pNext = pNode2;
pCurLastNode = pNode2;
pNode2 = pNode2->m_pNext;
}
if (pNode1 == NULL)
{
pCurLastNode->m_pNext = pNode2;
}
if (pNode2 == NULL)
{
pCurLastNode->m_pNext = pNode1;
}
}
return pMergeListHead;
}
/*
用O(1)的时间复杂度删除单链表中的某个节点
一般单链表删除某个节点,需要知道删除节点的前一个节点,则需要O(n)的遍历时间,
显然常规思路是不行的。在仔细看题目,换一种思路,既然不能在O(1)得到删除节点的
前一个元素,但我们可以轻松得到后一个元素,这样,我们何不把后一个元素赋值给待
删除节点,这样也就相当于是删除了当前元素。可见,该方法可行,但如果待删除节点
为最后一个节点,则不能按照以上思路,没有办法,只能按照常规方法遍历,时间复杂
度为O(n),是不是不符合题目要求呢?可能很多人在这就会怀疑自己的思考,从而放
弃这种思路,最后可能放弃这道题,这就是这道面试题有意思的地方,虽看简单,但是
考察了大家的分析判断能力,是否拥有强大的心理,充分自信。其实我们分析一下,仍然
是满足题目要求的,如果删除节点为前面的n-1个节点,则时间复杂度为O(1),只有删除
节点为最后一个时,时间复杂度才为O(n),所以平均的时间复杂度为:
(O(1) * (n-1) + O(n))/n = O(1);仍然为O(1).
*/
void DeleteNode(ListNode *pListHead, ListNode *pToBeDeleted)
{
if (pListHead == NULL || NULL == pToBeDeleted)
return;
if (pToBeDeleted->m_pNext != NULL)
{
ListNode *pNext = pToBeDeleted->m_pNext;
pToBeDeleted->m_pNext = pNext->m_pNext;
pToBeDeleted->m_nValue = pNext->m_nValue;
delete pNext;
pNext = NULL;
}
else
{ //待删除节点为尾节点
ListNode *pTemp = pListHead;
while(pTemp->m_pNext != pToBeDeleted)
pTemp = pTemp->m_pNext;
if (pTemp == NULL)
{
return ;
}
pTemp->m_pNext = NULL;
delete pToBeDeleted;
pToBeDeleted = NULL;
}
}
/*
链表的排序--采用选择排序算法实现 复杂度n^2
*/
ListNode* GetMinNode(ListNode *pHead)
{
ListNode *pMin = pHead;
ListNode*pNode = pHead;
while(pNode != NULL)
{
if (pNode->m_nValue < pMin->m_nValue)
{
pMin = pNode;
}
pNode = pNode->m_pNext;
}
return pMin;
}
ListNode * SelectSortList(ListNode *pHead)
{
if (pHead == NULL)
{
return pHead;
}
ListNode *pCur = pHead;
ListNode *pMin = NULL;
while (pCur->m_pNext != NULL)
{
pMin = GetMinNode(pCur->m_pNext);
if (pMin->m_nValue < pCur->m_nValue)
{
int temp = pMin->m_nValue;
pMin->m_nValue = pCur->m_nValue;
pCur->m_nValue = temp;
}
pCur = pCur->m_pNext;
}
return pHead;
}
/*
链表的排序--采用归并排序算法实现 复杂度log(n)
*/
ListNode* getMid(ListNode* node)
{
if (node == NULL || node->m_pNext == NULL)
{
return node;
}
ListNode* l1 = node;
ListNode* l2 = node->m_pNext;
while (l2 && l2->m_pNext)
{
l1 = l1->m_pNext;
l2 = l2->m_pNext->m_pNext;
}
return l1;
}
ListNode* MergeList(ListNode* left, ListNode* right)
{
if (left == NULL)
{
return right;
}
if (right == NULL)
{
return left;
}
ListNode* temp = NULL;
if (left->m_nValue >= right->m_nValue)
{
temp = right->m_pNext;
right->m_pNext = left;
left = right;
right = temp;
}
left->m_pNext = MergeList(left->m_pNext, right);
return left;
}
ListNode *MergeSortList(ListNode* head)
{
if (head == NULL || head->m_pNext == NULL)
{
return head;
}
ListNode* mid = getMid(head);
ListNode* right = NULL;
if (mid != NULL)
{
right = mid->m_pNext;
mid->m_pNext = NULL;
}
head = MergeSortList(head);
right = MergeSortList(right);
head = MergeList(head, right);
return head;
}
/*
链表中结点去重
第一步:链表排序
第二步:遍历一遍链表 若重复删除后者
*/
void DelRepeatNode(ListNode *pHead)
{
if (pHead == NULL)
{
return;
}
SelectSortList(pHead);
ListNode *pNode = pHead;
while(pNode->m_pNext != NULL)
{
if (pNode->m_nValue != pNode->m_pNext->m_nValue)
{
pNode = pNode->m_pNext;
}
else
{
ListNode *pNext = pNode->m_pNext;
pNode->m_pNext = pNext->m_pNext;
delete pNext;
}
}
}
/*
给定链表中间某个节点,将待插入节点插入给定节点之前
先将待插入节点插入给定节点之后,然后交换这两个节点数据,就相当于将带插入节点插入给定节点之前
*/
void InsertNodeBefore(ListNode *pNode,ListNode *pToBeInsert)
{
if (pNode == NULL || pToBeInsert == NULL)
{
return;
}
pToBeInsert->m_pNext = pNode->m_pNext;
pNode->m_pNext = pToBeInsert;
int temp = pToBeInsert->m_nValue;
pToBeInsert->m_nValue = pNode->m_nValue;
pNode->m_nValue = temp;
}
int main()
{
ListNode *pHead = NULL;
cout<<"尾插法建立链表"<<endl;
for (int i = ;i < ;i ++)
{
AddToTail(pHead,i);
}
cout<<"打印链表"<<endl;
PrintList(pHead);
cout<<endl;
cout<<"获取链表的节点数目"<<endl;
cout<<GetListLength(pHead);
cout<<endl;
cout<<"倒序打印链表"<<endl;
PrintListReverse(pHead);
cout<<endl;
cout<<"销毁链表"<<endl;
ReleaseList(pHead);
cout<<endl;
cout<<"头插法建立链表"<<endl;
for (int i = ;i < ;i ++)
{
AddToHead(pHead,i);
}
PrintList(pHead);
cout<<endl;
cout<<"寻找倒数第K个节点"<<endl;
ListNode *pKthToTail = FindKthToTail(pHead,);
if (pKthToTail == NULL)
{
cout<<"NULL"<<endl;
}
else
{
cout<<pKthToTail->m_nValue<<endl;
}
cout<<endl;
cout<<"反转链表"<<endl;
pHead = ReverseList(pHead);
PrintList(pHead);
cout<<endl;
cout<<"查找链表中间节点"<<endl;
ListNode *pNode1 = SearchMidNode(pHead);
cout<<pNode1->m_nValue<<endl;
cout<<endl;
cout<<"判断链表是否有环"<<endl;
cout<<CheckCircle(pHead)<<endl;
cout<<"销毁链表"<<endl;
ReleaseList(pHead);
cout<<endl;
cout<<"创建一个有环的链表"<<endl;
AddToHead(pHead,);
ListNode *p1 = pHead;
for (int i = ;i < ;i ++)
{
AddToHead(pHead,i);
}
ListNode *p2 = pHead;
p1->m_pNext = p2;
for (int i = ;i < ;i ++)
{
AddToHead(pHead,i);
}
// PrintList(pHead);//会死循环
cout<<endl;
cout<<"判断链表是否有环"<<endl;
cout<<CheckCircle(pHead)<<endl;
cout<<endl;
cout<<"判断链表是否有环,并返回环上的节点数目"<<endl;
cout<<GetCircleNodeNum(pHead)<<endl;
cout<<endl;
cout<<"判断链表是否有环,并返回环上的入口节点"<<endl;
cout<<GetEnterNodeOfCircle(pHead)->m_nValue<<endl;
// cout<<"销毁链表"<<endl;
// ReleaseList(pHead);
ListNode *pHead1 = NULL;
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
AddToTail(pHead1,);
cout<<endl;
cout<<"打印链表"<<endl;
PrintList(pHead1);
cout<<endl;
cout<<"链表排序"<<endl;
pHead1 = MergeSortList(pHead1);
PrintList(pHead1);
cout<<endl;
cout<<"链表去重"<<endl;
DelRepeatNode(pHead1);
cout<<endl;
cout<<"打印链表"<<endl;
PrintList(pHead1);
{
ListNode *pNode2 = new ListNode;
pNode2->m_nValue = ;
pNode2->m_pNext = NULL;
cout<<endl;
cout<<"给定链表中间某个节点,将待插入节点插入给定节点之前"<<endl;
InsertNodeBefore(pHead1,pNode2);
PrintList(pHead1);
}
return ;
}
源代码