《剑指offer》面试题23:链表中环的入口节点

  更多剑指offer面试习题请点击:《剑指offer》(第二版)题集目录索引


1. 题目:

  一个链表中包含环,如何找出环的入口结点?例如,在图3.8的链表中,环的入口结点是结点3。
这里写图片描述


2. 解题思路1:

第一步:判断链表是否带环
  首先利用快慢指针法,确定链表是否带环。用两个指针pFastpSlow。开始时两者都指向头结点pHead,然后同时往后走。快指针一次走两步,慢指针一次走一步。如果最后两者相遇,则链表带环,返回相遇点meetingNode;如果没有相遇,那链表就不带环,返回nullptr。

第二步:计算环的长度
  上一步确定链表带环后可以得到相遇点meetingNode,让指针pNode1指向meetingNode,然后往后走,当它再次遇到meetNode时已经绕环走了一圈,此时便能得到环的长度lengthOfLoop

第三步:求入口点
  第二步中求得了环的长度lengthOfLoop,用两节点pNode1pNode2开始时均指向头结点pHead。让pNode1先走lengthOfLoop步,然后pNode1pNode2同时走,两者相遇时,这个相遇点便是环的入口点。


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");
    }
}

猜你喜欢

转载自blog.csdn.net/tianzez/article/details/80162900
今日推荐