記事ディレクトリ
質問リストのリンク: 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)です。○ (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. アルファベットのアナグラムのグループ化
解決策:
文字アナグラムは、特定の文字列に同じ文字が含まれており、各文字が同じ回数出現することを意味します。文字アナグラムは、ソート後にまったく同じ文字列として理解することもできます。
この問題の解決策は後者 (ソート) を使用します。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. 最長連続シーケンス
解決策: ハッシュテーブル
毎回シーケンスの最小値から列挙していくが、連続していれば反復子は止まらない++。たとえば、最小値が 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 つの数字の合計
解決策: ソート + ダブルポインター
配列を小さいものから大きいものに並べ替えます。配列の最初のインデックスiiの走査を開始します。i は3 つの数値のうちの最初の数値を表します。この数値を固定してから、ダブル ポインター演算を使用します (j と kj と kjとk ) 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 と kjとkはO ( 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. 雨水を受ける
解決策: 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;
}
};