《LeetCode零基础指南》(第七讲) 贪心

1. 两个数对之间的最大乘积差

1913. 两个数对之间的最大乘积差

  • 题目

两个数对 (a, b)(c, d) 之间的 乘积差 定义为 (a * b) - (c * d)

例如,(5, 6)(2, 7) 之间的乘积差是 (5 * 6) - (2 * 7) = 16
给你一个整数数组 nums ,选出四个 不同的 下标 w、x、yz ,使数对 (nums[w], nums[x])(nums[y], nums[z]) 之间的 乘积差 取到 最大值

返回以这种方式取得的乘积差中的 最大值

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-product-difference-between-two-pairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 思路分析

返回两个数对的最大值,那么就需要将数组中最大和次大的数相乘、最小与次小的数相乘,前一项减去后一项即为最大乘积差。对数组排序即可解答本题

  • 源码实现
int cmp(const void* a, const void *b)
{
    return *(int *)a - *(int *)b;
}

int maxProductDifference(int* nums, int numsSize){
    qsort(nums, numsSize, sizeof(int), cmp);	//排序
    return nums[numsSize - 1] * nums[numsSize - 2] - nums[0] * nums[1];
}

2. 数组拆分

561. 数组拆分 I

  • 题目

给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从 1nmin(ai, bi) 总和最大。

返回该 最大总和 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/array-partition-i
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 思路分析

每一次选取的是数对的较小值,要使得总和最大,那么每次拆分的数对中的较小值应该仅次于较大值。根据这个条件,我们可以对数组进行排序,然后选取偶数位的元素相加即可

  • 源码实现
int cmp(const void* a, const void *b)
{
    return *(int *)a - *(int *)b;
}
int arrayPairSum(int* nums, int numsSize){
    //先排序
    qsort(nums, numsSize, sizeof(int), cmp);
    int i;
    int ret = 0;
    for(i = 0; i < numsSize; i += 2)	
    {
        ret += nums[i];	//取偶数位的值相加
    }
    return ret;
}

3. 摆动排序 II

324. 摆动排序 II

  • 题目

给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

你可以假设所有输入数组都可以得到满足题目要求的结果。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/wiggle-sort-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 思路分析

显而易见这题是需要进行排序的,问题在于排序之后怎么排列元素,观察题目给出的示例,可以看到较大值一定是在奇数位的,按照升序排列之后,较大的元素值一定是在数组的右半边,那么我们是不是可以先从数组的最右边开始遍历,将元素按指定位置插入新数组中,较大值插入完毕后,剩下的就是较小值。

  • 源码实现
int cmp(const void *a, const void* b)
{
    return *(int *)a - *(int *)b;
}

void wiggleSort(int* nums, int numsSize){
    qsort(nums, numsSize, sizeof(int), cmp);
    
    int ans[numsSize];
    int i;
    for(i = 0; i < numsSize; ++i)
    {
        ans[i] = nums[i];	//赋值到新数组
    }
    
    int r = numsSize - 1;	//注意是从大到小插入
    for(i = 1; i < numsSize; i += 2)	//先插较大值
    {
        nums[i] = ans[r--];
    }
    for(i = 0; i < numsSize; i += 2)	//在插较小值
    {
        nums[i] = ans[r--];
    }
}

4. 分发饼干

455. 分发饼干

  • 题目

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/assign-cookies
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 思路分析

本题其实就是求s数组中的元素大于等于g数组中元素的个数

  • 源码实现
int cmp(const void* a, const void* b)
{
    return *(int *)a - *(int *)b;
}

int findContentChildren(int* g, int gSize, int* s, int sSize){
    qsort(g, gSize, sizeof(int), cmp);	//先排序
    qsort(s, sSize, sizeof(int), cmp);

    int p2 = sSize - 1;
    int p1 = gSize - 1;
    int cnt = 0; //统计满足的孩子的数量
	
    //先满足胃口最大的孩子
    while(p1 >= 0 && p2 >= 0)
    {
        if(s[p2] >= g[p1])	
        {
            cnt++;
            p1--;
            p2--;
        }
        else
        {
            p1--;
        }
    }
    return cnt;
}

5. 最少操作使数组递增

1827. 最少操作使数组递增

  • 题目

给你一个整数数组 nums (下标从 0 开始)。每一次操作中,你可以选择数组中一个元素,并将它增加 1 。

比方说,如果 nums = [1,2,3] ,你可以选择增加 nums[1] 得到 nums = [1,3,3]
请你返回使 nums 严格递增最少 操作次数。

我们称数组 nums严格递增的 ,当它满足对于所有的 0 <= i < nums.length - 1 都有 nums[i] < nums[i+1] 。一个长度为 1 的数组是严格递增的一种特殊情况。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-operations-to-make-the-array-increasing
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 思路分析

求最小的操作次数,只需要比前面的元素大1就行。

  • 双层循环可以解答本题,外层循环控制前一个元素,内层循环控制后一个元素。
  • 使用单层循环加条件语句。外循环控制前一个元素,条件语句保证后一个元素比前一个元素大
  • 源码实现
int minOperations(int* nums, int numsSize){
    int i;
    int cnt = 0;
    int pre = nums[0] + 1;	//只需要比前一个元素大1即可
    for(i = 1; i < numsSize; ++i)
    {
        if(pre < nums[i])
        {
            pre = nums[i] + 1;	//小于更新为当前nums[i]的元素并加1
        }
        else
        {
            cnt += pre - nums[i];	//统计数量
            ++pre;	//大于就加1,直到小于更新pre值
        }
    }

    return cnt;
}
/*
模拟:[1, 1, 1]
	第一次 pre = 2 大于 nums[1] cnt统计前一个元素和后一个元素的差值(即操作数) cnt = 1;
	第二次	pre = 3 大于 nums[2] cnt = 1 + 3 - 1;
	所以最终结果为3。
*/

6. 使数组唯一的最小增量

945. 使数组唯一的最小增量

  • 题目

给你一个整数数组 nums 。每次 move 操作将会选择任意一个满足 0 <= i < nums.length 的下标 i,并将 nums[i] 递增 1

返回使 nums 中的每个值都变成唯一的所需要的最少操作次数。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-increment-to-make-array-unique
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 思路分析

要保证数组中每一个元素是唯一的增量,需要找出数组中不唯一的值,利用排序先将数组中相同的值放在一起,然后参照上一题的思路求解

  • 源码实现
int cmp(const void* a, const void *b)
{
    return *(int *)a - *(int *)b;
}
int minIncrementForUnique(int* nums, int numsSize){
    qsort(nums, numsSize, sizeof(int), cmp);

    int i;
    int pre = nums[0] + 1;
    int cnt = 0;
    for(i = 1; i < numsSize; ++i)
    {
        if(pre < nums[i])
        {
            pre = nums[i] + 1;
        }
        else
        {
            cnt += pre - nums[i];
            ++pre;
        }
    }

    return cnt;
}

7. 三角形的最大周长

976. 三角形的最大周长

  • 题目

给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的、面积不为零的三角形的最大周长。

如果不能形成任何面积不为零的三角形,返回 0

  • 思路分析

利用贪心算法思想,要想得到最大的周长的值,只需要判断该数组中最大的三个元素是否构成三角形。

  • 构成三角形的条件:任意两边之和大于第三边。三个元素任意两个组合相加判断是否大于第三边固然是可以的,但是操作复杂且时间复杂度高,不建议使用。
  • 像这种情况我们可以先进行排序,排序之后我们发现a < b < c已经得出两个结论b + c > aa + c < b,现在只需要比较a + b > c
  • 最后求最大周长的值
  • 源码实现
int cmp(const void* a, const void *b)
{
    return *(int *)a - *(int *)b; 
}

int largestPerimeter(int* nums, int numsSize){
    if(numsSize < 2)	//两个元素是无法构成三角形的
    {
        return 0;
    }

    qsort(nums, numsSize, sizeof(int), cmp);
    int i;
	//从最后的元素开始遍历,只要能构成三角形,那么结果就为最大周长
    for(i = numsSize - 1; i >= 2; --i)
    {
        if(nums[i - 2] + nums[i - 1] > nums[i])
        {
            return nums[i - 2] + nums[i - 1] + nums[i];
        }
    }
    return 0;
}

8. 救生艇

881. 救生艇

  • 题目

第 i 个人的体重为 people[i],每艘船可以承载的最大重量为 limit

每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit

返回载到每一个人所需的最小船数。(保证每个人都能被船载)。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/boats-to-save-people
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 思路分析

要求所需最小船数,就必须保证每次乘坐的是能乘坐的最大值。这种情况使用排序就比较方便对其进行求解。

  • 第一步,排序
  • 第二步,判断最大的体重和最小体重能否一起乘坐一艘船,能就一起走,不能就先让体重大的人先走
  • 第三步,返回结果
  • 源码实现
int cmp(const void* a, const void *b)
{
    return *(int *)a - *(int *)b;
}
int numRescueBoats(int* people, int peopleSize, int limit){
    //体重people[i] , 船能承载的最大体重是limit
    //同时载两人
    //求载到每一个人所需的最小船数
    qsort(people, peopleSize, sizeof(int), cmp);

    int ans = 0;	//统计船的数量
    int left = 0;
    int right = peopleSize - 1;

    while(left <= right)
    {
        if(left == right)	//只有一个人了,直接走
        {
            ++ans;
            break;
        }
        else if(people[left] + people[right] > limit) //最小的与最大的比较,如果大于就让最大的先走
        {
            ++ans;
            right--;
        }
        else
        {
            ++ans;
            right--;
            left++;
        }
    } 
    return ans;
}

9. 有效三角形的个数

611. 有效三角形的个数

  • 题目

给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。

  • 思路分析

(参照的官方思路)先排序,在二分查找有效三角形的个数。

  • 假定数组[a ,b ,c], 排序完成后a < b < c,分析数组中元素可以得出两个结论b + c > aa + c < b,然后通过二分法查找满足a + b > c的元素个数。
  • 统计mid左边到j位置的元素个数。
  • 源码实现
int cmp(int *a, int *b) {
    return *a - *b;
}

int triangleNumber(int* nums, int numsSize){
    int cnt = 0; //统计有效三角形的数量
    //先排序,后比较
    int i, j;
   qsort(nums, numsSize, sizeof(int), cmp);
    for(i = 0; i < numsSize; ++i)
    {
        for(j = i + 1; j < numsSize; ++j)
        {
            int left = j + 1;
            int right = numsSize - 1;
            int k = j;  //防止枚举a,b的出现0的时候,二分查找失败

            while(left <= right)
            {
                int mid = left + (right - left) / 2;
                if(nums[mid] < nums[j] + nums[i])
                {
                    k = mid;
                    left = mid + 1;
                }
                else
                {
                    right = mid - 1;
                }
            }
            cnt += k - j;	//统计每次循环有效三角形的个数
        }
    }
    return cnt;
}

结语

题目来源于leetcode,由博主英雄哪里出来整理

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值