⭐️本篇博客我要来和大家一起聊一聊刷题最简单的开始,贪心算法
目录
前言
一、救生艇(传送门)
给定数组 people 。people[i]表示第 i 个人的体重 ,船的数量不限,每艘船可以承载的最大重量为 limit。
每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。
返回 承载所有人所需的最小船数 。
示例 1:
输入:people = [1,2], limit = 3
输出:1
解释:1 艘船载 (1, 2)
🔑思路:
1.首先将所有人排序
2.双指针指向第一个人和最后一个人
3.如果最后一个(最大的人)和第一个(最小的人)加起来都比限制小,那么这两个人都可以上船,不然就让最后一个(最大的人)一个人一个船。
int cmpfunc(const void* a,const void* b){
return *(int *)a - *(int *)b;
}
int numRescueBoats(int* people, int peopleSize, int limit){
//按照从小到大顺序排列
qsort(people, peopleSize, sizeof(int), cmpfunc);
int left = 0, right = peopleSize - 1;
int consst = 0;
while(left <= right){
//如果只剩一个人那么直接加上一只船并且结束循环
if(left == right){
consst++;
break;
}
//如果两个人加起来比限制小,则船加1减少两个人,如果两个加起来比限制大,则船只承载最重的那一个
else if(people[left] + people[right] > limit){
consst++;
right--;
}
else{
consst++;
left++;
right--;
}
}
return consst;
}
二、最小时间差(传送门)
给定一个 24 小时制(小时:分钟 "HH:MM")的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。
示例 1:
输入:timePoints = ["23:59","00:00"]
输出:1
🔑思路:
1.用sscanf函数将字符串转化成整数
2.再将小时转化成分钟
3.排序时间
4.然后比较相邻取出较小值
5.注意时间循环
1440就是整24小时,00:00等同于24:00,所以设置ans为1440(不能设置为0,比如设置)
int cmpfunc(const void* a,const void* b){
return *(int *)a - *(int *)b;
}
int min (int a, int b){
return a > b ? b : a;
}
int findMinDifference(char ** timePoints, int timePointsSize){
int* ret = (int*)malloc(sizeof(int)*timePointsSize);
if(ret == NULL){
return NULL;
}
int a, b;
for(int i = 0; i < timePointsSize; i++){
//字符串格式化对应的整数
sscanf(timePoints[i],"%d:%d",&a,&b);
//将小时和分钟转化为分钟
ret[i] = a*60+b;
}
//对分钟进行排序处理
qsort(ret, timePointsSize, sizeof(int), cmpfunc);
int ans = 24*60;
for(int i = 0; i < timePointsSize-1; i++){
//取相邻时间间隔最小者
ans = min(ans, ret[i+1] - ret[i]);
}
//注意时间循环的情况
ans = min(ans, ret[0]+24*60 - ret[timePointsSize-1]);
return ans;
}
三、最接近的三数之和(传送门)
给你一个长度为
n
的整数数组nums
和 一个目标值target
。请你从nums
中选出三个整数,使它们的和与target
最接近。返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
输入:nums = [-1,2,1,-4], target = 1 输出:2 解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
🔑思路:
1.排序数组
2.从第一个元素开始,循环数组
3.双指针从第二个和最后一个开始
4.不断缩小范围
5.如果最后差值比较,将所有差值较小的那一个存起来
int cmp(const void* e1, const void* e2){
return *(int*)e1 - *(int*)e2;
}
int threeSumClosest(int* nums, int n, int target) {
qsort(nums, n, sizeof(int), cmp);
int last = nums[0] + nums[1] + nums[2];
for (int i = 0; i < n - 2; i++) {
int l = i + 1;
int r = n - 1;
int sum = 0;
while (l < r) {
sum = nums[i] + nums[r] + nums[l];
if (sum == target) {
return target;
}
else if (sum > target) {
r--;
}
else if (sum < target) {
l++;
}
if (fabs(last - target) > fabs(sum - target)) {
last = sum;
}
}
}
return last;
}
四、三数之和(传送门)
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
🔑思路:
1.判断数组是不是空或者元素个数是否小于三
2.将数组升序排列
3.依次遍历数组
1.判断当前元素是否为正,是则结束循环,因为后续的数字也都为正,不会出现三数之和为0的情况
2.判断当前元素是否与上一次相等,相等则跳过进行去重
3.从 i + 1 到 numsSize - 1 进行左右指针判断三数之和sum
1.sum == 0
1.保存一组值
2.对left和right去重
2.sum < 0
1.left++
3.sum>0
1.right++
其中参数 int** returnColumnSizes 表示返回数组中每一行的列数:
分配:
*returnColumnSizes = (int*)malloc(numsSize * numsSize *sizeof(int));
使用:/* 返回数组当前行的列数为3 */
(*returnColumnSizes)[*returnSize] = 3;其中返回数组ret为类似二维数组:
分配:
int** ret = (int**)malloc(numsSize * numsSize * sizeof(int*));
ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
使用:
ret[*returnSize][0] = nums[i];
/**
* 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().
*/
/* qsort的比较函数 */
int cmp(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
/* 先记录返回的行数为0 */
*returnSize = 0;
/* 输入为空、或元素个数小于3则返回NULL */
if (nums == NULL || numsSize < 3) {
return NULL;
}
/* 将nums排序为升序排列 */
qsort(nums, numsSize, sizeof(int), cmp);
/* 分配返回数组、返回数组的列数 */
int** ret = (int**)malloc(numsSize * numsSize * sizeof(int*));
*returnColumnSizes = (int*)malloc(numsSize * numsSize *sizeof(int));
/* 排序后的数组从头到尾进行遍历 */
for (int i = 0; i < numsSize; i++) {
/* 当前数组大于0,表示后续没有三数之和为0,结束遍历 */
if (nums[i] > 0) {
break;
}
/* 当前元素与上一次相等,跳过此次计算,去重 */
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
/* 定义左右指针 */
int left = i + 1, right = numsSize - 1;
/* 开始查找三数之和为0 */
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
ret[*returnSize] = (int*)malloc(sizeof(int) * 3);
ret[*returnSize][0] = nums[i];
ret[*returnSize][1] = nums[left];
ret[*returnSize][2] = nums[right];
/* 返回数组当前行的列数为3 */
(*returnColumnSizes)[*returnSize] = 3;
/* 返回数组的行数自加1 */
(*returnSize)++;
/* 对左右指针进行去重 */
while (left < right && nums[left] == nums[++left]);
while (left < right && nums[right] == nums[--right]);
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return ret;
}
五、盛最多水的容器(传送门)
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
🔑思路:
1.左右指针指向左右两个端点
2.计算出左右端点较小的值
3.接着算出面积
4.如果移动高柱子的话长一定会减小,那么移动后的下一个柱子比矮柱子小则面积小,比矮柱子大则面积小,所以只有移动矮柱子才有可能将面积变大。
int min (int a, int b){
return a > b ? b : a;
}
int maxArea(int* height, int heightSize){
int max = 0;
int left = 0;
int right = heightSize-1;
while(left < right){
int gao = min(height[left], height[right]);
int sum = gao*(right - left);
max = max>sum?max:sum;
//当矮柱子小于高时候,不存在,矮柱子等于高的时候就移动矮柱子
if(gao == height[left]){
left++;
}
else{
right--;
}
}
return max;
}
六、有效三角形的个数(传送门)
给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。
示例 1:
输入: nums = [2,2,3,4]
输出: 3
解释:有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3
🔑思路:
1.排序整个数组
2.选定最后一个(最大数)
3.然后双指针指向第一个数(最小的数),倒数第二个数(第二大的数)
4.判断是否构成三角形
5.右指针减左指针得到组合个数,相减的是下标,如果最左值和当前右边的指针相加大于第三边,那么最左值右边的数都满足(升序排列),只要计算个数就可以了
int cmp(const void* e1, const void* e2){
return *(int*)e1 - *(int*)e2;
}
int triangleNumber(int* nums, int n){
qsort(nums, n, sizeof(int), cmp);
int counst = 0;
for(int i = n-1; i >= 2; i--){
int l = 0, r = i-1;
while(l < r){
if(nums[l]+nums[r]>nums[i]){
counst += r-l;
r--;
}
else{
l++;
}
}
}
return counst;
}
七、分发饼干(传送门)
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
🔑思路:
1.首先将饼干和孩子分别排序
2.然后从右向左遍历,如果最大的饼干符合最大胃口孩子,则将饼干减少一同时将孩子也减少一
3.如果最大的饼干不能满足胃口最大的孩子,则将胃口小一点的孩子与当前最大的饼干比较,当饼干或孩子遍历完后则结束循环。
int cmp (const void* e1, const void* e2){
return *(int*)e1 - *(int*)e2;
}
int findContentChildren(int* g, int gSize, int* s, int sSize){
int consut = 0;
int i = gSize - 1;
int j = sSize - 1;
qsort(g, gSize, sizeof(int), cmp);
qsort(s, sSize, sizeof(int), cmp);
while(i >= 0 && j >= 0){
if(s[j]>=g[i]){
i--;
j--;
consut++;
}
else{
i--;
}
}
return consut;
}
八、分发糖果(传送门)
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。示例 1:
输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
🔑思路:
1.首相创建一个数组,用于计算糖果的数量,先给每一个孩子一个糖果
2.如果孩子数量小于2,则直接返回一个糖果
3.之后先从左向右遍历,如果右边的比左边的要大,则将左边的糖果加一后赋值给右边
4.然后再从右向左遍历,将右边糖果加一和左边的糖果比较,要取最大值给左边的糖果(左边糖果比右边的要大,同时不能重复像加)
5.之后统计所有的糖果返回即可。
int max(int a, int b){
return a>b?a:b;
}
int candy(int* ratings, int ratingsSize){
int nums[ratingsSize];
for(int i = 0; i < ratingsSize; i++){
nums[i] = 1;
}
if(ratingsSize < 2){
return 1;
}
for(int i = 1; i < ratingsSize; i++){
if(ratings[i] > ratings[i-1]){
nums[i] = nums[i-1]+1;
}
}
for(int i = ratingsSize-1; i > 0; i--){
if(ratings[i-1] > ratings[i]){
nums[i-1] = max(nums[i-1], nums[i]+1);
}
}
int sum = 0;
for(int i = 0; i < ratingsSize; i++){
sum += nums[i];
}
return sum;
}
总结
1.救生艇:当最轻和最重两个人是否可以做同一条船,最重的人一定也可以坐一条船
2.最小时差:学习sscanf函数,注意时间循环,00:00和24:00相等,但是我们要取最小值,所以初始值设置为24:00(24*60min),最后比较最小值和最大值之间是否存在时间循环。
3.最接近三数之和:首先选定一个数,然后双指针一个一个判断,最后和上一个最接近的数比较
4.三数之和:详情看解析(都重要)
5.盛水最多的容器:首先算出当前水的容积,移动较矮的继续判断,移动高的容积一定会减小。
6.有效三角形的个数:双指针解法