BM6 判断链表中是否有环——Java Set集合&&hashSet哈希表应用

题目链接:判定链表是否有环


题目

在这里插入图片描述


解决思路

遍历链表的每一个节点,记录下来,若再次遇到了遍历过的节点,就可以判定存在环。

  • 遍历链表,把访问过的节点存储在哈希表中。
  • 判定节点是否存在哈希表中,若存在则返回true。
  • 遍历结束,则返回false。

关于Java的Set集合

Set继承于Collection接口,是一个不允许出现重复元素,而且无需的集合,主要有HashSetTreeSet两大类。
判定重复元素时候,Set集合会调用hashCodeequals方法实现。

HashSet是哈希表结构,主要利用HashMap的key来存储元素,计算插入元素的hashCode来获取元素在集合中的位置。

TreeSet是红黑树结构,每一个元素都是树中的一个节点,插入的元素都会进行排序。
在这里插入图片描述

Set常用方法

和List不同,Set提供了equals和hashCode方法,可以让子类重写,实现对集合插入重复元素的处理。

public interface Set<E> extends Collection<E> {

    A:添加功能
    boolean add(E e);
    boolean addAll(Collection<? extends E> c);

    B:删除功能
    boolean remove(Object o);
    boolean removeAll(Collection<?> c);
    void clear();

    C:长度功能
    int size();

    D:判断功能
    boolean isEmpty();
    boolean contains(Object o);
    boolean containsAll(Collection<?> c);
    boolean retainAll(Collection<?> c); 

    E:获取Set集合的迭代器:
    Iterator<E> iterator();

    F:把集合转换成数组
    Object[] toArray();
    <T> T[] toArray(T[] a);
    
    //判断元素是否重复,为子类提高重写方法
    boolean equals(Object o);
    int hashCode();
}

HashSet

实现了Set接口,底层是HashMap实现,为哈希表结构,新增元素相当于HashMap的key,value默认是一个固定的Object,HashSet类似一个阉割的HashMap。

当有元素插入的时候,会计算元素的hashCode值,将元素插入到哈希表对应的位置中来。

它继承于AbstractSet,实现了Set, Cloneable, Serializable接口

  1. HashSet继承AbstractSet类,获得了Set接口大部分的实现,减少了实现此接口所需的工作,实际上是又继承了AbstractCollection类;

  2. HashSet实现了Set接口,获取Set接口的方法,可以自定义具体实现,也可以继承AbstractSet类中的实现;

  3. HashSet实现Cloneable,得到了clone()方法,可以实现克隆功能;

  4. HashSet实现Serializable,表示可以被序列化,通过序列化去传输,典型的应用就是hessian协议。

因此具有如下特征:

  • 不允许出现重复元素。
  • 可以插入null数值。
  • 元素无序,添加顺序和遍历顺序不一样。
  • 线程不安全,若多个线程同时操作HashSet,则必须利用代码实现同步。

HashSet基本操作

底层是HashMap实现,插入元素当作是key和value。提供hashCode来确定集合中的位置,由于Set集合没有下标的概念,因此没有像List一样提供get方法。若要获取HashSet某个元素,只能够通过遍历形式进行equals方式比较。

public class HashSetTest {
    public static void main(String[] agrs){
        //创建HashSet集合:
        Set<String> hashSet = new HashSet<String>();
        System.out.println("HashSet初始容量大小:"+hashSet.size());

        //元素添加:
        hashSet.add("my");
        hashSet.add("name");
        hashSet.add("is");
        hashSet.add("jiaboyan");
        hashSet.add(",");
        hashSet.add("hello");
        hashSet.add("world");
        hashSet.add("!");
        System.out.println("HashSet容量大小:"+hashSet.size());

        //迭代器遍历:
        Iterator<String> iterator = hashSet.iterator();
        while (iterator.hasNext()){
            String str = iterator.next();
            System.out.println(str);
        }
        //增强for循环
        for(String str:hashSet){
            if("jiaboyan".equals(str)){
                System.out.println("你就是我想要的元素:"+str);
            }
            System.out.println(str);
        }

        //元素删除:
        hashSet.remove("jiaboyan");
        System.out.println("HashSet元素大小:" + hashSet.size());
        hashSet.clear();
        System.out.println("HashSet元素大小:" + hashSet.size());

        //集合判断:
        boolean isEmpty = hashSet.isEmpty();
        System.out.println("HashSet是否为空:" + isEmpty);
        boolean isContains = hashSet.contains("hello");
        System.out.println("HashSet是否为空:" + isContains);
    }
}

在这里插入图片描述

HashSet添加元素

Set集合不允许添加重复元素,请看如下代码:

public class HashSetTest2 {

    public static void main(String[] agrs){
        //hashCode() 和 equals()测试:
        hashCodeAndEquals();
    }
    public static void hashCodeAndEquals(){
        //第一个 Set集合:
        Set<String> set1 = new HashSet<String>();
        String str1 = new String("jiaboyan");
        String str2 = new String("jiaboyan");
        set1.add(str1);
        set1.add(str2);
        System.out.println("长度:"+set1.size()+",内容为:"+set1);

        //第二个 Set集合:
        Set<App> set2 = new HashSet<App>();
        App app1 = new App();
        app1.setName("jiaboyan");

        App app2 = new App();
        app2.setName("jiaboyan");

        set2.add(app1);
        set2.add(app2);
        System.out.println("长度:"+set2.size()+",内容为:"+set2);

        //第三个 Set集合:
        Set<App> set3 = new HashSet<App>();
        App app3 = new App();
        app3.setName("jiaboyan");
        set3.add(app3);
        set3.add(app3);
        System.out.println("长度:"+set3.size()+",内容为:"+set3);
    }
}

在这里插入图片描述

查看HashSet的add方法:

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

在底层HashSet调用了HashMap的put(key,value)方法:

public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

int hash = hash(key)对传入的key计算哈希数值;
int i = indexFor(hash, table.length)对哈希数值转换,转换为数组的索引index,这是因为HashMap底层存储使用了Entry<K ,V>[]数组。
for (Entry<K,V> e = table[i]; e != null; e = e.next) 判断对应index下是否存在元素;
如果存在,则if(e.hash == hash && ((k = e.key) == key || key.equals(k)))判断;
如果不存在,则addEntry(hash, key, value, i)直接添加。


解题代码

import java.util.*;
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode pos = head;
        //哈希表记录访问过的结点
        Set<ListNode> visited = new HashSet<>();
        while (pos != null) {
            //判定节点是否被访问过
            if (visited.contains(pos)) {
                return true;
            } else {
                //节点记录到哈希表中
                visited.add(pos);
            }
            //遍历哈希表
            pos = pos.next;
        }
        return false;
    }
}

运行结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值