ダブルポインタアルゴリズムの古典的な例の分析

目次

質問 1、ゼロを移動する

質問 2、ゼロをコピーする

質問 3: ハッピーナンバー

質問 4: 最大の容器にはどのくらいの水が入りますか?

質問 5. 有効な三角形の数

質問 6 2 つの数字の和


質問 1、ゼロを移動する

array が与えられた場合 、 ゼロ以外の要素の相対的な順序を維持しながら、すべてを配列の末尾に移動する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]);
            }
        }
    }
};

質問 2、ゼロをコピーする

固定長の整数配列 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--];
            }
        }

    }
};

質問 3: ハッピーナンバー

数値 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;
    }
};

質問 4: 最大の容器にはどのくらいの水が入りますか?

長さ n の整数配列の高さを指定します。垂直線は n 本あり、i 番目の線の 2 つの端点は (i, 0) と (i, height[i]) です。

X 軸と合わせて最も多くの水を保持できる容器を形成する 2 本の線を見つけます。

コンテナが保存できる水の最大量を返します。

出典: 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;

質問 5. 有効な三角形の数

非負の整数を含む配列を指定すると nums 、三角形の 3 つの辺を形成できるトリプルの数を返します。

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;
    }

};

質問 6 2 つの数字の和

質問の解釈が間違っている

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