LeetCode Hot Topic 100 [cpp] ソリューション (1) ハッシュ テーブルとダブル ポインタ

質問リストのリンク: LeetCode Hot question 100

1. 2 つの数値の合計

leetcode の質問リンク
解決策 1: 暴力的な列挙
時間計算量: O ( n 2 ) O(n^2)O ( n2 )

class Solution {
    
    
public:
    vector<int> twoSum(vector<int>& nums, int target) {
    
    
        vector<int> v;
        for (int i = 0; i < nums.size() - 1; i ++) {
    
    
            for (int j = i + 1; j < nums.size(); j ++) {
    
    
                if (nums[i] + nums[j] == target) {
    
    
                    v.push_back(i), v.push_back(j);
                }
            }
        }
        return v;
    }
};

解決策 2: ハッシュ テーブルの
時間計算量: O ( n ) O(n)O ( n )

ハッシュ テーブルを使用して、ターゲットと x の差の添え字を保存します。ここでは cpp のものを使用しますunordered_map。その検索効率はO (1) O(1)です。

キーを見つけるための補助unordered_mapメソッド: count() は、特定のキーが存在する場合は 1 を返し、特定のキーが存在しない場合は 0 を返します。

class Solution {
    
    
public:
    vector<int> twoSum(vector<int>& nums, int target) {
    
    
        unordered_map<int, int> heap; // heap存 <差值, 下标>
        for (int i = 0; i < nums.size(); i ++) {
    
    
            int r = target - nums[i];
            if (heap.count(r)) {
    
     // count方法判断key存不存在
                return {
    
    heap[r], i};
            }
            heap[nums[i]] = i; // 记录这个数的下标
        }
        return {
    
    };
    }
};

49. アルファベットのアナグラムのグループ化

leetcodeの質問リンク

解決策:
文字アナグラムは、特定の文字列に同じ文字が含まれており、各文字が同じ回数出現することを意味します。文字アナグラムは、ソート後にまったく同じ文字列として理解することもできます。

この問題の解決策は後者 (ソート) を使用します。2 つの文字列がアナグラムの場合、ソート後は同じでなければなりません。ハッシュ テーブルを使用すると、文字列のベクトルを維持し、文字列を同じ順序で記録できます。時間のボトルネックは並べ替えにあり、並べ替えの複雑さはnlogn nlognですログインます _ _
時間計算量:O (nlogn) O(nlogn)O ( nログn ) _ _

class Solution {
    
    
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
    
    
        unordered_map<string, vector<string>> hash;
        for(auto& str: strs) {
    
    
            string nstr = str; // nstr为排序过后的新字符串
            sort(nstr.begin(), nstr.end());
            hash[nstr].push_back(str);
        }

        vector<vector<string>> res;
        for(auto& item: hash) {
    
    
            res.push_back(item.second);
        }
        return res;
    }
};

128. 最長連続シーケンス

leetcodeの質問リンク

解決策: ハッシュテーブル
毎回シーケンスの最小値から列挙していくが、連続していれば反復子は止まらない++。たとえば、最小値が x で、その後に x+1、x+2、...、y が続く場合、この連続シーケンスの長さはどれくらいでしょうか。y - x +1 という長さです。検索を高速化するために、すべての要素をunordered_setハッシュ テーブル S に保存します。もう 1 つの質問があります。シーケンスの始まりをどのように判断するかということです。x がハッシュ テーブル S に存在し、x - 1 が存在しない場合、x はシーケンスの始まりであると言います。

このハッシュ テーブルを補足すると、
unordered_set要素の重複が除去され、ソートされなくなり、その値によって要素を迅速に取得できるようになります。
時間計算量: O ( n ) O(n)O ( n )

class Solution {
    
    
public:
    int longestConsecutive(vector<int>& nums) {
    
    
        unordered_set<int> S;
        // 构造hash表:将所有元素插入到set中
        for(auto x: nums) S.insert(x);
        
        int res = 0;
        for(auto x : S) {
    
    
            // 枚举序列的最小值x
            if(S.count(x) && !S.count(x - 1)) {
    
    
                int y = x;
                // 如果是连续的序列,y一直++
                while(S.count(y + 1)) {
    
    
                    y ++;
                }
                res = max(res, y - x + 1); // 更新序列长度
            }
        }
        return res;
    }
};

283. ゼロの移動

leetcodeの質問リンクの
解決策: ダブルポインター

前者のポインタ x は先頭から配列を走査し、後者のポインタ k はゼロ以外の要素を格納します。k は 0 から始まり、徐々に 1 ずつ増加します。すべての非ゼロ要素が前方に移動されると、k は最初のゼロ位置を指し、それ以降ゼロを列挙できるようになります。

時間計算量: O ( n ) O(n)O ( n )

class Solution {
    
    
public:
    void moveZeroes(vector<int>& nums) {
    
    
        int k = 0; // k是后一个指针,用于存放非零的数
        for (auto x : nums) {
    
    
            if (x) {
    
    
                nums[k] = x;
                k ++;
            }
        }
        // 把数组后面的位置补零
        while (k < nums.size()) {
    
    
            nums[k] = 0;
            k ++;
        }
    }
};

11. 最も多くの水を入れる容器

leetcode 質問リンク
解決策: ダブルポインター
2 つのポインター i と j によって形成される水槽の面積は、短いプレートによって決まります。面積の計算式は次のとおりです:
S = min ( h [ i ] , h [ j ] ) ∗ ( j − i ) S = min(h[i], h[j]) * (j - i)S=min ( h [ i ] ,h [ j ])( j

各状態において、i と j の 2 つのポインタを中央に 1 つ移動すると、下辺が短くなりますが、その領域を大きくするにはどうすればよいでしょうか。毎回ショートボードを中央に近づける
ことによってのみ、長方形の高さが高くなり、面積が大きくなります。次の状態の高さがショートボードよりも高くなる可能性があるからです。移動中はその都度エリアの最大値を記録します。leetcodeの問題解決方法を参照してください

時間計算量: O ( n ) O(n)O ( n )

class Solution {
    
    
public:
    int maxArea(vector<int>& height) {
    
    
        int res = 0;
        for (int i = 0, j = height.size() - 1; i < j;) {
    
    
            res = max(res, min(height[i], height[j]) * (j - i));
            // 每次短边向中间移动
            if (height[i] < height[j]) i ++;
            else j --;
        }
        return res;
    }
};

15. 3 つの数字の合計

leetcodeの質問リンク

解決策: ソート + ダブルポインター

配列を小さいものから大きいものに並べ替えます。配列の最初のインデックスiiの走査を開始します。i は3 つの数値のうちの最初の数値を表します。この数値を固定してから、ダブル ポインター演算を使用します (j と kj と kjk ) nums [ i ] + nums [ j ] + nums [ k ] ≥ 0 、 i < j < k nums[i] + nums[j] + nums[k] \ge 0 となるような他の 2 つの数値を見つけますi < j < k数値[ i ] _ _ _+数値[ j ] _ _ _+数値[ k ] _ _ _0 <j<kをできるだけ小さくしながら、 k を変更します (k は最後のポインターで、3 つの数値の最大値を指し、右から左に移動します)。上記の状況で 3 つの数値の合計が 0 に等しい状況を見つけます。

さらに、列挙に重複がないことを確認する必要があります (例: nums = [ − 1 , − 1 , − 1 , − 1 , 2 ] nums = [-1, -1, -1, -1, 2]_ _=[ 1 1 1 1 2 ]、それを一度だけ出現させるにはどうすればよいですか[ − 1 , − 1 , 2 ] [-1, -1, 2][ 1 1 2 ]nums [ i ] = = nums [ i − 1 ]列挙中に nums[i] == nums[i-1] が表示される数値[ i ] _ _ _==数値[ i _ _ _1 ] をスキップして、直接ii にi ++、までnums [ i ] ≠ nums [ i − 1 ] nums[i] \ne nums[i-1]数値[ i ] _ _ _=数値[ i _ _ _1 ]nums [ j ] nums[j]num s [ j ]について同様です。

時間計算量: O ( n 2 ) O(n^2)O ( n2 )、ソートはO (nlogn)、O(nlogn)、O ( n log n ) 外側のループ(i) (i )i O ( n ) O(n) O ( n )、内部ダブル ポインター ループj と kj と kjkO ( n ) O(n)O ( n )、乗算はO ( n 2 ) O(n^2)O ( n2 )

基準溶液:acwing溶液

class Solution {
    
    
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
    
    
        vector<vector<int>> res;
        if (nums.size() < 3) return res;
        sort(nums.begin(), nums.end());

        for (int i = 0; i < nums.size(); i ++) {
    
    
            if (i > 0 && nums[i] == nums[i - 1]) continue;
			// 双指针:j是头指针,k是尾指针,两者向中间靠拢
            for (int j = i + 1, k = nums.size() - 1; j < k; j ++) {
    
    
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                // 找到3数之和 ≥ 0 的最小的位置, k是3数之和≥0的最前面的位置
                while (j < k - 1 && nums[i] + nums[j] + nums[k - 1] >= 0) k --;

                if (nums[i] + nums[j] + nums[k] == 0)
                    res.push_back({
    
    nums[i], nums[j], nums[k]});
            }
        }

        return res;
    }
};

42. 雨水を受ける

leetcodeの質問リンク

解決策: 2 つのポインターを使用して、総面積 (土地面積) を計算します。総面積を計算する場合、各行の面積が累積されます。
配列の合計には、cpp の累積関数が使用されます。
参考ソリューション:雨水の捕集

class Solution {
    
    
public:
    int trap(vector<int>& height) {
    
    
        int length = height.size();
        if (length < 3) return 0;
        int l = 0, r = length - 1;
        // 前一次计算时的高度
        int preHeight = 0;
        // 陆地 + 雨水的总面积
        int totalArea = 0;
        // 陆地面积:数组所有数求和
        int landArea = 0;
        // accumulate 是cpp累加函数
        landArea = accumulate(height.begin(), height.end(), 0);

        while (l < r) {
    
    
            while (l < r && height[l] <= preHeight) l ++;
            while(l < r && height[r] <= preHeight) r --;

            // 每一层的面积:高度差 x 宽度
            totalArea += (min(height[l], height[r]) - preHeight) * (r - l + 1);

            preHeight = min(height[l], height[r]);
        }

        return totalArea - landArea;
    }
};

おすすめ

転載: blog.csdn.net/shizheng_Li/article/details/132639641