128.最大连续序列

给定一个未排序的整数数组,找出最长连续序列的长度。

要求算法的时间复杂度为 O(n)

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

方法1:排序
时间复杂度O(nlogn),不满足要求,但能通过,比较好想
思路
从小到大排序
遍历数组,比较相邻的两项,如果相同,则跳过,继续遍历下一项
如果 当前项+1 等于 下一项,说明遇到连续项,count +1
否则,说明连续中断,将 count 重置为 1
方法1 代码

var longestConsecutive = (nums) => {
  if (nums.length === 0) return 0
  nums.sort((a, b) => a - b)
  let max = 1
  let count = 1
  for (let i = 0; i < nums.length - 1; i++) {
    let cur = i, next = i + 1
    if (nums[cur] === nums[next]) continue // 相同就跳过本次循环
    if (nums[cur] + 1 === nums[next]) { // 发现连续项 count++
      count++
    } else { // 否则,count重置1
      count = 1
    }
    max = Math.max(max, count)
  }
  return max
}


方法2:Set 的查找是 O(1)
Set 查找元素的时间复杂度是 O(1),JS 的 Set 能给数组去掉重复元素
将数组元素存入 set 中,遍历数组 nums
如果 nums[i] - 1 存在于 set ,说明 nums[i] 不是连续序列的起点,跳过,继续遍历
当前项没有“左邻居”,它就是连续序列的起点
不断在 set 中查看 cur + 1 是否存在,存在,则 count +1
cur 不再有 “右邻居” 了,就算出了一段连续序列的长度

 

也可以找到一个数后分别左右查找,并删除找到的


方法2 代码

var longestConsecutive = (nums) => {
  const set = new Set(nums) // set存放数组的全部数字
  let max = 0
  for (let i = 0; i < nums.length; i++) {
    if (!set.has(nums[i] - 1)) { // nums[i]没有左邻居,是序列的起点
      let cur = nums[i]
      let count = 1
      while (set.has(cur + 1)) { // cur有右邻居cur+1
        cur++ // 更新cur
        count++ 
      }
      max = Math.max(max, count) // cur不再有右邻居,检查count是否最大
    }
  }
  return max
}


方法3:哈希表
哈希表的value存什么
key存数字,value存什么?
新存入的数字,它找到相邻的数,它希望从邻居数那里获取什么信息
很显然它希望,左邻居告诉它左边能提供的连续长度,右邻居告诉它右边能提供的连续长度
加上它自己的长度,就有了自己处在的连续序列的长度


更新新序列的两端数字的value
同处一个连续序列的数字的value理应都相同,这是它们共同特征
但没有必要每个的value都是序列长度,只需要两端的数存序列的长度就好
因为靠的是两端和新数对接,序列是连续的,中间没有空位
序列的一端找到邻居后,将另一端对应的value更新为最新的序列长度


方法3 代码

var longestConsecutive = (nums) => {
  let map = new Map()
  let max = 0
  for (const num of nums) { // 遍历nums数组
    if (!map.has(num)) { // 重复的数字不考察,跳过
      let preLen = map.get(num - 1) || 0  // 获取左邻居所在序列的长度 
      let nextLen = map.get(num + 1) || 0 // 获取右邻居所在序列的长度 
      let curLen = preLen + 1 + nextLen   // 新序列的长度
      map.set(num, curLen) // 将自己存入 map
      max = Math.max(max, curLen) // 和 max 比较,试图刷新max
      map.set(num - preLen, curLen)  // 更新新序列的左端数字的value
      map.set(num + nextLen, curLen) // 更新新序列的右端数字的value
    }
  }
  return max
}


方法4 并查集
我不会,待补充,下方评论区已有详细补充
这位大佬有讲,可以点进去看:[LeetCode 128] Longest Consecutive Sequence [并查集][OTTFF]

class Solution {
    Map<Integer,Integer> findMap=new HashMap<>();
    Map<Integer,Integer> sizeMap=new HashMap<>();
    int max=1;
    public int longestConsecutive(int[] nums) {
        //方法2:并查集
        if (nums.length<1)
            return 0;
        for (int item:nums) {
            findMap.put(item,item);
            sizeMap.put(item,1);
        }
        for(int item:nums){
            if (findMap.containsKey(item-1)){
                union(item,item-1);
            }
        }
        return max;
    }

    public int find(int son){//带路径优化的查找
        int parent=findMap.get(son);
        if (parent!=son)
            parent = find(parent);
        findMap.put(son,parent);
        return parent;
    }

    public void union(int root1,int root2){
        int father1=find(root1);
        int father2=find(root2);
        if (father1!=father2){
            int size1=sizeMap.get(father1);
            int size2=sizeMap.get(father2);
            //按秩合并,将深度小的合并到深度大的上去
            if (size1>size2){
                findMap.put(father2,father1);
                sizeMap.put(father1,size1+size2);
            }else {
                findMap.put(father1,father2);
                sizeMap.put(father2,size1+size2);
            }
            max=Math.max(max,size1+size2);
        }
    }
}

find方法递归改迭代

public int find(int son){//带路径优化的查找
        //迭代求解
        int parent=findMap.get(son);
        while (parent!=son){
            son=parent;
            parent=findMap.get(son);
        }
        findMap.put(son,parent);
        return parent;
    }

 

 

 

作者:hyj8
链接:https://leetcode-cn.com/problems/longest-consecutive-sequence/solution/fang-fa-cong-yi-dao-nan-bing-cha-ji-fang-fa-bu-hui/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值