給定一個未排序的整數數組,找出最長連續序列的長度。
要求算法的時間複雜度爲 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;
}
};