LeetCode Hot100刷题——最长连续序列

128.最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

HashSet

HashSet 是 Java 集合框架中的一个重要成员,它实现了 Set 接口,是一个无序的、不允许存储重复元素的集合。

定义

HashSet 是基于哈希表(实际上是一个 HashMap 实例)实现的 Set 接口的实现类,它继承自 AbstractSet 类并实现了 SetCloneable 和 Serializable 接口。其定义形式如下:

import java.util.HashSet;
HashSet<E> set = new HashSet<>(); // E 表示集合中元素的类型

特点

  1. 不允许重复元素这是 HashSet 最显著的特点之一。当向 HashSet 中添加元素时,它会使用元素的哈希码(hashCode)和 equals 方法来判断元素是否已经存在。如果两个元素的哈希码相同且 equals 方法返回 true,则认为这两个元素是重复的,不会将重复元素添加到集合中。
  2. 无序性HashSet 中的元素没有固定的顺序,不能保证元素的插入顺序和取出顺序一致。这是因为它是基于哈希表实现的,元素在集合中的存储位置是根据其哈希码计算得出的。
  3. 允许存储 null 元素HashSet 允许存储一个 null 元素。由于 null 没有哈希码,HashSet 对其做了特殊处理,只允许存储一个 null

实现原理

HashSet 内部是通过 HashMap 来实现的。当向 HashSet 中添加元素时,实际上是将元素作为 HashMap 的键来存储,而 HashMap 的值则是一个固定的对象(PRESENT,是 HashSet 内部定义的一个静态常量)。在判断元素是否重复时,先通过元素的 hashCode 方法计算哈希值,确定其在哈希表中的位置,然后再通过 equals 方法比较元素内容是否相同

常见方法

  • add(E e)向集合中添加一个元素,如果元素已存在则不添加,并返回 false;否则返回 true
  • remove(Object o)从集合中移除指定的元素,如果集合中包含该元素则返回 true;否则返回 false
  • contains(Object o)判断集合中是否包含指定的元素,如果包含则返回 true;否则返回 false
  • size()返回集合中元素的数量。
  • isEmpty()判断集合是否为空,如果为空则返回 true;否则返回 false

解题思路

        使用哈希集合(HashSet)来快速查找元素是否存在。可以将数组中的所有元素存入HashSet中,然后对于每个可能的起点,比如某个数的前一个数不在集合中,那么这个数可能是序列的起点,然后从这个数开始不断检查+1的数是否存在,统计长度。这样,每个数最多被访问两次(一次作为起点,一次在其他数的序列中被检查),时间复杂度是O(n)。

        比如,示例1中的数组 nums=[100,4,200,1,3,2] 存入HashSet后,遍历每个元素。比如当前数是100,检查99是否存在,不存在,所以以100为起点,向后找101是否存在,不存在,所以序列长度是1。接着是4,检查3是否存在,存在的话,说明4可能不是起点,跳过。对于200同理。而遇到1时,检查0不存在,所以作为起点,找2是否存在,存在,3也存在,4也存在,直到找不到下一个,此时序列长度是4。这样就能找到最长的长度。

具体步骤

1.将所有元素存入HashSet,以便O(1)时间查找是否存在。(创建 HashSet,将所有nums元素存入)

2.遍历集合中的每个元素,对于每个元素num,检查num - 1是否存在于集合中。如果不存在,说明num可能是一个连续序列的起点。

3.如果是起点,则开始循环检查num+1,num+2...是否存在,直到找不到下一个数为止,统计当前序列的长度。

4.记录最大的序列长度。

        接下来需要考虑如何实现这个逻辑。例如,如何避免重复处理元素。比如,当某个元素已经被包含在之前的某个序列中时,不需要再处理它,因为作为非起点的元素,其所在的序列已经被处理过了。比如,当处理元素4的时候,发现3存在,那么4所在的序列起点应该是3或者更小,所以当处理到3的时候才会作为起点处理,此时4会被包括进去。所以,对于非起点的元素,可以跳过处理。

代码实现

具体代码步骤:

1. 创建HashSet,并添加所有元素。

2. 初始化maxStreak为0。

3. 遍历数组中的每个数num:

a. 如果集合中不包含num-1,则开始检查连续序列。

b. 当前数为currentNum = num,currentStreak =1。

c. 循环:当集合中包含currentNum+1时,currentNum+1,currentStreak+1。

d. 比较currentStreak和maxStreak,取较大的。

4. 返回maxStreak。

class Solution {
    public int longestConsecutive(int[] nums) {
        Set<Integer> numSet = new HashSet<>();
        for(int i = 0; i < nums.length; i++){
            numSet.add(nums[i]);
        }

        int maxStreak = 0;

        //遍历集合而非原数组,避免重复元素多次判断
        for(int num : numSet){
            if(!numSet.contains(num - 1)){ //仅当当前数是连续序列的起点时处理
                int currentNum = num;
                int currentStreak = 1;

                while(numSet.contains(currentNum + 1)){
                    currentNum++;
                    currentStreak++;
                }
                maxStreak = Math.max(maxStreak,currentStreak);
            }
        }
        return maxStreak;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值