【hot100】141环形链表

一、思路

题目较为简单,主要运用了HashSet存储地址以及不重复的特性

其中较为绕的是存储的是地址,那常规数值(Integer、int)的时候为什么可以判定重复,以下是文心一言的答复

问题1:HashSet如果存的是地址,那为什么可以判断两个数字的重复

在Java中,HashSet 是一个基于哈希表的数据结构,用于存储不重复的元素。HashSet 内部实际上是通过 HashMap 来实现的,其中每个元素都作为 HashMap 的一个键(key),而对应的值(value)是一个常量对象(通常是 PRESENT,一个静态的 Object 实例)。

当你将对象添加到 HashSet 时,它会调用对象的 hashCode() 方法来生成哈希码,并使用这个哈希码来确定对象在哈希表中的位置(即桶位)。如果两个对象的哈希码相同,它们会被放置在同一个桶中,但 HashSet(或 HashMap)还会通过调用对象的 equals() 方法来检查这两个对象是否真正相等。

  1. 数字类型作为对象:在Java中,基本数据类型(如 intfloatdouble 等)有对应的包装类(如 IntegerFloatDouble 等)。这些包装类都是对象,因此它们有自己的 hashCode() 和 equals() 方法。

  2. hashCode() 和 equals() 方法:对于包装类,hashCode() 方法会根据数值生成哈希码,而 equals() 方法会比较两个包装类的数值是否相等。例如,对于 Integer 对象,hashCode() 方法通常返回数值的整数哈希码(例如,通过 (x >> 16) ^ x 计算),而 equals() 方法则比较两个 Integer 对象的数值是否相同。

  3. 判断重复:当你尝试将两个数值(实际上是它们的包装类对象)添加到 HashSet 时,HashSet 会先计算它们的哈希码来确定它们应该放在哪个桶中。如果哈希码相同(对于相同数值的 Integer 对象,这通常是成立的),HashSet 接着会调用 equals() 方法来检查这两个对象是否真正相等。如果 equals() 返回 true,则 HashSet 认为这两个对象是重复的,并且不会添加第二个对象。

 问题2:如果是我自己定义的ListNode类,是直接计算地址吗,还是有默认的hashCode方法

在Java中,当你定义自己的类(比如ListNode)时,该类不会自动获得一个基于其内存地址的hashCode()方法。相反,每个类都继承自java.lang.Object,而Object类提供了一个默认的hashCode()方法。这个默认的hashCode()方法通常基于对象的内存地址(尽管这不是Java语言规范所强制要求的,但大多数JVM实现都是这样做的),但这种方法对于大多数自定义类来说并不是很有用,因为它不能保证两个“逻辑上相等”的对象会有相同的哈希码。

对于自定义类,如ListNode,如果你没有重写hashCode()方法,那么当你将该类的对象添加到基于哈希的数据结构(如HashSetHashMap)中时,将使用Object类的默认hashCode()方法。这可能导致不同的ListNode实例(即使它们包含相同的字段值)被视为不同的键,即使它们在逻辑上应该是相等的。

同样重要的是,如果你重写了equals()方法(通常是为了定义两个对象何时在逻辑上相等),你也应该重写hashCode()方法,以确保当equals()方法返回true时,hashCode()方法也为这两个对象返回相同的整数值。这是hashCode()方法的一般约定,也是Java集合框架正确工作的基础。

因此,对于你的ListNode类,如果你希望基于节点的某些字段(如值或指向的下一个节点的引用)来判断节点的相等性,并希望这些节点能够正确地存储在基于哈希的数据结构中,你需要自己重写hashCode()equals()方法。这样做可以确保当两个ListNode实例在逻辑上相等时,它们也会有相同的哈希码,并且可以被正确地识别为集合中的重复元素。

 具体就是HashSet当然是计算了哈希值后存入的底层数据结构,重点就是用什么计算的哈希值,对于链表数据结构,是通过链表的地址,而传统的数值类型则是由于包装类提供了hashCode方法,是基于数值本身

二、记忆

1.HashSet,由于其存储地址特性,可以用来区别值相同的不同结构体,如ListNode1和ListNode2的val都为1,但是其地址不同,仍然可以存进Set,若发现无法存进,那必然是碰到地址相同的结构体,即出现了重复访问(可以用来判断是否访问过、环路等)

2.HashSet可以用来判断常规数值类型是否重复,与结构体不同,数值类型计算的HashCode函数是基于数值本身而不是地址

HashSet的主要特性包括‌:

  1. 不重复性‌:HashSet中的元素是唯一的,不能有重复的元素。当尝试将一个重复的元素添加到HashSet中时,旧的元素将被保留,新元素会被忽略‌。
  2. 无序性‌:HashSet不保证元素的插入顺序,元素的顺序可能与插入顺序不同,遍历HashSet的结果是无序的‌。
  3. 允许null值‌:HashSet允许存储一个null值,但只能有一个null值‌。
  4. 基于HashMap实现‌:HashSet是基于HashMap实现的,每个元素都是HashMap的键,而值则是一个固定对象(通常是PRESENT)。元素通过哈希码进行存储,如果发生哈希冲突,HashSet使用链表或红黑树来处理‌。
  5. 非线程安全‌:HashSet不是线程安全的,如果多个线程同时访问和修改同一个HashSet实例,可能会导致不确定的结果。如果需要在多线程环境中使用HashSet,可以使用Collections.synchronizedSet()方法将其转换为线程安全的集合‌。

 三、代码

public boolean hasCycle(ListNode head){
        HashSet hashSet = new HashSet<>();
        while(head!= null){
            if (hashSet.contains(head)){
                return true;
            }else {
                hashSet.add(head);
                head = head.next;
            }
        }
        return false;
    }

HashSet的add方法会自动判断集合中是否存在,若存在则返回false,可以简化contains的判断

 public boolean hasCycle(ListNode head) {
        HashSet seen = new HashSet();
        while (head != null) {
            if (!seen.add(head)) {//HashSet的add方法会自动判断集合中是否存在,若存在则返回false,可以简化contains的判断
                return true;
            }
            head = head.next;
        }
        return false;
    }

你可能感兴趣的:(HOT100,链表,数据结构)