[141].判断链表是否有环

 


题目

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 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 就是微秒。

卡点计时的复杂度:

  • 时间复杂度: Θ ( n ) \Theta (n) Θ(n)
  • 空间复杂度: Θ ( 1 ) \Theta (1) Θ(1)
     

算法设计:做标记

定义一个指针,一直遍历链表,如果有环的话,那遍历不完的。

其实遍历的都是已经遍历过的链表,那我们可以弄一个标记记录走过的路线,如果发现这个路线被标记了,就是有环。

这个标记可以用指针数组或者哈希来实现。

#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。

做标记的复杂度:

  • 时间复杂度: Θ ( n 2 ) \Theta (n^{2}) Θ(n2)
  • 空间复杂度: Θ ( n ) \Theta (n) Θ(n)
     

算法设计 = 快慢指针 & 题目特征 = 需要对链表中的节点进行遍历,并且都涉及到节点之间的相对位置或者距离

思路:定义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;
}

快慢指针的复杂度:

  • 时间复杂度: Θ ( n ) \Theta (n) Θ(n)
  • 空间复杂度: Θ ( 1 ) \Theta (1) Θ(1)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值