leetcode 128. 最长连续序列
常规qsort暴力解法
- 常规想法就是使用先排序,然后遍历的思想。时间复杂度为 nlogn + n
int MyCmp(const void *a, const void *b)
{
int left = *(int *)a;
int right = *(int *)b;
return left < right ? -1 : 1;
}
int longestConsecutive(int* nums, int numsSize)
{
if (numsSize == 0) {
return 0;
}
int maxlen = 1;
qsort(nums, numsSize, sizeof(int), MyCmp);
int pre = nums[0];
int cnt = 1;
for (int i = 1; i < numsSize; i++) {
if (nums[i] == pre) {
continue;
} else if (nums[i] == pre + 1) {
cnt++;
} else {
cnt = 1;
}
maxlen = fmax(maxlen, cnt);
pre = nums[i];
}
return maxlen;
}
使用hash表,精简O(n)算法
- 题目要求O(n)时间复杂度,所以需要想办法优化,用空间换时间
- 那么就按照hash表保存数组中所有元素,然后逐个遍历所有的元素。且从序列的最小的数字开始数有多少个连续
typedef struct {
int num;
int index;
UT_hash_handle hh;
} MyHash;
MyHash* g_head = NULL;
int GetNumsIndex(int num)
{
MyHash* tmp = NULL;
HASH_FIND_INT(g_head, &num, tmp);
if (tmp == NULL) {
return -1;
} else {
return tmp->index;
}
}
void AddNumIndex(int num, int index)
{
if (GetNumsIndex(num) != -1) return;
MyHash* tmp = (MyHash *)malloc(sizeof(MyHash));
tmp->num = num;
tmp->index = index;
HASH_ADD_INT(g_head, num, tmp);
}
int longestConsecutive(int* nums, int numsSize)
{
g_head = NULL;
if (numsSize == 0) {
return 0;
}
for (int i = 0; i < numsSize; i++) {
AddNumIndex(nums[i], i);
}
int maxlen = 1;
// 时间复杂度O(n)
for (int i = 0; i < numsSize; i++) {
if (GetNumsIndex(nums[i] - 1) != -1) {
continue;
}
int tmp = nums[i];
int len = 0;
while (GetNumsIndex(tmp) != -1) {
tmp++;
len++;
}
maxlen = fmax(maxlen, len);
}
return maxlen;
}
并查集算法
- 使用常规的解法后,再尝试一下使用并查集的方式,这种方式一开始是怎么也想不起来的。
- 也算是拓宽了自己的思路
- 大概的思路就是,遍历数组中每一个数值nums[i],然后尝试将nums[i] 和 nums[i] + 1合并(前提是nums[i] + 1也在数组中)
- 一开始每一个数字的parent就是自己。合并的时候将根据两个数值的parent的子节点个数的多少来裁决怎么合并,由谁来做新的parent根节点。
- 还有一个非常重要的数据结构,保存了每一个数字作为parent根时,该根下面有多少个子节点,非常关键。也是本题使用并查集的重要思路。
- 这样在合并的时候,就可以把根节点的子节点个数累加。在累加的过程中。比较记录最大的子节点个数。
- 本题使用c语言,无奈没有map,只能使用ut hash来处理了。代码稍显繁琐。
typedef struct {
int num;
int parent;
UT_hash_handle hh;
} Parent;
typedef struct {
int parent;
int sonNum;
UT_hash_handle hh;
} Son;
Parent* g_parent = NULL;
Son* g_sonNums = NULL;
int g_maxlen = 1;
Son* GetParentSon(int parent)
{
Son* tmp = NULL;
HASH_FIND_INT(g_sonNums, &parent, tmp);
return tmp;
}
void InitParentSon(int num)
{
Son* tmp = (Son *)malloc(sizeof(Son));
tmp->parent = num;
tmp->sonNum = 1;
HASH_ADD_INT(g_sonNums, parent, tmp);
}
bool NumIsExist(num) {
Parent* tmp = NULL;
HASH_FIND_INT(g_parent, &num, tmp);
if (tmp == NULL) {
return false;
} else {
return true;
}
}
int GetNumParent(int num)
{
Parent* tmp = NULL;
HASH_FIND_INT(g_parent, &num, tmp);
return tmp->parent;
}
void InitNumParent(int num)
{
Parent* tmp = (Parent *)malloc(sizeof(Parent));
tmp->num = num;
tmp->parent = num;
HASH_ADD_INT(g_parent, num, tmp);
}
int Find(int num)
{
int parent = GetNumParent(num);
if (parent == num) {
return num;
}
Parent* tmp = NULL;
HASH_FIND_INT(g_parent, &num, tmp);
tmp->parent = Find(parent);
return tmp->parent;
}
void Union(int a, int b)
{
int parent_a = Find(a);
int parent_b = Find(b);
if (parent_a == parent_b) {
return;
}
Son* son_a = GetParentSon(parent_a);
Son* son_b = GetParentSon(parent_b);
if (son_a->sonNum > son_b->sonNum) {
son_a->sonNum += son_b->sonNum;
Parent* ParentB = NULL;
HASH_FIND_INT(g_parent, &parent_b, ParentB);
ParentB->parent = parent_a;
g_maxlen = fmax(g_maxlen, son_a->sonNum);
} else {
son_b->sonNum += son_a->sonNum;
Parent* ParentA = NULL;
HASH_FIND_INT(g_parent, &parent_a, ParentA);
ParentA->parent = parent_b;
g_maxlen = fmax(g_maxlen, son_b->sonNum);
}
}
int longestConsecutive(int* nums, int numsSize)
{
g_parent = NULL;
g_sonNums = NULL;
if (numsSize == 0) {
return 0;
}
g_maxlen = 1;
for (int i = 0; i < numsSize; i++) {
if (NumIsExist(nums[i]) == true) continue;
InitNumParent(nums[i]); // 初始化各个元素的parent是自己
InitParentSon(nums[i]);
}
for (int i = 0; i < numsSize; i++) {
if (NumIsExist(nums[i] + 1) == false) continue;
// 开始合并,并在合并中更新最大的连续序列的长度
Union(nums[i], nums[i] + 1);
}
// 清理ut hash 表
return g_maxlen;
}