【贪心算法】贪心算法七

【贪心算法】贪心算法七

贪心算法七

在这里插入图片描述
点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.整数替换

题目链接:397. 整数替换

题目描述:

在这里插入图片描述

算法原理:

解法一:模拟(递归 + 记忆化搜索)

假设n = 18,我们要干的事情是把18变成1最小的步数。因为18是一个偶数只能除2变成9,拿到9这个数字,要干的其实也是一件相同的事情,要把9变成1最小的步数。

此时这里就出现了重复的子问题,大问题是18变成1的最小步数,18/2=9后就从了9变成1的最小步数的相同问题。因此我们可以把重复子问题拿到设计出函数头
int dfs(int n) 给一个整数n返回n变成1的最小步数。函数体 其实就是题目给的,如果n是偶数/2,如果n是奇数要么+1,要么-1我们求得是最小步数所以是 min(dfs(n-1),dfs(n+1)),递归出口 当 n == 1是之间返回0就行了。

在这里插入图片描述


在递归过程中发现大量重复,就可以用记忆化搜索,建一个数组,但是这道题的数据范围是1 <= n <= 2^31 - 1,我们要开这么大的空间肯定不行,因此搞一个hash<int,int> 第一个参数对应数字n,第二个参数对应这个数变成1的最小步数。

在这里插入图片描述
classSolution{  unordered_map<int,int> hash;public:intintegerReplacement(int n){ returndfs(n);}// 递归intdfs(longlong n)// 细节问题 数据范围1 <= n <= 2^31 - 1 加1会越界{ if(n ==1){ return0;}if(n %2==0)// 如果是偶数 { return1+dfs(n /2);}else{ return1+min(dfs(n -1),dfs(n +1));}}// 记忆化搜索intdfs(longlong n){ if(hash.count(n)){ return hash[n];}if(n ==1){  hash[1]=0;return hash[1];}if(n %2==0){  hash[n]=1+dfs(n /2);return hash[n];}else{  hash[n]=1+min(dfs(n -1),dfs(n +1));return hash[n];}}};

解法二:贪心

补充知识:二进制

  1. 偶数:二进制表示中最后一位是 0
  2. 奇数:二进制表示中最后一位是 1
  3. /2 操作:二进制表示中统一右移一位

我们这里研究的都是整数。
前两个可以自己举例看看。我们看最后一个

在这里插入图片描述


接下来想我们的贪心策略:

如果n是偶数没法贪,只能执行/2操作

是奇数就可以贪,要么执行+1,要么执行-1操作。
在模拟解法我们就是试试+1操作和-1操作看谁最小,但是如果在没有试之前就已经知道是+1好还是-1好,直接让奇数沿着较好的选择走,就可以舍去一个选择,那我们的时间复杂度会变得更优。

所以我们的贪心就是判断是+1好还是-1好。

如何判断?分情况讨论:

奇数的二进制最后一位是0,所以我们可以把奇数分为两大类

第一类:前面二进制位是 …,最后两个二进制位是 01

第二类:前面二进制位是 …,最后两个二进制位是 11

其中第一类我们默认 n > 1,也就是说 … 有1,如果没有1的话就是00…01了,直接返回即可。第二类默认 n > 3。

如果是 …01,接下来要么执行+1操作,要么执行-1操作。 +1操作会变成 …10,-1操作会变成 …00,那到底那个操作好呢? +1和-1操作都会变成偶数,偶数只能执行/2操作。假设…01是 …10001,执行+1操作会变成10010在执行/2操作会变成1001,执行-1操作会变成10000在执行/2操作会变成1000。这个时候就可以看出那个操作好了,肯定是-1操作好,因为1000我们可以一直/2操作尽快得到1,1001还需要在+1和-1操作在/2操作。

所以是奇数二进制最后两位是01,就执行-1操作,然后/2操作,会比较快得到1。

在这里插入图片描述

如果是 …11,接下来也是要么执行+1操作,要么执行-1操作,分析过程和上面一样。

在这里插入图片描述

但是n > 3这里有一个意外,当 n = 3的时候,我们需要特殊讨论,n = 3,二进制位前面都是0,后面虽然也是11。但是这里我们执行-1操作得到…10,然后在执行/2操作,直接就变成1了。这个和选择是不一样的。如果执行+1操作就会多一步/2操作。

在这里插入图片描述


我们这个贪心不用证明,分类讨论过程本身就是对这个贪心的证明。

那如何写代码呢?

如何判断二进制最后两位是01还是11呢?
拿n%4就可以了,因为n是奇数%4只能得到1和3,如果是1就是01情况,如果是3就是11情况。

classSolution{  unordered_map<int,int> hash;public:intintegerReplacement(int n){ int ret =0;while(n >1){ if(n %2==0){  n /=2;++ret;}else{ if(n ==3){  ret +=2; n =1;}elseif(n %4==1){  n 

Read more

【算法】双指针(二)-复写零

【算法】双指针(二)-复写零

目录 一、题目介绍 二、双指针原理 1.当前维护指针 1.2维护要求 (1)不能覆盖掉未产效元素 三、提交代码 一、题目介绍 1089. 复写零 - 力扣(LeetCode) 给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。 注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。 示例 1: 输入:arr = [1,0,2,3,0,4,5,0] 输出:[1,0,0,2,3,0,0,

By Ne0inhk
【LeetCode原地复写零】:双指针+逆向填充,O(n)时间O(1)空间最优解!

【LeetCode原地复写零】:双指针+逆向填充,O(n)时间O(1)空间最优解!

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:Java.数据结构 【前言】 本文聚焦 LeetCode“原地复写零”经典题目,核心需求是在固定长度数组中复写每个 0并右移其余元素,且需满足原地修改、不使用额外数组空间的约束。正向遍历易导致后续元素被覆盖,为此本文详解双指针+逆向填充的优雅解法,高效破解这一核心难点。 文章目录: * 一、复写零 * 二、思路分析 * 1.找到复写的最后一个数 * 2.开始从后往前复写 * 三、代码展示 * 四、时间和空间复杂度分析 * 五、总结 一、复写零 二、思路分析 复写零这道题是让在原数组修改,如果从前向后遍历,后面的元素会被覆盖,所以我们要找到被复写的最后一个元素,然后从后往前复写。运用双指针+逆向填充 1.

By Ne0inhk
【数据结构】长幼有序:树、二叉树、堆排序与TOP-K问题的层次解析(含源码)

【数据结构】长幼有序:树、二叉树、堆排序与TOP-K问题的层次解析(含源码)

为什么我们要学那么多的数据结构?这是因为没有一种数据结构能够去应对所有场景。我们在不同的场景需要选择不同的数据结构,所以数据结构没有好坏之分,而评估数据结构的好坏要针对场景,就如我们已经学习的结构而言,如果在一种场景下我们需要频繁地对头部进行插入删除操作,那么这个时候我们用链表;但是如果对尾部进行插入删除操作比较频繁,那我们用顺序表比较好。 因此,不同的场景我们选择不同的数据结构 文章目录 * 一、树 * 1.树的基本概念 * 2.树相关术语 * 3.树的表示 * 4.树形结构实际运用场景 * 二、二叉树 * 1. 概念与结构 * 现实中的二叉树 * 特殊的二叉树 * 二叉树的性质 * 二叉树存储结构 * 三、手动模拟实现顺序二叉树——堆 * 1.堆的结构 * 2.初始化 * 3.销毁 * 4.向上调整算法 * 5.插入数据 * 6.判空 * 7.求size * 8.向下调整算法

By Ne0inhk