原题是Leetcode https://leetcode.com/problems/linked-list-cycle/
判断下图中的链表是否有循环
一开始我的想法很简单,做一个循环,然后把之前遇到过的地址放在一个数组中,遇到新的地址就和之前的数组比对,看是否存在,如果存在,则返回true。
代码很简单,很快就能写好。
bool hasCycle(struct ListNode *head) {
char * tab[10000];
int size = 0;
while(head)
{
tab[size++] = head;
head = head->next;
for(int i = 0; i < size; i++)
if(head == tab[i])
return true;
}
return false;
}
能够通过但是成绩却很差,时间复杂度基本上是O(N2)。看了网上一些思路,其实还有更高效两种解法。
1 步长
bool hasCycle(struct ListNode *head) {
if (head == NULL || head->next == NULL) {
return false;
}
struct ListNode *slow = head;
struct ListNode *fast = head->next;
while (fast != NULL && fast->next != NULL) {
if (slow == fast) {
return true; // Cycle detected
}
slow = slow->next;
fast = fast->next->next;
}
return false; // No cycle found
}
一开始不好理解,但是如果把链表看成是一个圈,那么快指针没多久就会转回来,最后和慢指针重合,从而检测到闭环。
2 检测内存位置:
bool hasCycle(struct ListNode *head) {
while(head != NULL && head->next != NULL)
{
if(head->next <= head)
return true;
head = head->next;
}
return false; // No cycle found
}
从代码简洁测度,这个几乎是最简单的一版了。效率也很好。就是判断新的节点地址是否小于之前节点,如果是,则存在闭环。
有点好奇这个方案,我也写了一个程序验证地址的问题。
int *p1 = malloc(sizeof(int));
printf("%p\n", p1);
p1 = malloc(sizeof(int));
printf("%p\n", p1);
p1 = malloc(sizeof(int));
printf("%p\n", p1);
p1 = malloc(sizeof(int));
printf("%p\n", p1);
p1 = malloc(sizeof(int));
printf("%p\n", p1);
p1 = malloc(sizeof(int));
printf("%p\n", p1);
p1 = malloc(sizeof(int));
printf("%p\n", p1);
p1 = malloc(sizeof(int));
printf("%p\n", p1);
p1 = malloc(sizeof(int));
printf("%p\n", p1);
输出确实是依次增加4字节:
0X00A02028
0X00A02038
0X00A02048
0X00A02058
0X00A02068
0X00A02078
0X00A02088
借网上大神的一张图,malloc是分配在下面蓝色的部分,从下往上。
leetcode的初始链表应该是使用malloc。malloc的内存是在堆上分配,从下往上,如果新的malloc内容,一般来说地址都在更高位。所以第二种方法是根据这种特性来判断的。
但是呢。。。个人觉得这种方法还是太投机了,leetcode这边是按照顺序分配,但是谁能保证其它情况后面的节点就一定是后分配内存呢?我先分配了后面再用难道不行?所以还是第一种方法为佳。