• 【LeetCode】最长连续序列 [M](数据结构设计)


    128. 最长连续序列 - 力扣(LeetCode)

    一、题目

    给定一个未排序的整数数组 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
    

    提示:

    • 0 <= nums.length <= 105
    • -109 <= nums[i] <= 109

    二、代码

    1. class Solution {
    2. public int longestConsecutive(int[] nums) {
    3. // 过滤无效参数
    4. if (nums == null || nums.length == 0) {
    5. return 0;
    6. }
    7. // 连续区间头表
    8. HashMap headMap = new HashMap<>();
    9. // 连续区间尾表
    10. HashMap tailMap = new HashMap<>();
    11. // 已经遍历到过哪些数
    12. HashSet visited = new HashSet<>();
    13. // 开始遍历 先向连续区间尾表加入新的数,然后再去想连续区间头标加新的数,最后如果这个数同时都找到了自己要加入的头表和尾表,说明这个头表和尾表也可以合并,在对两个连续区间进行合并
    14. for (int i = 0; i < nums.length; i++) {
    15. // 只取操作第一次遍历到的数,避免重复操作
    16. if (!visited.contains(nums[i])) {
    17. // 初始先将这个数加入到头表和尾表
    18. headMap.put(nums[i], 1);
    19. tailMap.put(nums[i], 1);
    20. visited.add(nums[i]);
    21. // 当前数的前一个数如果已经存在在尾表了,那么就可以将当前数加入到这个连续区间中
    22. if (tailMap.containsKey(nums[i] - 1)) {
    23. // 已存在于尾表中的尾节点key
    24. int tailKey = nums[i] - 1;
    25. // 已存在于尾表中的尾节点key代表的连续区间长度
    26. int tailLen = tailMap.get(tailKey);
    27. // 计算tailKey代表的尾表连续区间对应的头表连续区间的头节点key
    28. int headKey = tailKey - tailLen + 1;
    29. // 该连续区间在头表和为表中长度都是一样的
    30. int headLen = tailLen;
    31. // 将当前数nums[i]加入到尾表连续区间,长度扩充了1,key也向后移动了一位
    32. tailMap.put(nums[i], tailLen + 1);
    33. // 并将原有的尾表连续区间对应的key都删除
    34. tailMap.remove(tailKey);
    35. // 将原尾表连续区间对应的头表连续区间的长度也扩充1,但是头表key不变
    36. headMap.put(headKey, headLen + 1);
    37. // 移除掉在一开始加入到头表nums[i]这个键值对
    38. headMap.remove(nums[i]);
    39. }
    40. // 当前数的后一个数如果已经存在在头表了,那么就可以将当前数加入到这个连续区间中
    41. // 这一步操作还可能涉及到加入到nums[i]后,会将前面的一段连续区间和后面的一段连续区间连起来,就需要将原本两个独立的连续区间合并成一个大的连续区间
    42. if (headMap.containsKey(nums[i] + 1)) {
    43. // 当前nums[i]已经作为尾表中的key了,代表一段连续区间,这段连续区间在前面
    44. int tailKey = nums[i];
    45. // 计算前面这段连续区间的长度
    46. int preLen = tailMap.get(tailKey);
    47. // 计算前面这段连续区间对应的头表的key,用来后面的合并操作
    48. int preHead = tailKey - preLen + 1;
    49. // 记录当前这个数后一个位置,这个位置作为原有头表中连续区间的头节点
    50. int headKey = nums[i] + 1;
    51. // 计算原有头表连续区间的长度
    52. int headLen = headMap.get(headKey);
    53. // 记录原头表连续区间对应的尾表的尾节点,用来后面的合并操作
    54. int postTail = headKey + headLen - 1;
    55. // 最终就是将preHead和postTail代表的两端连续区间合并成一个大的
    56. // 将前后两端连续区间合并,这两段连续区间是因为nums[i]而连接起来的
    57. headMap.put(preHead, preLen + headLen);
    58. tailMap.put(postTail, preLen + headLen);
    59. // 移除原头表的连续区间头节点
    60. headMap.remove(headKey);
    61. // 移除掉在一开始加入到尾表nums[i]这个键值对
    62. tailMap.remove(nums[i]);
    63. }
    64. }
    65. }
    66. // 这里随便找尾表或者头表都可以,遍历找到最大长度的连续区间并返回
    67. int max = -1;
    68. for (Integer len : headMap.values()) {
    69. max = Math.max(max, len);
    70. }
    71. return max;
    72. }
    73. }

    三、解题思路 

    看这道题的数据量是0<=nums.length<=10^5,很明显这道题要求O(N),因为首先O(N^2)肯定超时,如果是O(logN)那就没必要把数据量设置的10^5这么小,还可以再设置大一点,这样设置有点浪费,所以这道题的正确解法应该是O(N)。

    每个数来的时候都自己建出自己的区间,看看跟之前连续区间能不能合并,看看后面的连续区间能不能合并,问我最后有多长的连续区间,就遍历头表或尾表, 把value最大值(value表示这个连续区间有多少个数)拿出来,这个就是最长的连续区间。

    每一个数来到的时候,对于哈希表的操作都是O(1)。

  • 相关阅读:
    鲁大师特殊股息割韭菜
    【Java课堂】接口详解
    深入剖析C++多态的实现与原理-详解
    STM32笔记之 SDRAM
    猿创征文|基于鲁棒控制理论的微电网优化调度(Matlab代码实现)
    mysql-6-主从复制搭建
    Bcachefs 文件系统驱动程序已被合并到 Linux-Next 代码树
    ChatGPT 帮你写作
    ChatGPT追祖寻宗:GPT-2论文要点解读
    程序的编译汇编和链接
  • 原文地址:https://blog.csdn.net/cy973071263/article/details/127597753