环形链表——java

文章讲述了如何利用链表和Floyd判圈算法(快慢指针)以及哈希集合来检测给定链表中是否存在环。通过比较快慢指针的移动速度,如果它们相遇,则链表有环;反之,遍历整个链表并检查每个节点是否唯一,哈希集合中重复则说明有环。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

解题思路

看到这个题我只想着可以用链表的知识来解决,但完全不知道该怎么下手。所以去看了链表的知识

链表

链表是一种逻辑相连,但物理上不一定相连的存储结构,是由每一格节点组成,(该节点包含val,和next,其中val存放该节点的值,next是一个索引,指向下一个节点)

如何创建一个新的空链表: LinkNode head=new LinkNode(); head.next=null;  (LinkNode是一个节点)

解题一:

所以这个题应该如何使用链表来解决呐?

这里我们介绍Floyd判圈算法(龟兔赛跑算法):

假想「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方;如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。(链接:https://leetcode.cn/problems/linked-list-cycle/solutions/440042/huan-xing-lian-biao-by-leetcode-solution/  来源:力扣(LeetCode))

在该题中我们可以创建两个指针,分别是快慢指针,fast和slow,在刚开始我们设置slow指向head,fast指向head.next(即慢指针指向第一个节点,快指针指向第二个节点),fast一次移动两格,慢指针一次移动一格;所以若在后面的循环过程中,快慢指针相遇,则表明该链表存在环那么如何来判断该链表无环呐?我们在这里可以设置一个循环while(slow!=fast) 若fast指向空(即fast指向链尾),则表明该链表无环;

在这里我们来需要注意,因为在刚开始我们要去创建快慢指针,一个指向第一个节点,一个指向第二个节点,为了防止出现空指针异常的情况,我们还需要对第一个节点和第二个节点捷星判断,若为空,则直接返回false(即或此时空链表或者只有一个节点的聊表是不可能存在环的)
 

if(head==null ||head.next==null)  //判断该链表是否为空链表,或者只有一个节点的链表;这两种情况是不可能存在环的
  return false;

LinkNode slow=head;
LinkNode fast=head.next;  //创建快慢指针


while(slow!=fast)
{
    if(fast==null || fast.next==null)   //如果此时fast指针指向链表尾,或者fast所指节点的后一个节点是链表尾(因为fast一次移动两格),则表明该链表无环
       return false;
    slow=slow.next;
    fast=fast.next.next;//移动快慢指针
}

return true;  //说明此时slow==fast

解题二:

看到还有另外一种解法,就是去遍历整个链表。首先创建一个容器,每遍历一个节点先看容器中是否包含该节点,若不包含则将该节点加入到容器中,继续遍历下一个节点,直到链表为空;若包含,则说明该链表存在环,返回true

那么该容器该如何选择??在这里我们选择哈希集合

哈希集合

HashSet是一个没有重复元素的集合(Set集合是不允许出现重复元素的)

初始化: HashSet<类型>  集合名 =new HashSet<>();

添加:集合名.add(元素);

删除:集合名.remove(元素);

判断是否包含某元素:集合名.countains(元素);

判断是否为空:集合名.isEmpty();

获取大小:集合名.size();

获取所有元素:

  可以使用for:: for(类型 s:集合名){}                   

  可以使用迭代器::Iterator it=集合名.iterator;    while(it.hashNext){}

 (s和it是我这里随便起的名称)

HashSet<LinkNode> hash=new HashSet<>();//创建一个哈希集合

while(head!=null)
{
    if(hash.contains(head))  //如果哈希集合中包含了head节点
        return true;
    hash.add(head);
    head=head.next;
}
retrun false;  //已经遍历完该链表,没有环

想再写一点hashset和hashmap的区别::

HashMap是基于键值对(key-value pair)的存储结构,每个元素都有一个key和一个对应的value;HashSet是基于哈希表(hash table)的存储结构,只存储元素的值

HashMap中的key是唯一的,不允许重复;HashSet中的元素也是唯一的,不允许重复

HashMap通过使用key来访问value,所以可以通过key快速查找、插入和删除元素;HashSet只能通过元素的值来访问和操作集合,具有快速查找、插入和删除元素的特性。

(原文链接:HashMap与HashSet的区别_hashset hashmap区别-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值