1.1 LeetCode总结(线性表)_枚举技巧

零、常用枚举技巧

§0.1 枚举右,维护左

对于 双变量问题,例如两数之和 ai + aj = t,可以枚举右边的 aj,转换成 单变量问题,也就是在 aj

​左边查找是否有 ai = t − aj,这可以用哈希表维护。

我把这个技巧叫做 枚举右,维护左。

1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9

输出:[0,1]

解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6

输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6

输出:[0,1]

c 复制代码
typedef struct {
    int num; // 哈希表的 key
    int idx; // 哈希表的 value
    UT_hash_handle hh; // uthash 需要
} hashTable;
hashTable *g_hashTable = NULL; // 创建一个空哈希表
int *twoSum(int *nums, int numsSize, int target, int *returnSize)
{
    hashTable *tmp;
    for (int j = 0; ; j++) { // 枚举 j
        // 在左边找 nums[i],满足 nums[i]+nums[j]=target
        int t = target - nums[j];
        HASH_FIND_INT(g_hashTable, &t, tmp);
        if (tmp != NULL) { // 找到了
            int *ans = (int *)malloc(2 * sizeof(int)); // 分配返回结果的内存空间
            *returnSize = 2; // ans 的长度
            ans[0] = tmp->idx; // 存储第一个数的下标
            ans[1] = j; // 存储第二个数的下标
            // 释放哈希表中所有节点,防止内存泄漏
            hashTable *tmp_del;
            HASH_ITER(hh, g_hashTable, tmp, tmp_del) {
                HASH_DEL(g_hashTable, tmp);
                free(tmp);
            }
            return ans; // 返回结果数组的指针
        }
        HASH_FIND_INT(g_hashTable, &nums[j], tmp);
        if (tmp == NULL) { // nums[j] 不在哈希表中
            // 保存 nums[j] 和 j 到哈希表中
            tmp = (hashTable *)malloc(sizeof(hashTable));
            tmp->num = nums[j];
            tmp->idx = j;
            HASH_ADD_INT(g_hashTable, num, tmp);
        } // else 哈希表有 nums[j],无需更新
    }
}

2342. 数位和相等数对的最大和

给你一个下标从 0 开始的数组 nums ,数组中的元素都是 正 整数。请你选出两个下标 i 和 j(i != j),且 nums[i] 的数位和 与 nums[j] 的数位和相等。

请你找出所有满足条件的下标 i 和 j ,找出并返回 nums[i] + nums[j] 可以得到的 最大值。如果不存在这样的下标对,返回 -1。

示例 1:

输入:nums = [18,43,36,13,7]

输出:54

解释:满足条件的数对 (i, j) 为:

  • (0, 2) ,两个数字的数位和都是 9 ,相加得到 18 + 36 = 54 。
  • (1, 4) ,两个数字的数位和都是 7 ,相加得到 43 + 7 = 50 。
    所以可以获得的最大和是 54 。
c 复制代码
typedef struct {
    int num; // 哈希表的 key
    int bit; // 哈希表的 value
    UT_hash_handle hh; // uthash 需要
} hashTable;
hashTable *g_hashTable = NULL; // 创建一个空哈希表
int Judge(int num) 
{
    int cnt = 0;
    while (num > 0) {
        cnt = cnt + num % 10;
        num = num / 10;
    }
    return cnt;
}
int maximumSum(int *nums, int numsSize)
{
    hashTable *tmp;
    int res = -1;
    for (int i = 0; i < numsSize; i++) {
        int bit = Judge(nums[i]);
        HASH_FIND_INT(g_hashTable, &bit, tmp);
        if (tmp == NULL) {
            tmp = (hashTable *)malloc(sizeof(hashTable));
            tmp->num = nums[i];
            tmp->bit = bit;
            HASH_ADD_INT(g_hashTable, bit, tmp);
        } else {
            res = fmax(nums[i] + tmp->num, res);
            tmp->num = fmax(nums[i], tmp->num);
        }
    }
    // 释放哈希表中所有节点,防止内存泄漏
    hashTable *tmp_del;
    HASH_ITER(hh, g_hashTable, tmp, tmp_del) {
        HASH_DEL(g_hashTable, tmp);
        free(tmp);
    }
    return res;
}
int main()
{
    int nums[] = {229,398,269,317,420,464,491,218,439,153,482,169,411,93,147,50,347,210,251,366,401};
    int ret = maximumSum(nums, sizeof(nums)/sizeof(int));
    printf("%ld ", ret);
    return 0;
}

1128. 等价多米诺骨牌对的数量

给你一组多米诺骨牌 dominoes 。

形式上,dominoes[i] = [a, b] 与 dominoes[j] = [c, d] 等价 当且仅当 (a == c 且 b == d) 或者 (a == d 且 b == c) 。即一张骨牌可以通过旋转 0 度或 180 度得到另一张多米诺骨牌。

在 0 <= i < j < dominoes.length 的前提下,找出满足 dominoes[i] 和 dominoes[j] 等价的骨牌对 (i, j) 的数量。

示例 1:

输入:dominoes = [[1,2],[2,1],[3,4],[5,6]]

输出:1

示例 2:

输入:dominoes = [[1,2],[1,2],[1,1],[1,2],[2,2]]

输出:3

提示:

1 <= dominoes.length <= 4 * 10^4

dominoes[i].length == 2

1 <= dominoes[i][j] <= 9

对于每个 dominoes[i]=[a,b],如果 a>b,我们就交换 a 和 b,从而保证 a≤b。这样就可以用 1512. 好数对的数目 题的算法了。注:这个思路和 49. 字母异位词分组 是一样的,如果两个骨牌在排序后相等,那么就是一对符合题目要求的骨牌对。

实现细节:可以用哈希表 cnt 存储 dominoes[i] 的出现次数,也可以利用 dominoes[i][j]≤9 的性质,用 10×10 的数组存储 dominoes[i] 的出现次数。

c 复制代码
int numEquivDominoPairs(int **dominoes, int dominoesSize, int *dominoesColSize)
{
    int ans = 0;
    int cnt[10][10] = {0};
    for (int i = 0; i < dominoesSize; i++) {
        int a = dominoes[i][0], b = dominoes[i][1];
        if (a <= b) {
            ans = ans + cnt[a][b]++;
        } else {
            ans = ans + cnt[b][a]++;
        }
    }
    return ans;
}

§0.2 枚举中间

对于有三个或者四个变量的问题,枚举中间的变量往往更好算。

为什么?比如问题有三个下标,需要满足 0≤i<j<k<n,对比一下:

枚举 i,后续计算中还需保证 j<k。

枚举 j,那么 i 和 k 自动被 j 隔开,互相独立,后续计算中无需关心 i 和 k 的位置关系。

所以枚举中间的变量更简单。

2909. 元素和最小的山形三元组 II

给你一个下标从 0 开始的整数数组 nums 。

如果下标三元组 (i, j, k) 满足下述全部条件,则认为它是一个 山形三元组 :

i < j < k

nums[i] < nums[j] 且 nums[k] < nums[j]

请你找出 nums 中 元素和最小 的山形三元组,并返回其 元素和 。如果不存在满足条件的三元组,返回 -1 。

示例 1:

输入:nums = [8,6,1,5,3]

输出:9

解释:三元组 (2, 3, 4) 是一个元素和等于 9 的山形三元组,因为:

  • 2 < 3 < 4
  • nums[2] < nums[3] 且 nums[4] < nums[3]
    这个三元组的元素和等于 nums[2] + nums[3] + nums[4] = 9 。可以证明不存在元素和小于 9 的山形三元组。
c 复制代码
int minimumSum(int *nums, int numsSize)
{
    int ans = INT_MAX;
    int *leftArr  = (int *) malloc(numsSize * sizeof(int));
    int *rightArr = (int *) malloc(numsSize * sizeof(int));
    // 1.左边界的最小值
    leftArr[0] = nums[0];
    for(int i = 1; i < numsSize; i++) {
        leftArr[i] = fmin(nums[i], leftArr[i - 1]);
    }
    // 2.右边界的最小值
    rightArr[numsSize - 1] = nums[numsSize - 1];
    for (int i = numsSize - 2; i >= 0; i --) {
        rightArr[i] = fmin(nums[i], rightArr[i + 1]);
    }
    for(int i = 1; i < numsSize - 1; i ++) {
        // [0, i - 1] 的最小值
        int minl = leftArr[i - 1];
        // [i + 1, numsSize - 1] 的最小值
        int minr = rightArr[i + 1];
        if (minl >= nums[i] || minr >= nums[i]) {
            continue;
        }
        ans = fmin(ans, minl + nums[i] + minr);
    }
    if (ans == INT_MAX) return -1;
    return ans;
}
int main()
{
    int nums[6] = {5,4,8,7,10,2};
    printf("%d\n", minimumSum(nums, 6));
    return 0;
}

3446. 按对角线进行矩阵排序

给你一个大小为 n x n 的整数方阵 grid。返回一个经过如下调整的矩阵:

左下角三角形(包括中间对角线)的对角线按 非递增顺序 排序。

右上角三角形 的对角线按 非递减顺序 排序。

示例 1:

输入: grid = [[1,7,3],[9,8,2],[4,5,6]]

输出: [[8,2,3],[9,6,7],[4,5,1]]

解释:

标有黑色箭头的对角线(左下角三角形)应按非递增顺序排序:

1, 8, 6\] 变为 \[8, 6, 1\]。 \[9, 5\] 和 \[4\] 保持不变。 标有蓝色箭头的对角线(右上角三角形)应按非递减顺序排序: \[7, 2\] 变为 \[2, 7\]。 \[3\] 保持不变。 ```c int cmp_less(const void* a, const void* b) { return (*(int*)a - *(int*)b); } int cmp_greater(const void* a, const void* b) { return (*(int*)b - *(int*)a); } int **sortMatrix(int **grid, int gridSize, int *gridColSize, int *returnSize, int **returnColumnSizes) { int m = gridSize, n = gridColSize[0]; int *a = malloc(MIN(m, n) * sizeof(int)); // k = n - j + i, 右上角 k=1,左下角 k=m+n-1 for (int k = 1; k < m + n; k++) { int min_j = MAX(n - k, 0); // i=0 时 j=n-k,但不能是负数 int max_j = MIN(m + n - 1 - k, n - 1); // i=m-1 时 j=m+n-1-k,但不能超过 n-1 int idx = 0; for (int j = min_j; j <= max_j; j++) { a[idx++] = grid[k + j - n][j]; // 根据 k 的定义 i=k+j-n } qsort(a, max_j - min_j + 1, sizeof(int), min_j > 0 ? cmp_less : cmp_greater); for (int j = min_j; j <= max_j; j++) { grid[k + j - n][j] = a[j - min_j]; } } free(a); *returnSize = m; *returnColumnSizes = malloc(m * sizeof(int)); for (int i = 0; i < m; i++) { (*returnColumnSizes)[i] = n; } return grid; } ```

相关推荐
mit6.8249 小时前
几何|阻碍链
算法
有一个好名字9 小时前
力扣-小行星碰撞
算法·leetcode·职场和发展
MM_MS9 小时前
Halcon图像锐化和图像增强、窗口的相关算子
大数据·图像处理·人工智能·opencv·算法·计算机视觉·视觉检测
lamentropetion9 小时前
E - Equal Tree Sums CF1656E
算法
代码游侠9 小时前
应用——智能配电箱监控系统
linux·服务器·数据库·笔记·算法·sqlite
Xの哲學9 小时前
Linux Platform驱动深度剖析: 从设计思想到实战解析
linux·服务器·网络·算法·边缘计算
逑之9 小时前
C语言笔记11:字符函数和字符串函数
c语言·笔记·算法
栈与堆9 小时前
LeetCode-1-两数之和
java·数据结构·后端·python·算法·leetcode·rust
不知名XL10 小时前
day20 回溯算法part02
算法