【优选算法】(第六篇)

目录

最⼤连续1的个数III(medium)

题目解析

讲解算法原理

编写代码

将x减到0的最⼩操作数(medium)

题目解析

讲解算法原理

编写代码


最⼤连续1的个数III(medium)

题目解析

1.题目链接:. - 力扣(LeetCode)

2.题目解析

给定⼀个⼆进制数组 nums 和⼀个整数 k ,如果可以翻转最多 k 个 0 ,则返回数组中连续 1 的最⼤个数。
⽰例1:
输⼊: nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出: 6
解释:
[1,1,1,0,0,1,1,1,1,1,1]
红⾊数字从 0 翻转到 1 ,最⻓的⼦数组⻓度为 6 。
⽰例2:
输⼊: nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出: 10
解释:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
红⾊数字从 0 翻转到 1 ,最⻓的⼦数组⻓度为 10 。

讲解算法原理

解法(滑动窗⼝):
算法思路:
不要去想怎么翻转,不要把问题想的很复杂,这道题的结果⽆⾮就是⼀段连续的 1 中间塞了 k 个 0 嘛。
因此,我们可以把问题转化成:求数组中⼀段最⻓的连续区间,要求这段区间内 0 的个数不超过 k 个。
既然是连续区间,可以考虑使⽤「滑动窗⼝」来解决问题。
算法流程:
a. 初始化⼀个⼤⼩为 2 的数组就可以当做哈希表 hash 了;初始化⼀些变量 left = 0 ,
right = 0 , ret = 0 ;
b. 当 right ⼩于数组⼤⼩的时候,⼀直下列循环:
i. 让当前元素进⼊窗⼝,顺便统计到哈希表中;
ii. 检查 0 的个数是否超标:
• 如果超标,依次让左侧元素滑出窗⼝,顺便更新哈希表的值,直到 0 的个数恢复正
常;
iii. 程序到这⾥,说明窗⼝内元素是符合要求的,更新结果;
iv. right++ ,处理下⼀个元素;
c. 循环结束后, ret 存的就是最终结果。

如图所示:

left和right指针只需把整个数组遍历一遍,并且right进行一次判定之后不需要返回,因为此时便是最长的数目,只需要left进行++,大大节省了时间复杂度 

编写代码

c++算法代码:

class Solution
{
public:
 int longestOnes(vector<int>& nums, int k) 
 {
 int ret = 0;
 for(int left = 0, right = 0, zero = 0; right < nums.size(); right++)
 {
 if(nums[right] == 0) zero++; // 进窗⼝
 while(zero > k) // 判断
 if(nums[left++] == 0) zero--; // 出窗⼝
 ret = max(ret, right - left + 1); // 更新结果
 }
 return ret;
 }
};

java算法代码:

class Solution
{
 public int longestOnes(int[] nums, int k) 
 {
 int ret = 0;
 for(int left = 0, right = 0, zero = 0; right < nums.length; right++)
 {
 if(nums[right] == 0) zero++; // 进窗⼝
 while(zero > k) // 判断
 if(nums[left++] == 0) zero--; // 出窗⼝
 ret = Math.max(ret, right - left + 1); // 更新结果
 }
 return ret;
 }
}

将x减到0的最⼩操作数(medium)

题目解析

1.题目链接:. - 力扣(LeetCode)

2.题目解析

给你⼀个整数数组nums和⼀个整数x。每⼀次操作时,你应当移除数组nums最左边或最右边的元素,然后从x中减去该元素的值。请注意,需要修改数组以供接下来的操作使⽤。
如果可以将x恰好减到0,返回最⼩操作数;否则,返回-1。
⽰例1:
输⼊:nums=[1,1,4,2,3],x=5
输出:2
解释:最佳解决⽅案是移除后两个元素,将x减到0。
⽰例2:
输⼊:nums=[5,6,7,8,9],x=4
输出:-1
⽰例3:
输⼊:nums=[3,2,20,1,1,3],x=10
输出:5
解释:最佳解决⽅案是移除后三个元素和前两个元素(总共5次操作),将x减到0。
提⽰:
1<=nums.length<=10^5
1<=nums[i]<=10^4
1<=x<=10^9

讲解算法原理

解法(滑动窗⼝):
算法思路:
题⽬要求的是数组「左端+右端」两段连续的、和为 x 的最短数组,信息量稍微多⼀些,不易理清思路;我们可以转化成求数组内⼀段连续的、和为 sum(nums) - x 的最⻓数组。此时,就是熟悉的「滑动窗⼝」问题了。
算法流程:
a. 转化问题:求 target = sum(nums) - x 。如果 target < 0 ,问题⽆解;b. 初始化左右指针 l = 0 , r = 0 (滑动窗⼝区间表⽰为 [l, r) ,左右区间是否开闭很重
要,必须设定与代码⼀致),记录当前滑动窗⼝内数组和的变量 sum = 0 ,记录当前满⾜条件数组的最⼤区间⻓度 maxLen = -1 ;
c. 当 r ⼩于等于数组⻓度时,⼀直循环:
i. 如果 sum < target ,右移右指针,直⾄变量和⼤于等于 target ,或右指针已经移到
头;
ii. 如果 sum > target ,右移左指针,直⾄变量和⼩于等于 target ,或左指针已经移到
头;
iii. 如果经过前两步的左右移动使得 sum == target ,维护满⾜条件数组的最⼤⻓度,并
让下个元素进⼊窗⼝;
d. 循环结束后,如果 maxLen 的值有意义,则计算结果返回;否则,返回 -1 。

如图所示:
 计算两边的和并不是容易的事情,那么就可以转变为中间的区间和两边区间的和,此时只需要计算中间区间的最长可能性,那么也就是两边所组成的最短的解法,便是“正难则反”的解法,从另一边思考这个问题,此时便可以很好的解决时间复杂度问题

编写代码

c++算法代码:

class Solution
{
public:
 int minOperations(vector<int>& nums, int x) 
 {
 int sum = 0;
 for(int a : nums) sum += a;
 int target = sum - x;
 // 细节问题
 if(target < 0) return -1;
 int ret = -1;
 for(int left = 0, right = 0, tmp = 0; right < nums.size(); right++)
 {
 tmp += nums[right]; // 进窗⼝
 while(tmp > target) // 判断
 tmp -= nums[left++]; // 出窗⼝
 if(tmp == target) // 更新结果
 ret = max(ret, right - left + 1);
 }
 if(ret == -1) return ret;
 else return nums.size() - ret;
 }
};

java算法代码:

class Solution
{
 public int minOperations(int[] nums, int x) 
 {
 int sum = 0;
 for(int a : nums) sum += a;
 int target = sum - x;
 // 处理细节
 if(target < 0) return -1;
 int ret = -1;
 for(int left = 0, right = 0, tmp = 0; right < nums.length; right++)
 {
 tmp += nums[right]; // 进窗⼝
 while(tmp > target) // 判断
 tmp -= nums[left++]; // 出窗⼝
 if(tmp == target) 
 ret = Math.max(ret, right - left + 1); // 更新结果
 }
 if(ret == -1) return ret;
 else return nums.length - ret;
 }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值