更多剑指offer面试习题请点击:《剑指offer》(第二版)题集目录索引
1. 题目:
一个链表中包含环,如何找出环的入口结点?例如,在图3.8的链表中,环的入口结点是结点3。
2. 解题思路1:
第一步:判断链表是否带环
首先利用快慢指针法,确定链表是否带环。用两个指针pFast
和pSlow
。开始时两者都指向头结点pHead
,然后同时往后走。快指针一次走两步,慢指针一次走一步。如果最后两者相遇,则链表带环,返回相遇点meetingNode
;如果没有相遇,那链表就不带环,返回nullptr。
第二步:计算环的长度
上一步确定链表带环后可以得到相遇点meetingNode
,让指针pNode1
指向meetingNode
,然后往后走,当它再次遇到meetNode
时已经绕环走了一圈,此时便能得到环的长度lengthOfLoop
。
第三步:求入口点
第二步中求得了环的长度lengthOfLoop
,用两节点pNode1
和pNode2
开始时均指向头结点pHead
。让pNode1
先走lengthOfLoop
步,然后pNode1
和pNode2
同时走,两者相遇时,这个相遇点便是环的入口点。
3. < Code1>
ListNode* MeetingNode(ListNode* pHead)
{
if (pHead == nullptr)
{
return nullptr;
}
// 1. 快慢指针开始都指向链表头结点
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
// 2. 快指针一次走两步,慢指针一次走一步
while (pFast->m_pNext != nullptr && pFast->m_pNext->m_pNext != nullptr)
{
pFast = pFast->m_pNext->m_pNext;
pSlow = pSlow->m_pNext;
if (pFast == pSlow)
{
return pSlow;
}
}
return nullptr;
}
// 方法一:
ListNode* EntryNodeOfLoop1(ListNode* pHead)
{
// 1. 找到相遇点
ListNode* meetingNode = MeetingNode(pHead);
if (meetingNode == nullptr)
return nullptr;
// 2. 计算环的长度
int lengthOfLoop = 1;
ListNode* pNode1 = meetingNode;
while (pNode1->m_pNext != meetingNode)
{
pNode1 = pNode1->m_pNext;
++lengthOfLoop;
}
// 3. 让pNode1 先走环的长度的步数
pNode1 = pHead;
int i = lengthOfLoop;
for (; i > 0; --i)
{
pNode1 = pNode1->m_pNext;
}
// 4. pNode2 从链表起点出发,pNode1和pNode2的相遇点就是环的入口点
ListNode* pNode2 = pHead;
while (pNode1 != pNode2)
{
pNode1 = pNode1->m_pNext;
pNode2 = pNode2->m_pNext;
}
return pNode1;
}
4. 解题思路2:
详解参考博客:链表面试题——下
5. < Code2 >
ListNode* EntryNodeOfLoop2(ListNode* pHead)
{
// 1. 找到相遇点
ListNode* meetingNode = MeetingNode(pHead);
if (meetingNode == nullptr)
return nullptr;
// 2. pNode1 从相遇点出发,pNode2 从链表头结点出发
ListNode* pNode1 = meetingNode;
ListNode* pNode2 = pHead;
// 3. 两者相遇点就是环的入口点
while (pNode1 != pNode2)
{
pNode1 = pNode1->m_pNext;
pNode2 = pNode2->m_pNext;
}
return pNode1;
}
6. < TestCode>
void Test(char* testName, ListNode* pHead, ListNode* entryNode)
{
if (testName != nullptr)
printf("%s begins:\n", testName);
if (EntryNodeOfLoop1(pHead) == entryNode)
{
printf("EntryNodeOfLoop1 Passed.\n");
}
else
{
printf("EntryNodeOfLoop1 FAILED.\n");
}
if (EntryNodeOfLoop2(pHead) == entryNode)
{
printf("EntryNodeOfLoop2 Passed.\n");
}
else
{
printf("EntryNodeOfLoop2 FAILED.\n");
}
printf("\n");
}
// 1. 链表只有一个节点,不带环
void Test1()
{
ListNode* pNode1 = CreateListNode(1);
Test("Test1", pNode1, nullptr);
DestroyList(pNode1);
}
// 2. 链表只有一个节点,带环
void Test2()
{
ListNode* pNode1 = CreateListNode(1);
ConnectListNodes(pNode1, pNode1);
Test("Test2", pNode1, pNode1);
delete pNode1;
pNode1 = nullptr;
}
// 3. 链表有多个节点,带环,入口点为中间结点
void Test3()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode3);
Test("Test3", pNode1, pNode3);
delete pNode1;
pNode1 = nullptr;
delete pNode2;
pNode2 = nullptr;
delete pNode3;
pNode3 = nullptr;
delete pNode4;
pNode4 = nullptr;
delete pNode5;
pNode5 = nullptr;
}
// 4. 链表有多个节点,带环,入口点为头结点
void Test4()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode1);
Test("Test4", pNode1, pNode1);
delete pNode1;
pNode1 = nullptr;
delete pNode2;
pNode2 = nullptr;
delete pNode3;
pNode3 = nullptr;
delete pNode4;
pNode4 = nullptr;
delete pNode5;
pNode5 = nullptr;
}
// 5. 链表有多个节点,带环,入口点为尾结点
void Test5()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode5);
Test("Test5", pNode1, pNode5);
delete pNode1;
pNode1 = nullptr;
delete pNode2;
pNode2 = nullptr;
delete pNode3;
pNode3 = nullptr;
delete pNode4;
pNode4 = nullptr;
delete pNode5;
pNode5 = nullptr;
}
// 6. 链表有多个节点,不带环
void Test6()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
Test("Test6", pNode1, nullptr);
DestroyList(pNode1);
}
// 7. 空链表
void Test7()
{
Test("Test7", nullptr, nullptr);
}
int main(int argc, char* argv[])
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
Test7();
system("pause");
return 0;
}
7. < TestResult>
8. < BasicCode >
ListNode* CreateListNode(int value) //创建节点
{
ListNode* newNode = new ListNode;
newNode->m_nValue = value;
newNode->m_pNext = nullptr;
return newNode;
}
void ConnectListNodes(ListNode* pNode1, ListNode* pNode2) //链接节点
{
pNode1->m_pNext = pNode2;
}
void DestroyList(ListNode* pListHead) //销毁链表
{
if (pListHead == nullptr)
return;
ListNode* pCurNode = pListHead->m_pNext;
while (pCurNode != nullptr)
{
delete pListHead;
pListHead = pCurNode;
pCurNode = pCurNode->m_pNext;
}
delete pListHead;
pListHead = nullptr;
}
void PrintList(ListNode* pListHead) //打印链表
{
ListNode* pCurNode = pListHead;
while (pCurNode != nullptr)
{
printf("%d -> ", pCurNode->m_nValue);
pCurNode = pCurNode->m_pNext;
}
printf("nullptr\n");
}
void PrintListNode(ListNode* pNode) //打印节点
{
if (pNode != nullptr)
{
printf("%d\n\n", pNode->m_nValue);
}
else
{
printf("nullptr\n\n");
}
}