LeetCode hot 100 每日一题(3)--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

示例 3:

输入: nums = [1,0,1,2]
输出: 3

提示:

  • 0 <= nums.length <= 1 0 5 10^5 105
  • − 1 0 9 -10^9 −109 <= nums[i] <= 1 0 9 10^9 109

同样的,有一点看不懂这道题目,下面进入说人话环节:

给你一个乱序数组,需要找出其中连续数字的最长序列,返回这个序列的长度。

如果问连续数字是什么?请你现在就开始从1开始数数,这就是连续数字,1234就是一个连续数字序列。

解法1

java 复制代码
class Solution {
    public int longestConsecutive(int[] nums) {
        // 将数组中的每个数字加入HashSet中,方便后续快速查找
        Set<Integer> num_set = new HashSet<Integer>();
        for (int num : nums) {
            num_set.add(num);
        }

        // 初始化变量,用于记录最长连续序列的长度
        int longestStreak = 0;

        // 遍历HashSet中的每个数字
        for (int num : num_set) {
            // 如果当前数字的前一个数字不存在,说明这个数字可能是一个连续序列的起点
            if (!num_set.contains(num - 1)) {
                // 以当前数字作为连续序列的起点
                int currentNum = num;
                // 初始化当前连续序列的长度至少为1
                int currentStreak = 1;

                // 检查连续序列中是否存在下一个数字
                while (num_set.contains(currentNum + 1)) {
                    // 找到下一个数字,将当前数字更新为下一个数字
                    currentNum += 1;
                    // 当前连续序列长度增加1
                    currentStreak += 1;
                }

                // 更新全局最长连续序列长度
                longestStreak = Math.max(longestStreak, currentStreak);
            }
        }

        // 返回找到的最长连续序列的长度
        return longestStreak;
    }
}

这里是总体思路

  • 首先将数组中所有数字存入集合中,利用集合自动去重。
  • 对于每个数字,通过判断它的前一个数字是否存在来确定是否为连续序列的起点(只有序列起点才需要计算序列长度)。
  • 如果是序列起点,就通过 while 循环不断查找后续连续的数字,直到无法找到连续的数字为止,从而计算出当前序列的长度。
  • 最后比较所有连续序列的长度,返回最长的那个。

问题与解答

NOTE\] 问题1 **1.为什么要使用`HashSet`这种数据类型,而不是`HashMap`?** * 解释 在这道题中,我们只关心数字是否存在,而不需要为每个数字关联额外的数据(value)。`HashSet` 是专门用来存储不重复元素的集合,并且它的内部实现就是基于 `HashMap`,但只使用了 key,而忽略了 value。使用 HashSet 可以让代码更加简洁,并且更符合题目的需求。 1. 数据需求不同: HashSet 只存储单个元素`(key)`,而 HashMap 需要存储键值对`(key-value)`。 对于仅需要判断某个数字是否存在的场景,HashSet 更直观、语义更明确。 2. 简洁性和易用性: `HashSet` 的接口更简单,不需要处理不必要的 value 部分。使用`HashSet`代码更容易理解和维护。 3. 性能和内存: 虽然两者底层实现类似,但 `HashSet` 只保存 key,可能在内存和操作上有更小的开销。 因此,在这道题中选择 HashSet 是因为它正好满足只需要存储不重复数字、快速判断数字存在性的需求。 \[NOTE\] 问题2 **2.while循环找不到下一个数字才会退出吗?** * 解释 是的,while 循环的条件是 num_set.contains(currentNum + 1),只要集合中存在下一个数字(即 currentNum + 1),就会一直执行循环。当条件不满足,即找不到连续的下一个数字时,循环才会退出。

实例

下面通过一个例子来了解整体流程,加深印象。

输入:nums = [100,4,200,1,3,4,2]

输出:4

复制代码
            +--------------------------+
            |      输入数组              |
            | [100, 4, 200, 1, 3, 4, 2] |
            +-------------+-------------+
                          │
                          ▼
            +---------------------------+
            |       转换为 HashSet       |
            |  {1, 2, 3, 4, 100, 200}   |
            +-------------+-------------+
                          │
                          ▼
            +--------------------------+
            | 遍历 HashSet 中的每个数字 |
            +-------------+------------+
                          │
        ┌─────────────────┴─────────────────┐
        │                                   │
        ▼                                   ▼
+----------------------+         +----------------------+
|   数字 1             |         |    数字 2            |
| 判断: 1-1 = 0不存在   |         | 判断: 2-1 = 1存在      |
|   → 是序列起点       |         |   → 不是起点,跳过     |
+----------+-----------+         +----------------------+
           │
           ▼
+--------------------------+
| 从1开始扩展连续序列:      |
| 1 → 2 (存在)          |
| 2 → 3 (存在)          |
| 3 → 4 (存在)          |
| 4 → 5 (不存在)        |
| 序列:[1, 2, 3, 4]     |
| 长度 = 4               |
+----------+-------------+
           │
           ▼
+--------------------------+
| 更新最长序列长度为 4     |
+--------------------------+
                      │
                      ▼
  ┌───────────────────┴──────────────┐
  ▼                                  ▼
+------------------+        +------------------+
| 数字 100            |      | 数字 200             |
| 判断: 100-1=99不存在 |      | 判断: 200-1=199不存在|
| → 是序列起点         |      | → 是序列起点         |
| 扩展: 100 → 101不存在|      | 扩展: 200 → 201不存在|
| 序列长度 = 1         |      | 序列长度 = 1         |
+------------------+          +------------------+
           │                       │
           └───────────┬───────────┘
                       ▼
           +---------------------------+
           | 最终最长序列长度 = 4         |
           +----------------------------+

总结回顾

下面是HashSet在 Java 中的声明和基本用法示例,以及详细解释:

在 Java 中,HashSet 是一个基于哈希表(HashMap)实现的集合类,用于存储不重复的元素。​它不保证元素的顺序,即元素的插入顺序可能与迭代顺序不同。

声明 HashSet

要使用 HashSet,首先需要导入 java.util.HashSet 类。​然后,可以通过以下方式声明一个 HashSet:

java 复制代码
import java.util.HashSet;
import java.util.Set;

// 创建一个存储字符串的 HashSet
Set<String> set = new HashSet<>();

HashSet 的常用方法:

  • 添加元素:

    使用 add(E e) 方法将元素添加到集合中。如果元素已存在,添加操作不会生效,且返回 false。

    java 复制代码
    set.add("Apple");
    set.add("Banana");
    set.add("Apple"); // 重复元素,不会被添加
  • 删除元素:

    使用 remove(Object o) 方法从集合中移除指定的元素。如果元素存在且被移除,返回 true;否则,返回 false

    java 复制代码
    set.remove("Banana");
  • 检查元素是否存在:

    使用 contains(Object o) 方法判断集合中是否包含指定的元素。

    java 复制代码
    boolean exists = set.contains("Apple");
  • 获取集合大小:

    使用 size() 方法获取集合中元素的数量。

    java 复制代码
    int size = set.size();
  • 清空集合:

    使用 clear() 方法移除集合中的所有元素。

    java 复制代码
    set.clear();
  • 判断集合是否为空:

    使用 isEmpty() 方法检查集合是否为空。

    java 复制代码
    boolean isEmpty = set.isEmpty();
  • 遍历集合:

    可以使用增强型 for 循环或迭代器来遍历 HashSet 中的元素。

    java 复制代码
    // 使用增强型 for 循环
    for (String item : set) {
        System.out.println(item);
    }
    
    // 使用迭代器
    Iterator<String> iterator = set.iterator();
    
    while (iterator.hasNext()) {
     	System.out.println(iterator.next());
    }

以下是一个完整的示例,演示了 HashSet 的声明和常用方法的使用:

java 复制代码
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;

public class HashSetExample {
    public static void main(String[] args) {
        // 创建一个 HashSet
        Set<String> set = new HashSet<>();

        // 添加元素
        set.add("Apple");
        set.add("Banana");
        set.add("Cherry");
        set.add("Apple"); // 重复元素,不会被添加

        // 显示集合中的元素
        System.out.println("HashSet: " + set);

        // 检查元素是否存在
        boolean containsApple = set.contains("Apple");
        System.out.println("Contains 'Apple': " + containsApple);

        // 删除元素
        set.remove("Banana");
        System.out.println("After removing 'Banana': " + set);

        // 获取集合大小
        int size = set.size();
        System.out.println("Size of HashSet: " + size);

        // 遍历集合
        System.out.println("Iterating over HashSet:");
        for (String item : set) {
            System.out.println(item);
        }

        // 清空集合
        set.clear();
        System.out.println("After clearing, is HashSet empty? " + set.isEmpty());
    }
}

输出:

sql 复制代码
HashSet: [Apple, Banana, Cherry]
Contains 'Apple': true
After removing 'Banana': [Apple, Cherry]
Size of HashSet: 2
Iterating over HashSet:
Apple
Cherry
After clearing, is HashSet empty? true

需要注意的是,HashSet 不保证元素的迭代顺序,因此输出的顺序可能与插入顺序不同。

相关推荐
LYFlied7 分钟前
【每日算法】LeetCode 17. 电话号码的字母组合
前端·算法·leetcode·面试·职场和发展
式51633 分钟前
线性代数(八)非齐次方程组的解的结构
线性代数·算法·机器学习
Nandeska1 小时前
2、数据库的索引与底层数据结构
数据结构·数据库
橘颂TA1 小时前
【剑斩OFFER】算法的暴力美学——翻转对
算法·排序算法·结构与算法
叠叠乐1 小时前
robot_state_publisher 参数
java·前端·算法
hweiyu002 小时前
排序算法:冒泡排序
算法·排序算法
brave and determined2 小时前
CANN训练营 学习(day9)昇腾AscendC算子开发实战:从零到性能冠军
人工智能·算法·机器学习·ai·开发环境·算子开发·昇腾ai
Dave.B2 小时前
用【vtk3DLinearGridCrinkleExtractor】快速提取3D网格相交面
算法·3d·vtk
一起养小猫2 小时前
LeetCode100天Day1-字符串匹配与Z字形变换
java·leetcode
yaoh.wang2 小时前
力扣(LeetCode) 1: 两数之和 - 解法思路
python·程序人生·算法·leetcode·面试·跳槽·哈希算法