0:题目要求:
给定一个偶数长度的数组,其中不同的数字代表着不同种类的糖果,每一个数字代表一个糖果。你需要把这些糖果平均分给一个弟弟和一个妹妹。返回妹妹可以获得的最大糖果的种类数。
示例 1:
输入: candies = [1,1,2,2,3,3]
输出: 3
解析: 一共有三种种类的糖果,每一种都有两个。
最优分配方案:妹妹获得[1,2,3],弟弟也获得[1,2,3]。这样使妹妹获得糖果的种类数最多。
示例 2 :
输入: candies = [1,1,2,3]
输出: 2
解析: 妹妹获得糖果[2,3],弟弟获得糖果[1,1],妹妹有两种不同的糖果,弟弟只有一种。这样使得妹妹可以获得的糖果种类数最多。
注意:
数组的长度为[2, 10,000],并且确定为偶数。
数组中数字的大小在范围[-100,000, 100,000]内
1:题目思路
虽然题目给出的例子中都是非递减排序的,但是我们将答案中的
Arrays.sort(candyType);
注释掉,不难发现测试样例中有不按顺序的
所以我本题的思路:
先对数组进行排序,然后消除排序后的数组中的相同元素。
这时候得到的数组中的不相同的元素的个数就是 数组中总共的糖果总数
然后 进行判别 因为是偶数个数组元素,如果 糖的总数的一半 比 糖的种类多,那么小女孩能拿到种类的显然是 糖的种类 。
即 共10个糖,种类6种,大于10/2=5;小女孩最多拿到六种中的五种
共10个糖,种类4种,小女孩最多拿到四种,返回四
2:变量设置和数据结构
经过上述分析我们不拿发现,调用Arrays类中的排序方法,有手就行。
关键是如何将排序后的数组筛选变的只有不重复元素
从而筛选后的数组长度==糖果总数。
这时候相信有些老铁就联想到了力扣
力扣中这个入门题,删除重复元素
我们直接调用快慢指针。
为什么强调他是快慢指针而不是双指针呢?
因为盯着两个指针一起走,考虑两个指针的情况
像个智障,不断地排除BUG情况,凑出来的代码勉强排除特殊情况,可以通过
上面的题力扣
例如我的第一次提交:
package leetcode.review.rereview;
/*
@author 作者 E-mail:
@version 创建时间:2021年10月31日 下午12:12:31
类说明:removeDuplicates 原地删除数组的多余元素
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
*/
public class RemoveDuplicates
{
public int removeDuplicates(int[] nums)
{
if(nums.length<=1)
{
return nums.length;//对长度0和1的数组进行处理
}
int flag=nums[0];
int slow=0;
int fast=0;
for(;fast<nums.length;fast++)
{
if(nums[fast]==flag)
{
fast++;
{
if(fast==nums.length)
{
break;
}
}
}
if(nums[fast]!=flag)
{
slow++;
flag=nums[fast];
nums[slow]=nums[fast];
}
}
return slow+1;
}
}
/*
此题我的编程思想从想法上来说还是很成熟的,风骚的双指针,快慢指针,当他等于前边已有的元素时直接fast++跳过他,
跳过之后再看下一个是否也还是跟旗帜相同,for循环和内置的fast++推动继续前进。当快指针指到不等于旗帜的数据时,交换,换位,对慢指针进行操作。但是需要考虑,当快指针指向边界并且这一整个数组都是同一个数字,如【1,1,1,1】时,会边界溢出。
计算机是非常精密和严格的。
同时需要对空数组,只有一个的数组进行特殊情况处理
其中【1,1】这种特殊情况,全是1,但是返回长度还是0,且根据程序编写的巧合性,第一次相等于flasg
fast++之后,他会返回for再执行语句三fast++,这时候fast就进不来了!
*/
//[1,1,2]
//[0,0,1,1,1,2,2,3,3,4]
//[0,0,1,1,1,2,2,3,3,4,55,55,66]
//[]
//[1]
//[1,2]
//[1,2]
//[1,1,1]
//执行出错信息:
//java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
// at line 19, Solution.removeDuplicates
// at line 54, __DriverSolution__.__helper__
// at line 84, __Driver__.main
//最后执行的输入:
//[1,1,1]
14点01分
从2021.10.31日 12点开始
第一题是删除重复元素
盯着两个指针一起走
像个智障,不断地排除BUG情况,凑出来的
并且把这种错误思想引入了今天做的第二题移动零
其实没必要盯着两个指针
现针对第一题重做!
package leetcode.daily;
/*
@author 作者 E-mail:
@version 创建时间:2021年10月31日 下午2:03:47
类说明:第十四天,对本日做的第一题重做。
*/
public class Daily13_1_2
{
public int solution(int[] nums)
{
if(nums.length<2)
{
return nums.length;
}
int slow=0;
int fast=0;
int flag=nums[0];
while(fast<nums.length)
{
if(nums[fast]==flag)
{
fast++;
//此处还要加,防止fast出界
{if(fast==nums.length) break;}
}
if(nums[fast]!=flag)
{
nums[slow++]=nums[fast];
flag=nums[fast];
}
}
return slow+1;
}
}
class Solution
{
public int removeDuplicates(int[] nums)
{
if(nums.length<2)
{
return nums.length;
}
int slow=1;
int fast=0;
int flag=nums[0];
for(;fast<nums.length;fast++)
{
if(nums[fast]!=flag)
{
nums[slow++]=nums[fast];
flag=nums[fast];
}
}
return slow;
}
}
双指针里for和while是一样的,记住模板稍加更改就可
这样我们通过刷题整理得到了双指针的框架:,
双指针,一左一右:left||right
快慢指针,一快一慢:slow和fast
两种循环方式:
while和for 其中用while时,
需要在循环内对fast(right)指针自加
for循环中(依赖for自加即可)
所需要空间为原数组
3:代码求解:
class Solution
{
public int distributeCandies(int[] candyType)
{
int bornlen=candyType.length;
if(bornlen==2)
{
return 1;
}
Arrays.parallelSort(candyType);
int left=1;
int right=0;
int flag=candyType[0];
for(;right<bornlen;right++)
{
if(candyType[right]!=flag)
{
flag=candyType[right];
candyType[left++]=candyType[right];
}
}
if(left>bornlen/2)
{
return bornlen/2;
}
return left;
}
}
4:快慢指针总结:
为何叫做快慢指针,而不叫双指针
因为如
#977 有序数组的平方 |
这种题,用到的双指针,一左一右,两个一个在数组0位置,一个在数组末尾,需要同时盯两个
指针力扣
但是本题,快慢指针一起从头开始,盯着一个即可
for(int right=0;right<数组名.length;fast++)
{
if(不满足条件)
{
看题目要求执不执行操作
//如果是while循环,此处fast应该自加,推动fast快指针去检查下一个位置
}
if(满足条件)
{
//通常是对慢指针进行操作,如换位,删除,进位,移动,退位
//之后慢指针位置变化(一般是自加)
}
}
5:给老铁们留了几道快慢指针的题目练手
- 26.删除排序数组中的重复项
- 283.移动零
- 844.比较含退格的字符串
- 977.有序数组的平方
针对移动零,以下是刷题时候的笔记和源码:-
public void moveZeroes(int[] nums) { //本题设置两个指针,快慢指针,慢指针停在0,快指针从0出发往后找非0, //找到之后,换位置,这样从头开始,保证了原有的顺序性,然后这样避免了那种两个指针一头一尾的,换过来没顺序。 int fast=0; int slow=0; int n=nums.length; for(;fast<n;fast++) { public void moveZeroes(int[] nums) { //本题设置两个指针,快慢指针,慢指针停在0,快指针从0出发往后找非0, //找到之后,换位置,这样从头开始,保证了原有的顺序性,然后这样避免了那种两个指针一头一尾的,换过来没顺序。 int fast=0; int slow=0; int n=nums.length; for(;fast<n;fast++) { if(nums[fast]==0) { fast++;//此处其实不用管,因为你的fast无非就两种情况,fast为0,fast不为0 //此处判断fast不为0,且++,出去了还要再加,还要防止下面对此处的fast+1之后的新fast判断,且看出不出bug,不如直接不管 } if(nums[fast]!=0) {//此处更加不对,你的意思就是从0开始一个一个判断,应该是交换而不是简单的覆盖。 nums[slow]=nums[fast];//直接swap交换操作,交换之后,再对慢指针进行操作。这种快慢指针就是应该紧盯着一个指针进行操作。 //并且为什么叫做快慢指针不叫作双指针,感觉它的意义就在于从头开始,并且将快的边界设置在数组长度,紧盯着快的,符合条件对慢指针操作!!! //像上一个题删除重复元素,同时紧盯快慢指针,容易出bug且没有模板通用性! slow++; n--; } if(nums[slow]!=0) { slow++; } } for(int i=slow;i<nums.length;i++) { nums[i]=0; } if(nums[fast]!=0) { nums[slow]=nums[fast]; slow++; n--; } if(nums[slow]!=0) { slow++; } } for(int i=slow;i<nums.length;i++) { nums[i]=0; } //其实快慢指针就是,模板就是 从0开始,一快一慢,快的去找,找到操作,紧盯着一个指针就行!
#其他
小菜鸡我正在力扣刷题。
双指针,快慢指针,螺旋数组等,日拱一卒不断进步,
觉得写得还不错的老铁点个关注,相互交流学习呀!