算法原理
双指针
常见的双指针有两种形式,一种是对撞指针,一种是左右指针
对撞指针:一般用于顺序结构中,也称左右指针,对撞指针一般从两端向中间移动,终止条件一般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环)
快慢指针:又叫龟兔赛跑算法,基本思想就是使用两个移动速度不同的指针在数组或链表等结构上移动(我们在数据结构学习链表解决链表有环问题的时候用到了这种算法)
题目解析
1.移动零https://leetcode.cn/problems/move-zeroes/description/
https://leetcode.cn/problems/move-zeroes/description/
题目描述:
将全部的0移动到数组最后,且其他元素的相对位置保持不变
算法原理:数组划分
我们的目的是将数组分为两个区间 一个是非0区间,一个是0并且非零元素的相对位置不变,最直接的方法是遍历移动,即遇到0我们去找下一个非0然后将这两个元素进行位置交换
那么我们可以使用两个指针:1.cur 扫描遍历数组 2.dest:已经处理的区间内,非零元素的最后一个位置
【0,dest】都是非0 【dest+1,cur-1】都是0
cur遇到非0停止,将cur与dest+1进行交换,然后cur++,dest++
代码实现:
public void moveZeroes(int[] nums) {
int cur=0,dest=-1;
while(cur<nums.length){
if(nums[cur]!=0){
dest++;
int tmp=nums[dest];
nums[dest]=nums[cur];
nums[cur]=tmp;
}
cur++;
}
}
2.复写0
https://leetcode.cn/problems/duplicate-zeros/description/
题目描述
给定一个长度固定的数组,将每个零复写一遍,并将其余元素向右平移
算法原理
如果我们从左向右的进行复写的话,这会导致我们的0覆盖掉本来应该存在的数,所以我们从右向左进行复写,从右向左复写的时候我们要先找到最后一个需要复写的数,因此我们的大体流程分为两步:1.找到最后一个复写的数 2.然后从右向左进行复写操作
注意实现:
1.如何找到最后一个要复写的数?
当cur<n的时候,一直进行以下循环 :
如果nums【cur】==0,dest+2,不是的话dest+1
如果dest到结束位置就结束循环
如果没有cur++继续判断
2.判断dest是否越界到n的位置

如果越界的话,执行下面几步:1.n-1改成0 2.cur向前移动一步 3.dest 向前移动两步
代码实现
1.先找到最后一个要复写的数
2."从后向前"完成复写操作
public void duplicateZeros(int[] arr) {
int cur=0,dest=-1,n=arr.length;
while(cur<n){
if(arr[cur]==0){
dest+=2;
}else{
dest++;
}
if(dest>=n-1){
break;
}
cur++;
}
if(dest==n){
arr[n-1]=0;
dest-=2;
cur--;
}
while(cur>=0){
if(arr[cur]!=0){
arr[dest--]=arr[cur--];
}else{
arr[dest--]=0;
arr[dest--]=0;
cur--;
}
}
}
3.快乐数
https://leetcode.cn/problems/happy-number/description/
题目描述
什么是快乐数?
每一次将这个数替换成每个位置上的数的平方和,最后能变成1的正整数
算法原理
因为这个要么是最后变为1,要么会进入一个循环,所以我们要做的是判断环是否存在
如果题目没有告诉我们会进入循环,我们如何能判断有环的存在呢?
这会利用到鸽巢原理(抽屉原理)(n个巢,(n+1)个鸽子,至少有一个鸽巢中鸽子数大于1)

判断环的存在我们已经很熟悉了,用到快慢指针
代码实现
public boolean isHappy(int n) {
int slow=n,fast=bitSum(n);
while(slow!=fast){
slow=bitSum(slow);
fast=bitSum(bitSum(fast));
}
if(slow==1){
return true;
}else{
return false;
}
}
public int bitSum(int n){
int sum=0;
while(n!=0){
int s=n%10;
sum+=s*s;
n/=10;
}
return sum;
}
4.盛最多水的容器
https://leetcode.cn/problems/container-with-most-water/description/
题目描述
给定一个长度为n的整数数组height有n条垂线,第i条最大高度为数组内对应下标的数
目标:找出其中两条线,使他们与x轴共同构成的容器可以容纳最多的水(不能倾斜容器(木桶效应))
算法原理
解法一:暴力枚举(两层for循环) 时间复杂度过高
解法二:在暴力枚举的基础上进行优化,v=sh,(s相当于数组下标的差,h为两条线对应的最小值)
我们在数组两端固定left,right先算出此时的v,并记录
然后比较left和right对应的值,如果left大,right左移;如果right大,left右移
代码实现
public int maxArea(int[] h) {
int left=0,right=h.length-1;
int ret=0;
while(left<right){
int v=Math.min(h[left],h[right])*(right-left);
ret=Math.max(ret,v);
if(h[left]<h[right]){
left++;
}else{
right--;
}
}
return ret;
}