《Leetcode零基础刷题指南》- 贪心

本文通过一系列编程题目,如救生艇问题、最小时间差计算、最接近三数之和等,深入浅出地介绍了贪心算法的思路和应用。文章详细分析了每个问题的解决方案,并提供了相应的代码实现,帮助读者理解如何利用贪心策略找到最优解。
摘要由CSDN通过智能技术生成

⭐️本篇博客我要来和大家一起聊一聊刷题最简单的开始,贪心算法


前言


一、救生艇(传送门)

给定数组 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.有效三角形的个数:双指针解法

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

penguin_bark

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值