今日学んだ記事とビデオへのリンク
454 記事リンク:リンク
454 動画説明リンク:リンク
383 記事リンク:リンク
383 動画説明15
記事リンク:リンク
15 動画説明リンク:リンク
18 記事リンク:リンク
18 動画説明リンク:リンク
問題 454. 4 つの数のたし算 II
最初にタイトルを見てください
タイトルの説明: 4 つの整数配列 nums1、nums2、nums3、および nums4 があり、配列の長さが n である場合、タプル (i、j、k、l) がいくつ満たすことができるかを計算してください。
0 <= i、j、k、l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
問題解決のアイデア: 4 つの数値を加算する際に重複排除を考慮する必要がないため、この問題にハッシュ テーブルを使用することを考えます。
セット内の a + b の出現を記録するだけでなく、a + b が何回出現したかをカウントする必要があるため、c + d のマッピングを作成できます。したがって、出現したかどうかを数えるだけでなく、出現した回数も数える必要があります。したがって、 map を使用することを選択し、キーを使用して出現したかどうかを保存し、値を使用して出現回数を保存します。
コードカプリスを読んだ感想
問題解決の手順:
- まず unowned_map を定義し、キーには a と b の合計を、値には a と b の合計の出現回数を入れます。
- nums1 配列と nums2 配列を走査し、2 つの配列の要素の合計と出現回数をカウントし、それらをマップに入力します。
- a+b+c+d = 0 の出現回数をカウントする int 変数 count を定義します。
- 次に、nums3 配列と nums4 配列を走査し、マップ内に 0-(c+d) が出現した場合、count を使用してマップ内のキーに対応する値、つまり出現回数をカウントします。
- 最後に統計値の数を返します。
導入中に遭遇した困難
当初、私は最初に配列を走査し、次に他の 3 つの配列が対応するコレクションに出現するかどうかを走査することを考えました。このようにして、次の 3 つの配列を走査するときの時間計算量は O( n 3 n^3n3 )、最初に最初の 2 つの配列を走査し、次に最後の 2 つの配列を走査する時間計算量はO (n 2 ) O (n^2)○ (ん)2)。
コード
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
// 遍历nums1和nums2数组,统计两个数组元素之和,和出现的次数,放到map中
for (int a : nums1) {
for (int b : nums2) {
umap[a + b]++;
}
}
int count = 0; // 统计a+b+c+d = 0 出现的次数
// 在遍历nums3和nums4数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : nums3) {
for (int d : nums4) {
if (umap.find(0 - (c + d)) != umap.end()) {
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
383. 身代金の手紙
最初にタイトルを見てください
タイトルの説明: ransomNote とmagazine という 2 つの文字列が与えられた場合、ransomNote が雑誌の文字で構成できるかどうかを判断します。
可能な場合は true を返し、そうでない場合は false を返します。
マガジン内の各キャラクターは身代金注で 1 回のみ使用できます。
問題解決のアイデア: この質問は前の242. 効果的なアナグラムと似ていることがわかりましたが、この質問は、文字列 b が文字列 a を形成できるかどうかに関係なく、文字列 a が文字列 b を形成できるかどうかを調べることです。
コードカプリスを読んだ感想
第一の文字列身代金が第二の文字列マガジンの文字で構成できるかどうかを判断する際には、次の 2 つの点に注意する必要があります。
- ポイント1「身代金注でマガジン内の各キャラクターは1回のみ使用可能」
- 2点目 身代金メモと雑誌は小文字の英字で構成されています
このトピックには小文字のみが含まれるため、長さ 26 の配列を使用して、雑誌に文字が登場する回数を記録します。
次に、ransomNote を使用して、この配列に ransomNote で必要な文字がすべて含まれていることを確認します。
この問題を解決するには、ハッシュを使用します。
導入中に遭遇した困難
マップを使用することも考えましたが、少しやりすぎな気がします。コードを読むと、この質問の場合、マップは赤黒ツリーを維持する必要があるため、配列よりもマップを使用した場合のスペース消費が大きくなります。またはハッシュ テーブル、さらにハッシュ関数を実行するのは時間がかかります。データ量が多い場合には差分を反映させることができます。したがって、配列はよりシンプルで、より直接的で、より効率的です。
コード
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26]={
0};
for(int i = 0;i < s.size();i++){
record[s[i] - 'a']++;
}
for(int i = 0;i < t.size();i++){
record[t[i] - 'a']--;
}
for(int i = 0;i < 26;i++){
if(record[i] != 0){
return false;
}
}
return true;
}
};
問題 15. 3 つの数値の和
最初にタイトルを見てください
タイトルの説明:
n 個の整数を含む配列 nums が与えられた場合、nums に 3 つの要素 a、b、c があり、a + b + c = 0 となるかどうかを判断してください。条件を満たし、重複していないトリプルをすべて見つけてください。
注: 回答に繰り返しの 3 連符を含めることはできません。
問題解決のアイデア: この問題を解決するためにハッシュ法を使用したいと考えていますが、タイトルでは重複排除する必要がありますが、重複排除のアイデアはあまり明確ではありません。
コードカプリスを読んだ感想
Caprice では、ハッシュ法とダブル ポインター法という2 つの解決策が提供されています。
ハッシュ化
まず 2 層の for ループを使用して a + b を決定し、次にハッシュを使用して 0-(a+b) が配列内に出現したかどうかを決定します。
しかし、修飾されたトリプルをベクターに入れて重複を除去するのは非常に時間がかかり、タイムアウトになりやすいです。
時間計算量は O(n^2) になる可能性がありますが、枝刈り操作を実行するのは簡単ではないため、それでも時間がかかります。
ダブルポインタ方式
ダブル ポインタ方式はハッシュ方式より効率的です。次のアニメーションを見てみましょう:
まず配列をソートし、次に for ループの層を持ち、i は添字 0 から開始し、同時に添字を定義します。 i+1 の位置、添字の右は配列の最後にあります。
さらに、配列内で a + b +c = 0 となる abc を見つけます。ここでは、a = nums[i]、b = nums[left]、c = nums[right] と等価です。
左右に移動します:
- nums[i] + nums[left] + nums[right] > 0 の場合、配列がソートされているため、この時点では 3 つの数値の合計が大きいことを意味し、右の添字は左に移動するはずです。 3つの数字の合計が小さいこと。
- nums[i] + nums[left] + nums[right] < 0 の場合、この時点では 3 つの数値の合計が小さいことを意味し、left が右に移動して 3 つの数値の合計が大きくなります。
左が右に出会うまで。
導入中に遭遇した困難
重複排除操作にあまり慣れていない
重複排除の論理的思考
実際、主な考慮事項は 3 つの番号の重複排除です。a、b、c、nums[i]、nums[left]、nums[right] に対応
の重複排除
a が繰り返された場合はどうなるでしょうか。a は nums で走査される要素であるため、直接スキップする必要があります。
しかし、nums[i] が nums[i + 1] と同じかどうか、または nums[i] が nums[i-1] と同じかどうかを判断するためでしょうか? すべて nums[i] と比較されますが、前のものと比較されますか、それとも後のものと比較されますか?
後者を比較すると、次のようになります。
if (nums[i] == nums[i + 1]) {
// 去重操作
continue;
}
次に、トリプレット内の繰り返し要素のケースを直接渡します。たとえば、 {-1, -1 ,2} このデータセットは、最初の -1 まで移動すると、次のデータも -1 であると判断され、このデータセットが渡されます。
ただし、トリプルを繰り返すことはできませんが、トリプル内の要素は繰り返すことができることに注意してください。
この時点で、2 つの繰り返し次元が存在します。
したがって、以前のものと比較する必要があります。
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
この方法を書くには、現在 nums[i] を使用しています。最初の -1 に到達するときに、データのセット {-1, -1 ,2} を見て、前の要素が同じ要素であるかどうかを判断します。前のもの -1 がない場合は、{-1, -1 ,2} も結果セットに含めることができます。
b と c の重複排除
重複排除ロジックを追加した場合
コード
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set;
unordered_set<int> nums_set(nums1.begin(),nums1.end());
for(int num : nums2){
if(nums_set.find(num) != nums_set.end()){
result_set.insert(num);
}
}
return vector<int>(result_set.begin(),result_set.end());
}
};
質問202. ハッピーナンバー
最初にタイトルを見てください
タイトル説明:
数値 n が幸せな数値であるかどうかを判断するアルゴリズムを作成します。
「ハッピーナンバー」とは、次のように定義されます。正の整数の場合、毎回その数値を各位置の数値の二乗和に置き換え、数値が 1 になるまでこのプロセスを繰り返すか、無限ループになる可能性がありますが、 1に変わることはありません。それが1になれば、この数字は幸せな数字です。
n が適切な数値の場合は True を返し、そうでない場合は False を返します。
次のようなアイデアがあります。
キー: ** ループが無限である場合、合計は合計プロセス中に繰り返し表示されます**
そこで、合計が繰り返されるかどうかをハッシュ法で判定し、繰り返される場合は false を返し、そうでない場合は 1 になるまで合計を求めます。
unowned_set を使用すると、合計が繰り返されるかどうかを判断できます。
コードカプリスを読んだ感想
同じ考え
導入中に遭遇した困難
値の各桁に対する単数演算にはあまり詳しくありません
コード
class Solution {
public:
// 取数值各个位上的单数之和
int getSum(int n) {
int sum = 0;
while (n) {
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> set;
while(1) {
int sum = getSum(n);
if (sum == 1) {
return true;
}
// 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
if (set.find(sum) != set.end()) {
return false;
} else {
set.insert(sum);
}
n = sum;
}
}
};
1. 2 つの数値の合計
最初にタイトルを見てください
タイトル説明:
整数配列 nums とターゲット値 target が与えられた場合、配列内で合計がターゲット値となる 2 つの整数を見つけて、それらの配列の添字を返してください。
この問題を解決するのは難しいです、そして私は地図についてあまり知りません。
コードカプリスを読んだ感想
この質問には、走査した要素を保存するコレクションが必要で、配列を走査するときにこのコレクションに、要素が走査されたかどうか、つまり要素がこのコレクションに表示されているかどうかを尋ねます。
この質問では、要素が走査されたかどうかを知る必要があるだけでなく、この要素に対応する添え字も知る必要があります。保存するキー値構造、要素を保存するキー、および保存する値を使用する必要があります。添字なので、map を使用するのが適切です。
なぜ以前に使用したセットを使用するのですか?
- 配列のサイズには制限があり、要素が少なくハッシュ値が大きすぎる場合、メモリ空間が無駄に消費されてしまいます。
- set はコレクションであり、その中の要素は key のみにすることができます。2 つの数値の和の問題では、y が存在するかどうかを判断するだけでなく、y の添え字の位置も記録する必要があります。 x と y の値を返す必要があります。したがって、セットも使用できません。
マップはキー値のストレージ構造であり、キーを使用して値を保存したり、値を使用して値が配置されている添え字を保存したりできます。
C++ の 3 種類のマップのうち、std::unowned_map を選択します。この質問ではキーの順序は必要なく、 std::unowned_map を選択する方が効率的であるためです。
マップを使用する場合は、次の 2 つの点に注意してください。
- 地図は何に使われますか
- マップ内のキーと値は何を表しますか?
最初の点に関して、map の目的は、訪問した要素を保存することです。配列をトラバースするときに、現在の要素と一致する要素を見つけることができるように、以前に走査した要素と対応する添え字を記録する必要があるためです。要素 (つまり、ターゲットに等しいものを追加)
2 点目に関しては、この質問では、要素を指定し、その要素が出現したかどうかを判断し、出現した場合はその要素の添え字を返す必要があります。
要素が出現するかどうかを判断するには、この要素がキーとして使用されるため、配列内の要素がキーとして使用され、キーが値に対応し、値が添え字を格納するために使用されます。
したがって、マップ内の記憶構造は {key: データ要素、value: 配列要素に対応する添字} となります。
配列を走査するときは、マップをクエリして、現在走査されている要素に一致する値があるかどうかを確認するだけで済みます。存在する場合、それが見つかった一致ペアです。そうでない場合は、現在走査されている要素をマップに入力します。マップストアは私たちが訪れた要素です。
実装プロセスは次のとおりです。
導入中に遭遇した困難
値の各桁に対する単数演算にはあまり詳しくありません
コード
今日は収穫
1. ハッシュテーブルの基本理論を理解する
2. セットとマップの適用シナリオを知る
3. テンプレートライブラリの使い方がまだあまり上手ではないので、今後強化する必要がある
今日の勉強時間は3時間です
この記事の写真はすべて Carl のコード カプリスからのものです。本当に感謝しています。