单链表存在环的问题

问题

给定一个单链表,只给出头指针header

  1. 判断链表是否存在环
  2. 求环的长度
  3. 求环的连接点
  4. 求带环链表的长度

// 简单的链表定义及生成带环链表函数
typedef struct Node {
        struct Node *next;
} node;

node*  makeNode() {
        node * pNode = (node*)malloc(sizeof(node));
        pNode->next = NULL;
        return pNode;
}
typedef node* list;

node* constructCircle() {
        list header = makeNode();
        node *pNode = header;
        node *pTmpNode = NULL;
        srand(time(NULL));
        int rnd = rand()%50;
        printf("rand value:%d\n", rnd);
        for (int i = 0; i < 50; i++) {
                pNode->next = makeNode();
                pNode = pNode->next;
                if (i == rnd)
                    pTmpNode = pNode;

        }
        pNode->next = pTmpNode;

        return header;
}

// 1 判断链表是否存在环
int isCircleExist(node *header) {
        node *slow = header;
        node *fast = header;
        // 通过判断fast指针,则不必判断slow指针
        while (fast != NULL && fast->next != NULL) {
                fast = fast->next->next;
                slow = slow->next;
                if (slow == fast) {
                    return 1;
                }
        }

        return 0;
}

fast指针步长选择

fast指针步长能不能选择3、4 或5等?

能,fast指针步长可以选择3、4或5等其他步长。

fast指针步长为什么选择2?


第二问证明:
假设单链表的总长度为L,头结点到环入口的距离为a,环入口到快慢指针相遇的结点距离为x,环的长度为r,慢指针总共走了s步,则快指针走了2s步。另外,快指针要追上慢指针的话快指针至少要在环里面转了一圈多(假设转了n圈加x的距离),得到以下关系:

s = a + x;
2s = a + nr + x;
=>a + x = nr;
=>a = nr - x;

由上式可知:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点。如下图所示:

这里写图片描述

// 2 求环的长度
int circleLength(node *header) {
        node *slow = header;
        node *fast = header;
        int i = 0, j = 0;
        int meet = 0;
        while (fast != NULL && fast->next != NULL) {
                fast = fast->next->next;
                slow = slow->next;
                i = i+2;
                j++;
                if (slow == fast) {
                        meet++;
                        // 两指针第一次相遇,保证了他们已经在环内
                        if (meet == 1) {
                                i = 0;
                                j = 0;
                        }
                        // 两支针第二次相遇,则快指针比慢指针长走的长度刚好多一圈
                        if (meet == 2) {
                                return i-j;
                        }
                }
        }

        return 0;
}

// 3 求环的连接点
node* findCirclePort(node *header) {
        node *slow = header;
        node *fast = header;
        while (fast != NULL && fast->next != NULL) {
                fast = fast->next->next;
                slow = slow->next;
                if (slow == fast) {
                        break;
                }
        }
        if (fast == NULL || fast->next == NULL)
                return NULL;
        slow = header;
        while (slow != fast) {
                slow = slow->next;
                fast = fast->next;
        }
        return slow;
}

// 4 求带环链表的长度
int listLengthWithCircle(node *header) {
        node *port = findCirclePort(header);
        int len = 0;
        node *p = header;
        while (p->next != port) {
                len++;
                p = p->next;
        }

        len += circleLength(header);
        return len;
}

参考

判断一个单链表是否有环及环的链接点

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值