链表的环的问题一般分为三个阶段:判断是否有环,环的长度,环的起点。
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而言,它映射的是
9∗9∗7=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;
}//直观一点写要改将不同的功能封装在不同的函数中,这样写会节约一点时间。