双指针算法经典例题解析

目录

第一题,移动零

第二题,复写零

第三题 快乐数

第四题 盛最多容器的水

第五题.有效三角形个数

第六题 两数之和


第一题,移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int dest=-1;
        for(int cur=0;cur<nums.size();cur++)
        {
            if(nums[cur]!=0){
                swap(nums[++dest],nums[cur]);
            }
        }
    }
};

第二题,复写零

给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。

注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

解析:

从前往后进行复写会导致原来的数据被覆盖掉,所以我们考虑先找到最后一个复写的数据从后面往前面开始复写

但是在找最后一个复写的数据的时候若最后一个复写数据为0,那么dest指针可能会越界

        while(dest<arr.size()){//这样写找的cur是超出的那个数据,需要额外判断情况太麻烦
            if(arr[cur]=0){
                cur++;
                dest+=2;//步长为2就可能越界      
            }
            else{
                ++cur;
                ++dest
            }
        }
public:
    void duplicateZeros(vector<int>& arr) {
        int cur=0,dest=-1,n=arr.size();//我不知道dest的最后位置是什么,需要进行模拟所以初始化为-1
        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--]=0;
                arr[dest--]=0;
                cur--;
            }
            else{
                arr[dest--]=arr[cur--];
            }
        }

    }
};

第三题 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程结果为 1,那么这个数就是快乐数。

一个数经过这个操作以后一定会缩小取值范围,由鸽巢原理可知总是会陷入循环。

class Solution {
public:
    bool isHappy(int n) {
        int slow=n,fast=n;
        fast=0;
        while(n!=0){   
                fast+=(n%10)*(n%10);
                n/=10;
            }
        while(slow!=fast){
            int tmp1=slow;
            int tmp2=fast;
            slow=fast=0;
            while(tmp1!=0){
                slow+=(tmp1%10)*(tmp1%10);
                tmp1/=10;
            }
            while(tmp2!=0){
                fast+=(tmp2%10)*(tmp2%10);
                tmp2/=10;
            }
            tmp2=fast;
            fast=0;
            while(tmp2!=0){
                fast+=(tmp2%10)*(tmp2%10);
                tmp2/=10;
            }
        }

        if(slow==1){
            return true;
        }
        return false;
    }
};

当然我们可以观察到取每一位数的平方和多次使用,所以我们可以封装一个函数

class Solution {
public:

int all_sum(int n){
    int sum=0;
    while(n!=0){
        sum+=(n%10)*(n%10);
        n/=10;
    }
    return sum;
}
    bool isHappy(int n) {
        int slow=n,fast=all_sum(n);
        while(slow!=fast){
            slow=all_sum(slow);
            fast=all_sum(all_sum(fast));
        }
        if(slow==1) 
        return true;

        return false;
    }
};

第四题 盛最多容器的水

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/container-with-most-water

我们可以知道选小的那个杯壁向内枚举,无论如何只会使得我们的体积变小

class Solution {
public:
    int maxArea(vector<int>& height) {
        int ret=0;
        int left=0,right=height.size()-1;
        while(left<right){
            int min_high=height[left];
            if(height[left]>height[right]){
                min_high=height[right];
            }
            int tmp=min_high*(right-left);
            if(tmp>ret){
                ret=tmp;
            }
            if(height[left]>height[right]){
                right--;
            }
            else{
                left++;
            }
        }
        return ret;

    }
};

更简洁的写法

        int ret=0;
        int left=0,right=height.size()-1;
        while(left<right){
            int tmp=min(height[left],height[right])*(right-left);
            ret=max(tmp,ret);
            if(height[left]<height[right]) left++;
            else right--;
        }
        return ret;

第五题.有效三角形个数

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end());

        int ret=0,left=0,right=0;

        for(int i=nums.size()-1;i>=2;i--){
            left=0,right=i-1;

            while(left<right){
                if(nums[right]+nums[left]>nums[i]){
                    ret=ret+right-left;
                    right--;
                }
                else{
                    left++;
                }

            }
        }
        
    return ret;
    }

};

第六题 两数之和

题主错解

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> ret;
        ret.resize(2);

        sort(nums.begin(),nums.end());//排序以后你就不知道他原来的下标是多少了

        int left=0,right=nums.size()-1;

        while(left<right){
            if(nums[left]+nums[right]>target){
                right--;
            }
            else if(nums[left]+nums[right]<target){
                ++left;
            }
            else if(nums[left]+nums[right]==target){
                ret[0]=left;
                ret[1]=right;
                break;
            }

        }

        return ret;
    }
};

 利用哈希表快速查找不失为一种好方法


class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i;
        }
        return {};
    }
};

猜你喜欢

转载自blog.csdn.net/m0_74234485/article/details/131507226
今日推荐