代码训练LeetCode(5)最长连续序列

这篇具有很好参考价值的文章主要介绍了代码训练LeetCode(5)最长连续序列。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

代码训练(5)LeetCode之最长连续序列

Author: Once Day Date: 2024年3月9日

漫漫长路,才刚刚开始…

全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客

参考文章:

  • 128. 最长连续序列 - 力扣(LeetCode)
  • 力扣 (LeetCode) 全球极客挚爱的技术成长平台
1. 原题

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

例如,nums = [4, 2, 4, 3, 5],其最长连续数字序列为2, 3, 4, 5,长度为4。

2. 分析

这个问题是在寻找最长连续序列的长度,但不要求这些序列在原数组中是连续的。要在 O(n) 时间复杂度内解决这个问题,我们可以利用哈希表(在 C 中通常使用散列表或字典类型的数据结构)。

解题思路如下:

  1. 使用哈希表来存储每个数字是否已经被访问过,这可以用来避免重复计算。
  2. 遍历数组中每个数字:
    • 如果该数字已在哈希表中,跳过它(因为它已经被计算过了)。
    • 如果该数字不在哈希表中,检查数字前后是否有连续的数字,即检查 num-1num+1 是否在数组中。
    • 如果有,继续向前后检测,直到找不到连续数字为止,同时更新哈希表。
    • 在此过程中,计算并更新最长连续序列的长度。

实例处理时,可以按照下面步骤:

  1. 创建一个哈希表 hash
  2. 遍历 nums,将每个元素作为键存入 hash,值为 false 表示该元素还未被访问。
  3. 再次遍历nums,对于每个元素,若其值为false,则进行以下操作:
    • 初始化当前长度为 1。
    • 向上查找 num + 1, num + 2, ...,直到找不到为止,每找到一个数,长度加 1。
    • 向下查找 num - 1, num - 2, ...,直到找不到为止,每找到一个数,长度加 1。
    • 更新结果为当前长度和已有结果的较大值。
  4. 返回最长连续序列的长度。

需要注意,其实这里把Hash表过于理想化了,实际上Hash表查询时间并非一定是O(1)。这个地方先存入nums的原因,是为了减少遍历次数,最差的情况下,是每个元素都查询一次,这样就是n次查询。

查询时判断是否n-1存在,就是判断当前这个值是否是一个序列的开始,这样对于长序列,也只有一个序列的开始值会完整查询所有值,这又是n次,所以整体复杂度维持在3n左右。

假设 nums = [100, 4, 200, 1, 3, 2]

  • 创建哈希表 hash,存入所有数字。
  • 遍历 nums,当我们到达 1 时,查找 0(不在 hash 中),然后查找 2(在 hash 中,更新长度),继续查找 34,到 5 时停止。序列长度为 4。
  • 以此类推,我们找到最长的序列长度为 4。

性能优化关键点:

  • 使用哈希表来达到平均 O(1) 时间复杂度的查询。
  • 只在数字为序列起点时(即 num-1 不在数组中时)进行序列长度计算,以避免重复计算。
3. 代码实现
3.1 排序实现
int cmp(const void *a, const void *b) 
{
    return *(int *)a < *(int *)b;
}

int longestConsecutive(int* nums, int numsSize) {
    int count, last, max_count = 0;

    if (numsSize == 0) {
        return 0;
    }

    qsort(nums, numsSize, sizeof(int), cmp);
    last = nums[0] - 1;
    count = 1;
    for(int i = 1; i < numsSize; i++) {
        if (last == nums[i]) {
            count++;
            last--;
        } else if (!(last + 1 == nums[i])) {
            
            if (count > max_count) {
                max_count = count;
            }
            count = 1;
            last = nums[i] - 1;
        }
    }
    
    if (count > max_count) {
        max_count = count;
    }

    return max_count;
}

这段代码实现了寻找最长连续序列的功能。主要逻辑如下:

  1. cmp函数是用于qsort的比较函数,比较两个整数的大小。
  2. longestConsecutive函数接收一个整数数组nums和数组大小numsSize,返回最长连续序列的长度。
  3. 首先判断数组是否为空,如果为空则返回0。
  4. 使用qsort函数对数组进行升序排序。
  5. 初始化变量last为第一个元素减1,count为1,表示当前连续序列的长度。
  6. 遍历排序后的数组,从第二个元素开始:
    • 如果当前元素与last相等,说明是重复元素,count加1,last减1。
    • 如果当前元素不等于last+1,说明连续序列中断,更新max_countcountmax_count的最大值,重置count为1,更新last为当前元素减1。
    • 如果当前元素等于last+1,说明连续序列继续,不需要进行操作。
  7. 遍历结束后,再次比较countmax_count,更新max_count
  8. 返回max_count,即最长连续序列的长度。

这个算法的时间复杂度是O(n log n),主要是排序的时间复杂度。空间复杂度是O(1)。在正常的情况下,排序做法其实也不错,比较节约内存

测试结果如下(仅供参考):

代码训练LeetCode(5)最长连续序列,CS小白之路,#  十年代码训练,# C语言,leetcode,算法,c语言

3.2 Hash表实现
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#define HASH_SIZE 65536

typedef struct hash_node {
    bool              flag;
    int               value;
    struct hash_node *next;
} hash_node;

hash_node *hash_table[HASH_SIZE];

static inline int hash(int value)
{
    return value & (HASH_SIZE - 1);
}

hash_node hash_table_cache[100000];
int       hash_table_cache_index = 0;

void hash_insert(int value)
{
    int        index    = hash(value);
    hash_node *node     = hash_table[index];

    if (node != NULL) {
        do {
            if (node->value == value) {
                return;
            }
            if (node->next == NULL) {
                break;
            }
            node = node->next;
        } while (1);
    }

    hash_node *new_node = &hash_table_cache[hash_table_cache_index++];
    new_node->value     = value;
    new_node->next      = NULL;
    new_node->flag      = false;
    if (node == NULL) {
        hash_table[index] = new_node;
    } else {
        node->next = new_node;
    }
}

hash_node *hash_find(int value)
{
    int        index = hash(value);
    hash_node *node  = hash_table[index];
    while (node != NULL) {
        if (node->value == value) {
            return node;
        }
        node = node->next;
    }
    return NULL;
}

bool is_visited(int value)
{
    hash_node *node = hash_find(value);
    if (!node->flag) {
        node->flag = true;
        return false;
    }
    return true;
}

void free_hash(void)
{
    memset(hash_table, 0, sizeof(hash_table));
    hash_table_cache_index = 0;
}

int longestConsecutive(int *nums, int numsSize)
{
    if (numsSize == 0 || numsSize > 100000) {
        return 0;
    }

    for (int i = 0; i < numsSize; ++i) {
        hash_insert(nums[i]);
    }

    int longestStreak = 0;

    for (int i = 0; i < numsSize; ++i) {
        if (hash_find(nums[i] - 1) == NULL) {
            if (is_visited(nums[i])) {
                continue;
            }
            int currentNum    = nums[i];
            int currentStreak = 1;

            while (hash_find(currentNum + 1)) {
                currentNum += 1;
                currentStreak += 1;
            }

            longestStreak = longestStreak > currentStreak ? longestStreak : currentStreak;
        }
    }

    free_hash();
    return longestStreak;
}

这段代码实现了一个哈希表,用于解决最长连续序列的问题。下面是对代码的简要解释:

  1. #define HASH_SIZE 65536:定义了哈希表的大小为65536。
  2. typedef struct hash_node { ... }:定义了哈希表节点的结构体,包含一个标志位flag、一个整数值value和指向下一个节点的指针next
  3. hash_node *hash_table[HASH_SIZE];:定义了一个哈希表数组,每个元素是指向哈希节点的指针。
  4. static inline int hash(int value):哈希函数,用于将整数值映射到哈希表的索引。这里使用了简单的按位与运算。
  5. hash_node hash_table_cache[100000];:定义了一个缓存数组,用于存储哈希节点,避免频繁的内存分配和释放。
  6. void hash_insert(int value):插入操作,将整数值插入到哈希表中。如果哈希表中已经存在该值,则不进行插入;否则,从缓存数组中取出一个新的节点,设置其值和指针,并将其插入到哈希表的对应位置。
  7. hash_node *hash_find(int value):查找操作,在哈希表中查找给定的整数值,并返回对应的节点指针。如果找不到,则返回NULL。
  8. bool is_visited(int value):判断一个整数值是否已经被访问过。如果对应的节点的flag为false,则将其设置为true并返回false,表示第一次访问;否则返回true,表示已经访问过。
  9. void free_hash(void):释放哈希表的内存,将哈希表数组清零,并将缓存数组的索引重置为0。
  10. int longestConsecutive(int *nums, int numsSize):求最长连续序列的长度的函数。首先将所有的整数值插入到哈希表中,然后遍历数组,对于每个整数值,如果它的前一个数不存在于哈希表中,则从该数开始向后搜索连续的数,直到无法继续为止,记录最长的连续序列长度。最后释放哈希表的内存并返回最长连续序列的长度。

这段代码使用哈希表来优化最长连续序列的查找过程,通过哈希表的快速插入和查找操作,可以在线性时间复杂度内解决问题。同时,使用缓存数组来避免频繁的内存分配和释放,提高了代码的性能。

下面是测试结果(仅供参考),可以看到执行效率很高,主要由Hash大小和元素分配缓存因素影响,减少无用的处理流程。

代码训练LeetCode(5)最长连续序列,CS小白之路,#  十年代码训练,# C语言,leetcode,算法,c语言

4. 总结

这个问题考察了对哈希表的使用,以及如何在不排序的情况下进行有效的搜索。这是一个对时间复杂度要求较高的问题,因此学习如何利用数据结构进行优化是非常重要的。要提升这方面的能力,建议多练习类似的问题,熟悉各种数据结构的性能特点,并学会根据问题的特点选择合适的数据结构。

对于哈希表的使用,需要注意的是如何处理冲突,以及如何设计一个好的哈希函数以减少冲突的发生。此外,在实际编码中,还需要考虑内存管理,确保所有分配的内存在不再使用时被正确释放,避免内存泄漏。







代码训练LeetCode(5)最长连续序列,CS小白之路,#  十年代码训练,# C语言,leetcode,算法,c语言

Once Day

也信美人终作土,不堪幽梦太匆匆......

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!

(。◕‿◕。)感谢您的阅读与支持~~~文章来源地址https://www.toymoban.com/news/detail-842948.html

到了这里,关于代码训练LeetCode(5)最长连续序列的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • leetcode做题笔记128. 最长连续序列

    给定一个未排序的整数数组  nums  ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 请你设计并实现时间复杂度为  O(n)   的算法解决此问题。 本题要求最长连续序列,可以将先将数组内数先排序一遍,再向后不断遍历数组比较得到最长连续序列,但

    2024年02月09日
    浏览(42)
  • 【LeetCode动态规划#14】子序列系列题(最长递增子序列、最长连续递增序列、最长重复子数组、最长公共子序列)

    力扣题目链接(opens new window) 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 示例 1: 输入:nums = [10,9,2,5,3,7,101,18] 输出

    2024年02月01日
    浏览(56)
  • LeetCode | C++ 动态规划——300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

    300题目链接 dp 数组定义 dp[i] 表示 i 之前包括 i 的以 nums[i]结尾 的最长递增子序列的长度 需要包含nums[i]结尾,不然在做递增比较的时候,就没有意义了。 递推公式 位置 i 的最长递增子序列 等于 j 从 0 到 i - 1各个位置的最长递增子序列 + 1 的 最大值 if (nums[i] nums[j]) dp[i] = ma

    2024年02月16日
    浏览(48)
  • ( 动态规划) 674. 最长连续递增序列 / 718. 最长重复子数组——【Leetcode每日一题】

    难度:简单 给定一个未经排序的整数数组,找到最长且 连续递增的子序列 ,并返回该序列的长度。 连续递增的子序列 可以由两个下标 l 和 r(l r) 确定,如果对于每个 l = i r ,都有 nums[i] nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

    2024年02月05日
    浏览(52)
  • 【刷题之路】LeetCode 2389. 和有限的最长子序列

    原题连接: 2389. 和有限的最长子序列 题目描述: 给你一个长度为 n 的整数数组 nums ,和一个长度为 m 的整数数组 queries 。 返回一个长度为 m 的数组 answer ,其中 answer[i] 是 nums 中 元素之和小于等于 queries[i] 的 子序列 的 最大 长度 。 子序列 是由一个数组删除某些元素(也可

    2023年04月16日
    浏览(32)
  • day55 最长递增子序列 最长连续递增子序列 最长重复子数组

    题目链接 300 最长递增子序列 题意 找到整数数组nums的最长严格递增子序列的长度(子序列并不改变原始的顺序,但是可以删除元素) 动态规划 动规五部曲 1)dp数组及下标i的含义 dp[i] 表示以nums[i]为结尾的最长递增子序列的长度 2)dp数组初始化 根据定义 长度至少是1  dp

    2024年04月11日
    浏览(61)
  • 力扣-哈希-最长连续序列

    给定一个未排序的整数数组  nums  ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 请你设计并实现时间复杂度为  O(n)  的算法解决此问题。 示例 1: **输入:**nums = [100,4,200,1,3,2] **输出:**4 **解释:**最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

    2024年02月10日
    浏览(33)
  • 算法刷题Day 52 最长递增子序列+最长连续递增子序列+最长重复子数组

    我自己想出来的方法,时间复杂度应该是 O(n2) 滑动窗口 连续的话,可以考虑用滑动窗口 动态规划 贪心算法

    2024年02月14日
    浏览(54)
  • 动态规划9:最长递增子序列、最长连续递增序列、最长重复子数组、最长公共子序列、不相交的线、最长子序和

    例题300: 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 确定dp数组和下标含义 dp[i]表示在第i个元素的最长子序列数

    2024年04月08日
    浏览(44)
  • 动态规划---最长连续子序列问题

    最长连续子序列问题算是动态规划问题中的一个小分支,这里单独写一篇文章介绍。至于动态规划基础问题和详细的处理步骤我在我的另一篇文章中详细介绍过。具体解决步骤请移步观看——动态规划基础篇。如果想了解01背包问题和滚动数组相关内容请移步观看——动态规

    2024年02月15日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包