有关链表环的问题

链表的环的问题一般分为三个阶段:判断是否有环,环的长度,环的起点。

1.判断是否有环:利用快慢指针。

两个指针从同一个起点出发,一个走两步,一个走一步。当两个指针相遇时,则证明有环。这个组合应该是最快的了。当提升1倍的速度,必须考虑路程的增加必须少于环长的一半,否则是没有效果的。当环比较大的时候,这是不合适的。

int linkedListCycleLength(ListNode *head) {
    if (head == NULL) {
        return 0;
    }
    ListNode *p = head, *q = head;
    while (q != NULL && q->next != NULL) {
        p = p->next;
        q = q->next->next;
        if (p == q) {
            int count = 1;
            p = p->next;
            q= q->next->next;
            while(p != q) {
                p = p->next;
                q = q->next->next;
                count++;
            }
            return count;
        }
    }
    return 0;
}

这个思路可以解决LeetCode的202题HappyNum.
A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.
对于一个7位数9999999而言,它映射的是 997=567 9 ∗ 9 ∗ 7 = 567 这就证明这种映射不是意义对应的。也就是说当一个数按照则和套规则构建时如果产生了环,那就证明它不是HappyNum。

int change(int n ) {
    int sum = 0;
    while(n > 0) {
        sum += (n % 10) * (n % 10);
        n /= 10;    
    }
    n = sum;
    return n;

}

bool isHappy(int n) {
    if (n < 1) {
        return false;
    }
    if (n == 1) return true;
    int p, q;
    p = n;
    q = change_num(n);
    while(q != 1) {
        p = change_num(p);
        q = change_num(change_num(q));
        if (p == q) {
            return false;
        }
    }
    return true;

}//链表只是思维上的链表,我们没有必要构建出完整的链表。只需要构建逻辑上的链表,按照链表判断是否有环来判断就可。

2判断环的长度:当我们确定了是否有环时,p和q会在环中相遇。这个时候,如果再次利用快慢指针,一个走一步,一个走两步,当他们再次相遇时,快的指针刚好比慢的指针多走一个环的长度。

int linkedListCycleLength(ListNode *head) {
    if (head == NULL) {
        return 0;
    }
    ListNode *p = head, *q = head;
    while (q != NULL && q->next != NULL) {
        p = p->next;
        q = q->next->next;
        if (p == q) {
            int count = 1;
            p = p->next;
            q= q->next->next;
            while(p != q) {
                p = p->next;
                q = q->next->next;
                count++;
            }
            return count;
        }
    }
    return 0;
}

3.找环的起点

当我们知道了环的长度,我们可以让两个指针相隔环的长度,当两个指针相遇时,就找到了环的起点。

ListNode* linkedListCycleLinkedNode(ListNode *head) {
     if (head == NULL) {
        return NULL;
    }
    ListNode *p = head, *q = head;
    while (q != NULL && q->next != NULL) {
        p = p->next;
        q = q->next->next;
        if (p == q) {
            ListNode *m = head, *n = head;
            int count = 1;
            p = p->next;
            q= q->next->next;
            n = n->next;
            while(p != q) {
                p = p->next;
                q = q->next->next;
                count++;
                n = n->next;
            }
            while (n != m) {
                n = n->next;
                m = m->next;
            }
            return n;
        }
    }
    return NULL;

}//直观一点写要改将不同的功能封装在不同的函数中,这样写会节约一点时间。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值