有环单链表衍生问题

给定一个单链表的头指针h,解决以下问题:

  1. 判断该链表是否有环
  2. 如果有环,如何知道环的长度
  3. 找出环的连接点
  4. 求得单链表的长度
背景

图1

假设单链表有环,则相遇点必定在环上。设,环的长度为r,h为头指针,a为“甩尾”的长度,t为连接点,m1为slow和fast相遇的节点,b为m1距离t的长度(逆时针方向),此时假设slow共走了x步,则fast走了2x步。

问题一
思路

给定两个指针slow和fast,slow一次走一步,fast一次走两步。如果fast能等于slow,则说明有环。

证明

假设单链表有环,且slow走了x = nr步 (n为某个正整数且nr >= a),此时fast和slow走的步数差nr步,且二者都进入环内,则二者必定相遇。换言之,若单链表有环,必定存在一个n使得二者相遇(且不为空)。若没有环,则不存在这一特性。

代码

LeeCode题目

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head == NULL)return false;
        ListNode * slow = head;
        ListNode * fast = head;
        while(fast != NULL && fast->next != NULL){
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast)return true;
        }
        return false;
    }
};
问题二
思路

如果单链表有环,则从相遇时,固定fast,走slow,开始计步length。当slow再次与fast相遇时,即走了一圈,此时length即为环的长度

代码
int getRingLength(ListNode* head){
    ListNode* slow(head),*fast(head);
    while(fast != NULL && fast->next != NULL){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast){
            slow = slow->next;
            int length = 1;
            while(slow != fast){
                slow = slow->next;
                length++;
            }
            return length;
        }
    }
    return 0;
}
问题三
思路

如果单链表有环,则找出连接点t。有个定理:分别从头指针和相遇点出发,一次一步,当二者相遇时的节点即为连接点。

证明

有环单链表共有两种情况。
情况一:环中没有节点。即从最后一个节点出发又回到最后一个节点,则b = 1,此时fast一直会在最后一个节点循环,定理成立。
情况二:环中有节点(图1的情况)。假设共走了x步第一次相遇,slow走了x = a + b,fast走了2x = a + nr + b,则a = nr - b。因此,分别从头指针和相遇点出发,头指针走了a步到t,m1则走了nr-b步,刚好也走到了t,定理成立。

代码

LeeCode题目

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head == NULL)return NULL;
        ListNode *slow(head),*fast(head);
        while(fast != NULL && fast->next != NULL){
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast){
                ListNode* meet = slow;
                while(head != meet){
                    head = head->next;
                    meet = meet->next;
                }
                return meet;
            }
        }
        return NULL;
    }
};
问题四
思路

链表的长度即等于 “甩尾”长度 + 环长度,即为从头指针到连接点t长度a + 从t出发再次回到t长度

代码
int getListLength(ListNode* head){
    if(NULL == head) return 0;
    ListNode* meetNode = getCircleStart(head);
    int extraLength = 0;
    while(meetNode != head){
        head = head->next;
        extraLength++;
    }
    int ringLength = getRingLength(head);
    return extraLength + ringLength;
}
完整代码
/******
** 有环单链表
** 1.createLoopList 创建“环”,这是一个循环链表
** 2.createLinkList 创建单链表,需要先创建“环”
** 3.showLinkList 打印有环单链表
** 4.judgeRing 判断单链表是否有环
** 5.getRingLength 获得环长
** 6.getCircleStart 获得连接点位置
** 7.getListLength 获得链表总长度
*******/

#include <iostream>
#include <algorithm>
#include <stack>
#include <set>

using namespace std;

typedef struct ListNode{
    int value;
    ListNode * next;
    ListNode(int val):value(val){};
}ListNode,*ListNodePointer;



ListNode * createLoopList(){
    int value,n;
    cout<<"1.创建循环链表,并返回循环链表起始点。请输入环长度"<<endl;
    cin>>n;
    cout<<"请输入"<<n<<"个节点值"<<endl;

    cin>>value;
    ListNode * circleHead = new ListNode(value);
    if(NULL == circleHead){
        cout<<"创建循环链表失败,节点value:"<<value<<endl;
        exit(-1);
    }

    ListNode * temp = circleHead;
    temp->next = circleHead;
    for(int i = 1;i < n;++i){
        cin>>value;
        ListNode * node = new ListNode(value);
        if(NULL == node){
            cout<<"创建循环链表失败,节点value:"<<value<<endl;
            exit(-1);
        }
        temp->next = node;
        node->next = circleHead;
        temp = node;
    }

    return circleHead;
}

ListNode * createLinkList(){
    cout<<"创建单链表,需要1.先创建循环链表 2.创建甩尾"<<endl;
    ListNode * startCircle = createLoopList();
    ListNode * temp(startCircle),*head(NULL);
    cout<<"2.倒着输入甩尾部分节点"<<endl;
    int value;
    while(cin>>value){
        ListNode* node = new ListNode(value);
        node->next = temp;
        temp = node;
    }
    head = temp;
    return head;
}

void showLinkList(ListNode* head){
    if(NULL == head)
        return;
    cout<<"打印单链表"<<endl;
    set<ListNode*> nodeSet;
    while(head != NULL && nodeSet.count(head) == 0){
        nodeSet.insert(head);
        cout<<head->value<<" ";
        head = head->next;
    }
    cout<<endl;
}

bool judgeRing(ListNode* head){
    ListNode* slow(head),*fast(head);
    while(fast != NULL && fast->next != NULL){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
            return true;
    }
    return false;
}

int getRingLength(ListNode* head){
    ListNode* slow(head),*fast(head);
    while(fast != NULL && fast->next != NULL){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast){
            slow = slow->next;
            int length = 1;
            while(slow != fast){
                slow = slow->next;
                length++;
            }
            return length;
        }
    }
    return 0;
}

ListNode * getCircleStart(ListNode* head){
    ListNode* slow(head),*fast(head);
    while(fast != NULL && fast->next != NULL){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast){
            ListNode* meet = slow;
            while(head != meet){
                head = head->next;
                meet = meet->next;
            }
            return meet;
        }
    }
    return NULL;
}

int getListLength(ListNode* head){
    if(NULL == head) return 0;
    ListNode* meetNode = getCircleStart(head);
    int extraLength = 0;
    while(meetNode != head){
        head = head->next;
        extraLength++;
    }
    int ringLength = getRingLength(head);
    return extraLength + ringLength;
}

int main(int argc,char ** argv){
    ListNode * head = createLinkList();
    showLinkList(head);
    cout<<"链表"<<(judgeRing(head)?"含有":"不含有")<<"环,环长度为"<<getRingLength(head);
    ListNode * meetNode = getCircleStart(head);
    cout<<"。环连接点值为"<<meetNode->value<<",链表总长度为"<<getListLength(head)<<endl;
    return 0;
}

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值