给定一个未排序的整数数组,找出最长连续序列的长度。
要求算法的时间复杂度为 O(n)。
示例:
输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。
中等难度题吧,不算hard题,需要了解并查集用法
将连续的数字进行合并成一个集合,最后看所有集合中哪个集合数目最多(或者直接边合并边记录最大值)
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <set>
#include <map>
#include <unordered_set>
#include <iostream>
using namespace std;
class Solution {
//并查集模板(已优化)
struct DisJointSet
{
vector<int> _id;//元素
vector<int> _size;//集合内的元素个数
int max_size;//最大集合的元素个数【额外需要用到的参数】
int _count;//集合总个数
DisJointSet(int Num)
{
for (int i = 0; i < Num; i++)
{
_id.emplace_back(i);
_size.emplace_back(1);
}
_count = Num;
max_size = 1;
}
//查找
int find_(int p)
{
while (p != _id[p])
{
_id[p] = _id[_id[p]];
p = _id[p];
}
return p;
}
//合并
void _union(int p, int q) {
int i = find_(p);
int j = find_(q);
if (i == j)return;
if (_size[i] > _size[j])
{
_id[j] = i;
_size[i] += _size[j];
max_size = max(max_size, _size[i]);
}
else
{
_id[i] = j;
_size[j] += _size[i];
max_size = max(max_size, _size[j]);
}
_count--;
}
};
public:
int longestConsecutive(vector<int>& nums) {
if (nums.size() == 0)return 0;
DisJointSet disJointSet(nums.size());
unordered_set<int> nums_set;//记录是否有查复数字 或者 是否已经有数字的前后的数
unordered_map<int, int> nums_disJointSetID_map;//<数字,ID> 与并查集ID一一对应,不需改动模板参数
for (int i = 0; i < nums.size(); i++)
{
if (nums_set.find(nums[i]) != nums_set.end())continue;
nums_set.insert(nums[i]);
nums_disJointSetID_map[nums[i]] = i;
if(nums_set.find(nums[i] - 1) != nums_set.end())//是否有前一个数
{
disJointSet._union(nums_disJointSetID_map[nums[i]], nums_disJointSetID_map[nums[i] - 1]);
}
if(nums_set.find(nums[i] + 1) != nums_set.end())//是否有后一个数
{
disJointSet._union(nums_disJointSetID_map[nums[i]], nums_disJointSetID_map[nums[i] + 1]);
}
}
return disJointSet.max_size;
}
};
int main()
{
Solution Solution;
vector<int> nums{ 100,4,200,1,3,2 };
cout << Solution.longestConsecutive(nums) << endl;
}
网上另一种解法,使用HashMap进行存 nums[i] 值和其对应时刻下最长连续序列的长度。
通过搜寻当下nums[i]的左右临边值是否存在,从而在HashMap中进行修改 当前 子序列的两边界的value值(当前子序列的长度),方便后续的nuns[i]值进行搜索左右临边值,可以直接用(因为遇到相同的值,即HashMap中已经存在的,不需要再判断了,而且也不需要实时更新value值,只是需要更新当前子序列两端value值即可)
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
if(nums.empty()) return 0;
unordered_map<int, int> record;
int max_count = 0;
for(int i=0; i<nums.size(); i++)
{
//已经判断过这个数,则不需要再判断第二遍
if(record.find(nums[i]) != record.end()) continue;
//当前数nums[i]的左右两边的数,对应的最长连续序列的个数(有可能没有左右两边的数,没有的话,个数就是0)
int left = 0;
int right = 0;
if(record.find(nums[i]-1) != record.end())
left = record[nums[i]-1];
if(record.find(nums[i]+1) != record.end())
right = record[nums[i]+1];
int count = 1+left+right;
//保存nums[i]在当前时刻的 最大连续序列的个数
record[nums[i]] = count;
//更修nums[i] 最远的 左右两边的数的 最大连续序列的个数
if(left) record[nums[i]-left] = count;
if(right) record[nums[i]+right] = count;
//实时判断修改最大值
if(max_count < count)
max_count = count;
}
return max_count;
}
};