判断链表是否有环
题目
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
函数原型
C的函数原型:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {}
边界条件
如果链表为空,或者只有一个结点且这个结点的下一个元素为空,那就没有环。
bool hasCycle(struct ListNode *head) {
if( head == NULL || head->next == NULL )
return false;
}
算法设计:计时测试
定义一个指针,一直遍历链表,如果有环的话,那遍历不完的。
所以,我们可以卡点计时⌛️,比如 1s。
#include <stdio.h>
#include <sys/time.h> /* 记录开始时间 */
#include <time.h> /* 获取当前时间 */
int main()
{
struct timeval begin; // 记录开始时间的变量
time_t tnow; // 获取当前时间
gettimeofday(&begin,0); // 计时开始
while( 1 ){
tnow=time(0); // 获取当前时间
if( tnow - begin.tv_sec > 1 ){ // 1 秒之后输出 ac,而后退出程序
printf("ac");
return;
}
}
}
#include <sys/time.h> /* 记录开始时间 */
#include <time.h> /* 获取当前时间 */
bool hasCycle(struct ListNode *head) {
if( head == NULL || head->next == NULL )
return false;
struct ListNode *p = head;
while( p != NULL ){
p = p->next;
}
return false;
}
加入计时操作:
#include <sys/time.h> /* 记录开始时间 */
#include <time.h> /* 获取当前时间 */
bool hasCycle(struct ListNode *head) {
if( head == NULL || head->next == NULL )
return false;
struct ListNode *p = head; // 定义一个指针
struct timeval begin; // 记录开始时间的变量
time_t tnow; // 获取当前时间
gettimeofday(&begin,0); // 计时开始
while( p != NULL ){
p = p->next;
tnow=time(0); // 获取当前时间
if( tnow - begin.tv_sec > 1 ) // 卡点计时:1 秒
return true;
}
return false;
}
测试过了,但提交上去说:超出时间限制。
卡点调成 0.5s 试试,慢慢调试吧。
如果按秒算不行,那可以换微秒算,begin.tv_sec 改成 begin.tv_usec 就是微秒。
卡点计时的复杂度:
- 时间复杂度:
- 空间复杂度:
算法设计:做标记
定义一个指针,一直遍历链表,如果有环的话,那遍历不完的。
其实遍历的都是已经遍历过的链表,那我们可以弄一个标记记录走过的路线,如果发现这个路线被标记了,就是有环。
这个标记可以用指针数组或者哈希来实现。
#define MAX_SIZE 10000
bool hasCycle(struct ListNode *head) {
if( head == NULL || head->next == NULL )
return false;
struct ListNode *book[MAX_SIZE]= {NULL};
// book 有标记的意思,book先和[]结合,再和*结合,所以book是一个指针数组,是数组但存放的是指针
struct ListNode *p = head; // 定义一个指针
for( int i=0; p != NULL; i++ ){
for( int j=0; j < MAX_SIZE; j++ )
if( p == book[j] ) // 看看有没有标记
return true;
book[i] = p; // 保存起来
p = p->next; // 遍历下一个
}
return false;
}
AC。
做标记的复杂度:
- 时间复杂度:
- 空间复杂度:
算法设计:快慢指针
思路:定义2个指针,一个指针跑一步,一个指针跑2步;如果2个指针相遇,就存在循环,否则不存在循环。
类似于在学校操场跑圈,跑步、走路的人如果是同向、相向而行,总会碰头的。
bool hasCycle(struct ListNode *head) {
if( head == NULL || head->next == NULL )
return false;
// 定义俩个指针
struct ListNode *p = head; // 慢指针,一次走一步
struct ListNode *pp = head; // 快指针,一次走俩步
while( (pp != NULL) && (pp->next != NULL) ){
p = p->next;
pp = pp->next->next;
if( p == pp )
return true;
}
return false;
}
快慢指针的复杂度:
- 时间复杂度:
- 空间复杂度: