每日练习——leetcode77和347

目录

347. 前 K 个高频元素

题目描述

解题思路

代码实现

77. 组合

题目描述

解题思路

代码实现

347. 前 K 个高频元素

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

解题思路

  1. 排序:首先,对输入的整数数组 nums 进行排序,这样可以方便后续对相同数字进行计数。

  2. 计数:使用结构体数组 Nums 来存储排序后的数字以及它们的出现次数。遍历排序后的 nums,对于每个数字,如果与前一个数字相同,则增加计数;如果不同,则创建一个新的 Num 结构体,并设置计数为 1。

  3. 再次排序:根据 Num 结构体的 cnt 字段(即数字的出现次数)对 Nums 进行降序排序。这样,出现次数最多的数字会排在前面。

  4. 提取结果:从排序后的 Nums 中提取前 k 个数字,即出现次数最多的 k 个数,存入结果数组 res

  5. 返回结果:返回结果数组 res,并设置 returnSize 为 k

代码实现

/**  
 * Note: The returned array must be malloced, assume caller calls free().  
 */  
struct Num {  
    int num;     // 存储数字  
    int cnt;     // 存储数字出现的次数  
};  
  
// 比较函数,用于整数数组的排序  
int cmp(const void* a, const void* b) {   
    return *(int*)a - *(int*)b;  
}  
  
// 比较函数,用于Num结构体的排序,按出现次数降序  
int cmps(const void* a, const void* b) {  
    return (*(struct Num*)b).cnt - (*(struct Num*)a).cnt;  
}  
  

int* topKFrequent(int* nums, int numsSize, int k, int* returnSize) {  
    *returnSize = k;  
    int* res = (int*)malloc(sizeof(int) * k); // 分配结果数组内存  
    struct Num Nums[numsSize];                 // 定义结构体数组用于存储数字及其出现次数  
    int index = 0;                             // 结构体数组的索引  
  
    // 对整数数组进行排序  
    qsort(nums, numsSize, sizeof(int), cmp);  
  
    // 初始化第一个Num结构体  
    Nums[index].num = nums[0];  
    Nums[index].cnt = 1;  
  
    // 遍历整数数组,进行计数  
    for (int i = 1; i < numsSize; i++) {  
        if (nums[i] == nums[i - 1]) {  
            Nums[index].cnt++;  
        } else {  
            index++;  
            Nums[index].num = nums[i];  
            Nums[index].cnt = 1;  
        }  
    }  
  
    // 根据出现次数对Nums进行降序排序  
    qsort(Nums, index + 1, sizeof(struct Num), cmps);  
  
    // 提取前k个数字  
    for (int i = 0; i < k; i++) {  
        res[i] = Nums[i].num;  
    }  
  
    return res;  
}



77. 组合

题目描述

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

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

示例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]

提示:

  • 1 <= n <= 20
  • 1 <= k <= n

解题思路

一、问题分析

题目要求从n个数字中选出k个数字的所有组合。这是一个典型的组合问题,其中组合是不考虑顺序的。我们需要生成所有可能的组合,并返回这些组合的结果。

二、回溯算法介绍

为了解决这个问题,可以使用回溯算法。回溯算法是一种通过探索所有可能的候选解来找出所有解的算法。如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化来丢弃该解,即“回溯”并尝试其他的可能性。

三、定义全局变量

在回溯的过程中,需要一些全局变量来辅助计算。path 数组用于存储当前正在构建的组合路径,result 数组用于存储所有找到的组合结果,pIndex 和 rIndex 分别用于追踪 path 和 result 的索引。

四、编写回溯函数

  1. 递归终止条件:当 pIndex(当前路径的长度)等于 k 时,说明已经找到了一个完整的组合,将其添加到 result 数组中。

  2. 搜索空间剪枝:我们从 startIndex 开始遍历,确保不会重复选择数字,也不会选择超过剩余需要的数字数。这里 startIndex 的取值范围是根据当前路径长度和剩余需要选择的数字数来确定的。

  3. 递归与回溯:对于每个遍历到的数字,将其添加到 path 中,并递归调用 backtracking 函数继续搜索下一个数字。递归返回后,需要撤销选择,即将 pIndex 减一,以尝试其他可能性。

五、编写主函数

  1. 初始化:为 path 和 result 分配内存,并初始化 pIndex 和 rIndex

  2. 调用回溯函数:调用 backtracking 函数开始生成组合。

  3. 设置返回信息:设置返回的组合数量和大小信息。由于每个组合的大小都是 k,可以直接用一个数组 returnColumnSizes 来存储每个组合的大小信息。

  4. 返回结果:返回 result 数组作为结果。

代码实现

/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume
 * caller calls free().
 */

// 定义全局变量,用于存储当前路径和结果
int* path;          // 存储当前正在构建的组合路径
int** result;       // 存储所有组合的结果
int pIndex, rIndex; // 分别用于追踪 path 和 result 的索引

// 回溯函数,用于生成所有可能的组合
void backtracking(int n, int k, int startIndex) {
    // 如果当前路径中的数字个数达到了 k,说明找到了一组解
    if (pIndex == k) {
        // 分配内存存储当前路径
        int* tmp = (int*)malloc(k * sizeof(int));
        // 将当前路径复制到 tmp 中
        for (int i = 0; i < k; i++) {
            tmp[i] = path[i];
        }
        // 将 tmp 添加到结果数组中
        result[rIndex++] = tmp;
        return;
    }
    // 从 startIndex 开始遍历到最大可能的起始位置
    for (int i = startIndex; i <= n - (k - pIndex) + 1; i++) {
        // 将当前数字添加到路径中
        path[pIndex++] = i;
        // 继续递归搜索下一个数字
        backtracking(n, k, i + 1);
        // 回溯,撤销选择
        pIndex--;
    }
}

int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {
    path = (int*)malloc(k * sizeof(int));
    result = (int**)malloc(200001 * sizeof(int*));
    // 初始化索引
    pIndex = 0, rIndex = 0;
    // 调用回溯函数开始生成组合
    backtracking(n, k, 1);
    // 设置返回的组合数量和大小信息
    *returnSize = rIndex;
    *returnColumnSizes = (int*)malloc(rIndex * sizeof(int));
    // 设置每个组合的大小为 k
    for (int i = 0; i < rIndex; i++) {
        (*returnColumnSizes)[i] = k;
    }
    // 返回结果
    return result;
}

  • 46
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值