Advertisement

446. 等差数列划分 II - 子序列

阅读量:

1. 题号和题目名称

  1. 等差数列划分 II - 子序列

2. 题目叙述

给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目。

对于任何一个长度不小于3的序列而言,在其相邻元素之间的差异恒定时,则可将其定义为等差数列。

  • 例如,[1, 3, 5, 7, 9][7, 7, 7, 7][3, -1, -5, -9] 都是等差序列。
  • 再例如,[1, 1, 2, 5, 7] 不是等差序列。

子序列是从数组中移除某些元素(可能也排除部分)而形成的一个新序列

  • 例如,[2,5,10][1,2,1,2,4,1,5,10] 的一个子序列。

题目数据保证答案是一个 32-bit 整数。

示例 1

复制代码
    输入:nums = [2,4,6,8,10]
    输出:7
    解释:所有的等差子序列为:
    [2,4,6]
    [4,6,8]
    [6,8,10]
    [2,4,6,8]
    [4,6,8,10]
    [2,4,6,8,10]
    [2,6,10]
    
    
    plaintext
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-16/yZOhVURXSw5PoF3NLHJAMlEiGWsm.png)

示例 2

复制代码
    输入:nums = [7,7,7,7,7]
    输出:16
    解释:数组中的任意子序列都是等差子序列。
    
    
    plaintext

3. 模式识别

本题可采用动态规划方法来解决。针对每一个元素而言,在处理过程中我们需要建立一个哈希表用于记录以该特定结尾处公差为某一固定值的所有等差子序列的数量。通过遍历整个数组结构,在每一次迭代中计算当前所处理的数值与其前驱数值之间的差异,并根据这一差异更新对应位置上的数据。

4. 考点分析

  • 动态规划:通过利用记录中间状态的方法来消除重复计算带来的冗余。
    • 哈希表的应用:用于记录以每个元素结尾且公差固定的具体等差数列数量。
    • 子序列的相关处理:在处理过程中必须考虑到所有可能存在的子序列情况。

5. 所有解法

  • 动态规划 + 哈希表解法 :该方法被认为是本题的最佳方案之一,在算法设计中采用动态规划算法配合哈希表来存储中间计算结果的方式能够显著降低计算复杂度。
  • 暴力枚举法 :为了找出满足特定条件的子序列集合,在此方法中我们采用遍历所有可能的子序列集合的方式进行筛选,并最终得出满足条件的结果集。

6. 最优解法(动态规划 + 哈希表解法)的 C 语言代码

复制代码
    #include <stdio.h>
    #include <stdlib.h>
    
    // 定义哈希表节点结构体
    typedef struct HashNode {
    long long diff;  // 公差
    int count;       // 以当前元素结尾且公差为 diff 的等差子序列数量
    struct HashNode *next;
    } HashNode;
    
    // 定义哈希表结构体
    typedef struct {
    HashNode **table;
    int size;
    } HashMap;
    
    // 创建新的哈希表节点
    HashNode* createHashNode(long long diff, int count) {
    HashNode *node = (HashNode*)malloc(sizeof(HashNode));
    node->diff = diff;
    node->count = count;
    node->next = NULL;
    return node;
    }
    
    // 创建新的哈希表
    HashMap* createHashMap(int size) {
    HashMap *map = (HashMap*)malloc(sizeof(HashMap));
    map->table = (HashNode**)malloc(size * sizeof(HashNode*));
    map->size = size;
    for (int i = 0; i < size; i++) {
        map->table[i] = NULL;
    }
    return map;
    }
    
    // 哈希函数
    int hashFunction(long long diff, int size) {
    return (diff % size + size) % size;
    }
    
    // 向哈希表中插入或更新键值对
    void put(HashMap *map, long long diff, int count) {
    int index = hashFunction(diff, map->size);
    HashNode *node = map->table[index];
    while (node) {
        if (node->diff == diff) {
            node->count += count;
            return;
        }
        node = node->next;
    }
    node = createHashNode(diff, count);
    node->next = map->table[index];
    map->table[index] = node;
    }
    
    // 从哈希表中获取键对应的值
    int get(HashMap *map, long long diff) {
    int index = hashFunction(diff, map->size);
    HashNode *node = map->table[index];
    while (node) {
        if (node->diff == diff) {
            return node->count;
        }
        node = node->next;
    }
    return 0;
    }
    
    // 释放哈希表内存
    void freeHashMap(HashMap *map) {
    for (int i = 0; i < map->size; i++) {
        HashNode *node = map->table[i];
        while (node) {
            HashNode *temp = node;
            node = node->next;
            free(temp);
        }
    }
    free(map->table);
    free(map);
    }
    
    // 等差数列划分 II - 子序列函数
    int numberOfArithmeticSlices(int* nums, int numsSize) {
    if (numsSize < 3) {
        return 0;
    }
    // 为每个元素创建一个哈希表
    HashMap **dp = (HashMap**)malloc(numsSize * sizeof(HashMap*));
    for (int i = 0; i < numsSize; i++) {
        dp[i] = createHashMap(1000);
    }
    int result = 0;
    // 遍历数组
    for (int i = 0; i < numsSize; i++) {
        for (int j = 0; j < i; j++) {
            long long diff = (long long)nums[i] - nums[j];
            // 获取以 nums[j] 结尾且公差为 diff 的等差子序列数量
            int count = get(dp[j], diff);
            // 更新结果
            result += count;
            // 以 nums[i] 结尾且公差为 diff 的等差子序列数量增加
            put(dp[i], diff, count + 1);
        }
    }
    // 释放哈希表内存
    for (int i = 0; i < numsSize; i++) {
        freeHashMap(dp[i]);
    }
    free(dp);
    return result;
    }
    
    
    c
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-16/ksOruMf1o9dK0zEyPxnA3bc5ZFqg.png)

你可以使用以下方式调用这个函数:

复制代码
    int main() {
    int nums[] = {2, 4, 6, 8, 10};
    int numsSize = sizeof(nums) / sizeof(nums[0]);
    int result = numberOfArithmeticSlices(nums, numsSize);
    printf("等差子序列的数目: %d\n", result);
    return 0;
    }
    
    
    c

7. 复杂度分析

  • 该算法的时间复杂度为O(n^2), 其中n表示数组长度. 该过程需循环处理两次数组中的每一个元素对, 并在哈希表中完成插入和查找操作. 尽管单次操作的时间复杂度呈现恒定表现.
  • 该算法的空间复杂度同样为O(n^2), 主要由存储每个元素对应关系所形成的哈希表决定. 在最坏情况下分析时可假设每两个不同元素之间的差值各不相同, 则会导致哈希表中存在n^2个键值对.

全部评论 (0)

还没有任何评论哟~