最长连续序列
· 阅读需 8 分钟
力扣面试经典——128题
💡 参考代码:
- AI
- OTHER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 哈希表节点结构
typedef struct HashNode {
int key;
int value; // 用于标记是否已访问
UT_hash_handle hh; // uthash所需句柄
} HashNode;
/**
* 寻找最长连续序列长度
* @param nums 整数数组
* @param numsSize 数组长度
* @return 最长连续序列的长度
*/
int longestConsecutive(int* nums, int numsSize) {
// 边界条件处理:空数组直接返回0
if (numsSize == 0) {
return 0;
}
HashNode* hashTable = NULL; // 初始化哈希表
HashNode* tmp;
// 第一步:将所有元素存入哈希表,去重并建立快速查找结构
for (int i = 0; i < numsSize; i++) {
// 查找当前元素是否已存在于哈希表中
HASH_FIND_INT(hashTable, &nums[i], tmp);
if (tmp == NULL) {
// 若不存在,则创建新节点并插入哈希表
tmp = (HashNode*)malloc(sizeof(HashNode));
tmp->key = nums[i];
tmp->value = 0; // 0表示未访问过
HASH_ADD_INT(hashTable, key, tmp);
}
}
int maxLength = 0; // 记录最长连续序列长度
// 遍历哈希表中的每个元素
HashNode* current;
HashNode* tmpIter;
HASH_ITER(hh, hashTable, current, tmpIter) {
// 只有当current->key-1不存在时,current->key才是某个连续序列的起点
HASH_FIND_INT(hashTable, &(current->key - 1), tmp);
if (tmp == NULL) {
// 找到序列起点,从该点开始向后查找连续元素
int currentNum = current->key;
int currentLength = 1;
// 向后查找连续元素
HASH_FIND_INT(hashTable, &(currentNum + 1), tmp);
while (tmp != NULL) {
tmp->value = 1; // 标记为已访问
currentLength++;
currentNum++;
HASH_FIND_INT(hashTable, &(currentNum + 1), tmp);
}
// 更新最大长度
if (currentLength > maxLength) {
maxLength = currentLength;
}
}
}
// 清理哈希表内存
HashNode* el;
HashNode* tmpEl;
HASH_ITER(hh, hashTable, el, tmpEl) {
HASH_DEL(hashTable, el);
free(el);
}
return maxLength;
}
// 测试函数
int main() {
// 测试用例1
int nums1[] = {100, 4, 200, 1, 3, 2};
int size1 = sizeof(nums1)/sizeof(nums1[0]);
printf("Test 1: %d\n", longestConsecutive(nums1, size1)); // 预期输出: 4
// 测试用例2
int nums2[] = {0, 3, 7, 2, 5, 8, 4, 6, 0, 1};
int size2 = sizeof(nums2)/sizeof(nums2[0]);
printf("Test 2: %d\n", longestConsecutive(nums2, size2)); // 预期输出: 9
// 测试用例3
int nums3[] = {1, 0, 1, 2};
int size3 = sizeof(nums3)/sizeof(nums3[0]);
printf("Test 3: %d\n", longestConsecutive(nums3, size3)); // 预期输出: 3
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdbool.h>
#define EMPTY INT_MAX // 哈希表空位标记
// ====================== 哈希表实现 ======================
typedef struct {
int *keys;
int size;
} HashSet;
HashSet* createHashSet(int capacity) {
HashSet* set = (HashSet*)malloc(sizeof(HashSet));
set->size = capacity * 2;
set->keys = (int*)malloc(sizeof(int) * set->size);
for (int i = 0; i < set->size; i++) set->keys[i] = EMPTY;
return set;
}
unsigned int hash(HashSet* set, int key) {
return ((unsigned int)key) % set->size;
}
void insert(HashSet* set, int key) {
unsigned int idx = hash(set, key);
while (set->keys[idx] != EMPTY && set->keys[idx] != key) {
idx = (idx + 1) % set->size;
}
set->keys[idx] = key;
}
bool contains(HashSet* set, int key) {
unsigned int idx = hash(set, key);
int start = idx;
while (set->keys[idx] != EMPTY) {
if (set->keys[idx] == key) return true;
idx = (idx + 1) % set->size;
if (idx == start) break;
}
return false;
}
void freeHashSet(HashSet* set) {
free(set->keys);
free(set);
}
// ====================== 主逻辑 ======================
int longestConsecutive(int* nums, int n) {
if (n == 0) return 0;
int mn = INT_MAX, mx = INT_MIN;
for (int i = 0; i < n; i++) {
if (nums[i] < mn) mn = nums[i];
if (nums[i] > mx) mx = nums[i];
}
long long range = (long long)mx - (long long)mn + 1;
// ---------- 小范围:用位图 ----------
if (range <= 10000000) { // 1000万可接受
char *bitmap = (char*)calloc(range, sizeof(char));
for (int i = 0; i < n; i++) {
bitmap[nums[i] - mn] = 1;
}
int longest = 0, curr = 0;
for (long long i = 0; i < range; i++) {
if (bitmap[i]) {
curr++;
if (curr > longest) longest = curr;
} else {
curr = 0;
}
}
free(bitmap);
return longest;
}
// ---------- 大范围:用哈希表 ----------
HashSet* set = createHashSet(n);
for (int i = 0; i < n; i++) insert(set, nums[i]);
int longest = 0;
for (int i = 0; i < n; i++) {
int num = nums[i];
if (!contains(set, num - 1)) { // 只从起点扩展
int curr = num, len = 1;
while (contains(set, curr + 1)) {
curr++;
len++;
}
if (len > longest) longest = len;
}
}
freeHashSet(set);
return longest;
}
// ====================== 测试 ======================
// int main() {
// int nums1[] = {100, 4, 200, 1, 3, 2};
// int n1 = sizeof(nums1) / sizeof(nums1[0]);
// printf("%d\n", longestConsecutive(nums1, n1)); // 4
// int nums2[] = {0,1,2,4,8,5,6,7,9,3,55,88,77,99,999999999};
// int n2 = sizeof(nums2) / sizeof(nums2[0]);
// printf("%d\n", longestConsecutive(nums2, n2)); // 10 ✅
// return 0;
// }
📖 总结:
点击展开题目总结
🤔 最长连续序列
1️⃣ 题目核心信息
🎯 功能描述:给定一个未排序的整数数组,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
📥 输入输出:
- 输入:
nums: 整数数组,包含待处理的数字序列numsSize: 数组长度,表示数组中元素的个数
- 输出:返回最长连续数字序列的长度
2️⃣ 实现原理
💡 核心思路:使用哈希表存储所有数字,然后只从连续序列的起点开始计数,避免重复计算。
📋 实现步骤:
- 遍历数组,将所有元素存入哈希表实现去重和快速查找
- 再次遍历哈希表中的元素,判断每个元素是否为连续序列的起点
- 对于每个起点,向后查找连续元素并计算序列长度
- 记录并更新最长序列长度
3️⃣ 关键点解析
🎯 代码技巧
- 使用UTHASH库实现哈希表功能,提供O(1)的查找效率
- 通过检查
n-1是否存在来确定n是否为序列起点,避免重复计算 - 只对序列起点进行扩展搜索,确保每个元素最多被访问两次
- 使用
HASH_ITER安全地遍历和删除哈希表元素
4️⃣ 使用场景
✅ 适用情况:
- 需要在一个未排序数组中查找最长连续数字序列
- 数据范围较大,不能使用排序算法
- 需要O(n)时间复杂度的解决方案
⚠️ 前提条件:
- 数组元素为整数
- 可以使用额外的O(n)空间
- 允许修改哈希表中的元素状态
5️⃣ 复杂度分析
- ⏱️ 时间复杂度:O(n) - 每个元素最多被访问两次
- 💾 空间复杂度:O(n) - 需要哈希表存储所有元素
6️⃣ 注意事项
🚩 边界情况:
- 空数组:直接返回0
- 单个元素数组:返回1
- 包含重复元素:哈希表自动去重处理
💥 易错点:
- 不能对表达式取地址(如
&(current->key - 1)),需要先计算值再取地址 - 忘记释放哈希表内存导致内存泄漏
- 没有正确判断序列起点导致重复计算
7️⃣ 补充说明
以数组 [100, 4, 200, 1, 3, 2]为例:
步骤1:构建哈希表
首先将数组中所有元素存入哈希表:
哈希表内容:{100, 4, 200, 1, 3, 2}
步骤2:遍历哈希表寻找序列起点
遍历哈希表中的每个元素,判断是否为序列起点:
-
检查元素100:
- 判断99是否存在 → 不存在
- 所以100是序列起点
- 从100开始向后查找:100 → 无后续元素
- 序列长度:1
-
检查元素4:
- 判断3是否存在 → 存在
- 所以4不是序列起点,跳过
-
检查元素200:
- 判断199是否存在 → 不存在
- 所以200是序列起点
- 从200开始向后查找:200 → 无后续元素
- 序列长度:1
-
检查元素1:
- 判断0是否存在 → 不存在
- 所以1是序列起点
- 从1开始向后查找:
- 1 → 存在2
- 2 → 存在3
- 3 → 存在4
- 4 → 无后续元素
- 序列长度:4 ([1,2,3,4])
-
检查元素3:
- 判断2是否存在 → 存在
- 所以3不是序列起点,跳过
-
检查元素2:
- 判断1是否存在 → 存在
- 所以2不是序列起点,跳过
步骤3:得出结果
比较所有序列长度,最长的序列是[1,2,3,4],长度为4。
算法优化点
- 避免重复计算:只从序列起点开始查找,避免了对[2,3,4]、[3,4]等子序列的重复计算
- 高效查找:使用哈希表实现O(1)时间复杂度的元素查找
- 线性时间复杂度:虽然有嵌套循环,但每个元素最多被访问两次,总体时间复杂度为O(n)
通过这种方式,算法能够在线性时间内找出最长连续序列,而不需要先对数组进行排序。
加载评论中...
