算法---双指针一

算法原理

双指针

常见的双指针有两种形式,一种是对撞指针,一种是左右指针

对撞指针:一般用于顺序结构中,也称左右指针,对撞指针一般从两端向中间移动,终止条件一般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环)

快慢指针:又叫龟兔赛跑算法,基本思想就是使用两个移动速度不同的指针在数组或链表等结构上移动(我们在数据结构学习链表解决链表有环问题的时候用到了这种算法)

题目解析

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;
    }
相关推荐
girl-07261 天前
2025.12.28代码分析总结
算法
NAGNIP1 天前
GPT-5.1 发布:更聪明,也更有温度的 AI
人工智能·算法
NAGNIP1 天前
激活函数有什么用?有哪些常用的激活函数?
人工智能·算法
元亓亓亓1 天前
LeetCode热题100--416. 分割等和子集--中等
算法·leetcode·职场和发展
BanyeBirth1 天前
C++差分数组(二维)
开发语言·c++·算法
xu_yule1 天前
算法基础(数论)—算法基本定理
c++·算法·算数基本定理
CoderCodingNo1 天前
【GESP】C++五级真题(结构体排序考点) luogu-B3968 [GESP202403 五级] 成绩排序
开发语言·c++·算法
YGGP1 天前
【Golang】LeetCode 32. 最长有效括号
算法·leetcode
自然常数e1 天前
字符函数和字符串函数
c语言·算法·visual studio