<剑指Offer>面试题23: 链表中环的入口节点

题目描述 牛客网

  • 如果一个链表中包含环,如何找出环的入口节点?
  • 例如,在如下图所示的链表中,环的入口节点是节点3

题目解读

  • 剑指Offer 139
  • 书上思路挺想详细的,请自行翻阅
  • 第一步:确定一个链表中是否包含环。受到面试题22的启发,我们可以用两个指针来解决这个问题。和前面问题一样,定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就包含环;如果走得快的指针走到了链表的末尾都没有追上第一个指针,那么链表就不包含环
  • 第二步:得到环中节点数目。我们在前面提到判断一个链表里是否有环时用到了一快一慢指针。如果两个指针相遇,则表明链表中存在环。两个指针相遇的节点一定是在环中。可以从这个节点出发,一边继续向前移动一边计数,当再次回到这个节点时,就可以得到环中节点数了
  • 第三步:找到环的入口。我们还是可以用两个指针来解决这个问题。先定义两个指针 p1 、p2 指向链表的头节点。如果链表中的环有 n 个节点,则指针 p1 先在链表上向前移动 n 步,然后两个指针以相同的速度向前移动。当第二个指针指向环的入口节点时,第一个指针已经围绕着环走了一圈,又回到了入口节点

代码

  • 第一轮代码
#include<iostream>
using namespace std;

struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};

class Solution {
public:
    // 找到环中共有多少个节点
    // 此处注意必须注意编写代码时要考虑指针为空的情况,要不然程序太不健壮了.. 
    int find_loop_sum(ListNode* pHead){
        int n = 0;
        ListNode *kuai = NULL;
        ListNode *man  = NULL;

        if(pHead->next){
            man = pHead -> next;
        }else{
            return 0;
        }

        if(man->next){
            kuai = man -> next;
        }else{
            return 0;
        }

        while(kuai != man){
            if(kuai->next && kuai->next->next){
                kuai = kuai -> next -> next;
                man  = man  -> next; 
            }else{
                return 0;
            }
        }

        // 代码运行到这里的时候,kuai man 指针指向同一个节点且均在环中,
        // 此时,让一个指针在原地等待,一个在环中跑一圈,当另一个指针再次回到原点时,则遍历完环中节点数目
        while(kuai -> next != man){
            kuai = kuai -> next;
            n++;
        }
        n++;

        cout<<n<<endl;

        return n;
    }

    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        if(!pHead){
            return NULL;
        }

        int n = find_loop_sum(pHead);

        if(n == 0){
            return NULL;
        }

        ListNode *p1 = pHead;
        ListNode *p2 = pHead;

        for(int i=0; i < n; i++){
            p1 = p1 -> next;
        }

        while(p1 != p2){
            p1 = p1 -> next;
            p2 = p2 -> next;
        }
        return p1;
    }


    ListNode* create(){
        ListNode *p;
        ListNode *head = NULL;
        ListNode *tail = NULL;
        ListNode *loop = NULL;

        for(int i=1; i <= 6; i++){
            p = new ListNode(i);

            if(i == 3){
                loop = p;
            }

            if(!head){
                head = p;
            }
            else{
                tail -> next = p;
            }
            tail = p;
        }
        tail -> next = loop;

        return head;
    }

    void print_Node(ListNode *head){
        for(int i = 1; i <= 10; i++){
            cout<<head->val<<" ";
            head = head->next;
        }
        cout<<endl;
    }
};

main(){
    Solution ss;
    ListNode *head = NULL;
    ListNode *loop = NULL;

    head = ss.create();
    ss.print_Node(head);
    // cout<<ss.find_loop_sum(head)<<endl;
    loop = ss.EntryNodeOfLoop(head);
    cout<<loop->val<<endl;
}
  • 第二轮代码
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead){
        if(pHead == NULL){
            return NULL;
        }
        
        // 找到环中节点数目
        int count = find_loop_count(pHead);
        if(count == 0){
            return NULL;
        }
        
        // 找到环的入口
        ListNode* p1 = pHead;
        ListNode* p2  = pHead;
        for(int i=0; i < count; i++){
            p1 = p1->next;
        }
        
        while(p1 != p2){
            p1 = p1->next;
            p2 = p2->next;
        }
        return p1;
    }
    
    // 找到环中节点数目
    int find_loop_count(ListNode* pHead){
        ListNode* kuai = NULL;
        ListNode* man  = NULL;
        
        // 第一步先判断是否有环
        if(pHead->next != NULL && pHead->next->next != NULL){
            man = pHead->next;
            kuai = pHead->next->next;
        }else{
            return 0;
        }
        while(kuai != man){
            if(kuai->next->next != NULL && man->next != NULL){
                kuai = kuai->next->next;
                man  = man->next;
            }else{
                return 0;
            }
        }
        
        // 找到环中数目
        int count = 0;
        while(kuai->next != man){
            kuai = kuai->next;
            count++;
        }
        count++;
        return count;
    }
};

2023.5.25 第三轮

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
#include <cstddef>
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        if (!pHead) return pHead;
        int n = GetLoopCount(pHead);
        std::cout << "n = " << n << std::endl;
        if (n == 0) return NULL;

        ListNode* p1 = pHead;
        ListNode* p2 = pHead;
        for (int i=0; i<n; i++) {
            p1 = p1->next;
        }
        while (p1 != p2) {
            p1 = p1->next;
            p2 = p2->next;
        }
        return p1;
    }
    int GetLoopCount(ListNode* pHead) {
        int n = 0;
        bool flag = false;
        // 1、一快一慢两个指针先判断是否有环
        ListNode* kuai = pHead;
        ListNode* man = pHead;
        while (kuai->next && kuai->next->next) {
            kuai = kuai->next->next;
            man = man->next;
            if (kuai == man) {
                flag = true;
                break;
            }
        }
        if (flag) {
            kuai = kuai->next;
            while (kuai != man) {
                kuai = kuai->next;
                n++;
            }
            n++;
        }
        return n;
    }
};

总结展望

  • 越来越发现剑指 offer 题目是真好,自己见识还是太浅显
  • 认真,努力,勤奋
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容