目录
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 是数组大小。
解题思路
-  排序:首先,对输入的整数数组 nums进行排序,这样可以方便后续对相同数字进行计数。
-  计数:使用结构体数组 Nums来存储排序后的数字以及它们的出现次数。遍历排序后的nums,对于每个数字,如果与前一个数字相同,则增加计数;如果不同,则创建一个新的Num结构体,并设置计数为 1。
-  再次排序:根据 Num结构体的cnt字段(即数字的出现次数)对Nums进行降序排序。这样,出现次数最多的数字会排在前面。
-  提取结果:从排序后的 Nums中提取前k个数字,即出现次数最多的k个数,存入结果数组res。
-  返回结果:返回结果数组 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 的索引。
四、编写回溯函数
-  递归终止条件:当 pIndex(当前路径的长度)等于k时,说明已经找到了一个完整的组合,将其添加到result数组中。
-  搜索空间剪枝:我们从 startIndex开始遍历,确保不会重复选择数字,也不会选择超过剩余需要的数字数。这里startIndex的取值范围是根据当前路径长度和剩余需要选择的数字数来确定的。
-  递归与回溯:对于每个遍历到的数字,将其添加到 path中,并递归调用backtracking函数继续搜索下一个数字。递归返回后,需要撤销选择,即将pIndex减一,以尝试其他可能性。
五、编写主函数
-  初始化:为 path和result分配内存,并初始化pIndex和rIndex。
-  调用回溯函数:调用 backtracking函数开始生成组合。
-  设置返回信息:设置返回的组合数量和大小信息。由于每个组合的大小都是 k,可以直接用一个数组returnColumnSizes来存储每个组合的大小信息。
-  返回结果:返回 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));// 设置每个组合的大小为 kfor (int i = 0; i < rIndex; i++) {(*returnColumnSizes)[i] = k;}// 返回结果return result;
}