コンテンツ
LeetCode 31. 次の順列 (中)
[タイトル説明]
整数配列を配置すると、そのすべてのメンバーが順次または線形順序で配置されます。
たとえば、次のようなものは、 、、、の順列arr = [1,2,3]
とみなすことができます。整数の配列の次の置換は、その整数の辞書編集的に次に大きな置換です。より正式には、配列のすべての順列が辞書編集順に従って最小から最大までコンテナ内に配置されている場合、配列の次の順列は、この並べ替えられたコンテナ内でその後に来る順列になります。次に大きい順列がない場合は、配列を辞書編集上で最小の順列に再配置する必要があります (つまり、その要素は昇順になります)。arr
[1,2,3]
[1,3,2]
[3,1,2]
[2,3,1]
- たとえば、
arr = [1,2,3]
の次の順列は です[1,3,2]
。 - 同様に、
arr = [2,3,1]
の次の順列は です[3,1,2]
。 - そして
arr = [3,2,1]
、 の次の順列は、の辞書編集上、これより大きな順列がない[1,2,3]
ためです。[3,2,1]
整数の配列が与えられた場合nums
、nums
の次の順列を見つけます。その場で
変更する必要があります。追加の定数スペースのみが許可されます。
【例1】
输入:nums = [1,2,3]
输出:[1,3,2]
【例2】
输入:nums = [3,2,1]
输出:[1,2,3]
【例3】
输入:nums = [1,1,5]
输出:[1,5,1]
【ヒント】
1 ≤ 数値。長さ ≤ 100 1\le nums.length\le 1001≤数値。_ _ _ 長さ_ _ _ _≤100
0 ≤ nums [ i ] ≤ 100 0\le nums[i]\le 1000≤数値[ i ] _ _ _≤100
【分析する】
次の順列の生成はnext_permutation
関数を使用して実行できますが、手動での実装については必ず説明します。
後ろから前への最初の上昇位置kkを見つけます。k、つまりk + 1 k+1nums[k] < nums[k + 1]
からk+1開始して、次より大きい 最小の数値ttを見つけます。nums[k]
t、つまりnums[t] > nums[k] && nums[t + 1] <= nums[k]
、次にnums[k]
と をk + 1 k+1nums[t]
になります。k+1.逆のシーケンスを開始するだけです。
【コード】
【next_permutation
機能実装】
class Solution {
public:
void nextPermutation(vector<int>& nums) {
next_permutation(nums.begin(), nums.end());
}
};
【手動実装】
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int k = nums.size() - 2;
while (k >= 0 && nums[k] >= nums[k + 1]) k--;
if (k < 0) reverse(nums.begin(), nums.end()); // 已经是最后一个排列了
else
{
int t = k + 1;
while (t < nums.size() && nums[t] > nums[k]) t++;
swap(nums[k], nums[t - 1]); // nums[t-1]为大于nums[k]最小的数
reverse(nums.begin() + k + 1, nums.end());
}
}
};
LeetCode 32. 最長有効括弧 (ハード)
[タイトル説明]
'('
と のみを含む文字列を指定して')'
、最も長い有効な (整形式で連続した) 括弧部分文字列の長さを見つけます。
【例1】
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
【例2】
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
【例3】
输入:s = ""
输出:0
【ヒント】
0 ≤ 秒 長さ ≤ 3 ∗ 1 0 4 0\and s.length\and 3 * 10^40≤s 。長さ_ _ _ _≤3∗1 04
s[i]
は'('
、または')'
【分析する】
有効な括弧シーケンスの特徴を忘れた場合は、質問 22を復習してください。この質問のアイデアは考えるのが難しく、飛躍があります。
)
まず、この文字列をどのように分割するかを検討し、前から後ろに向かって数値がその数値より大きい最初の場所を見つけます。(
その後、これを分割点として扱い、有効な括弧シーケンスがこの分割点を横切ることはありません。この点から開始して、次の分割点を探し続けます。
その後、有効な括弧シーケンスを見つける必要がある場合は、各セグメント内を調べるだけでよく、最後のセグメント (つまり、セグメント化ポイント) を除く各セグメント内で、任意のプレフィックスの数がそれ以上である必要があります。 1 つの段落内で、正当な部分文字(
列)
の右端の位置を列挙し、対応する左端の左端点を見つけます。
このプロセスを実装するにはスタックを使用できます。スタックには、各左括弧の添え字が保存されます (例: ) (()(()))
。アルゴリズム フローは次のとおりです:
- 最初の
)
文字列 (添字が2
) に遭遇した場合、スタックの内容は次のようになります。[0, 1]
照合のためにスタックの先頭をポップした後、スタックの先頭は となり0
、現在の最長の有効な部分文字列の長さは2 − 0 = 2 2-0 になります。 =22−0=2; - 2 番目
)
(添字が5
) に遭遇した場合、スタックの内容は次のようになります。[0, 3, 4]
照合のためにスタックの先頭をポップした後、スタックの先頭は となり3
、現在の最長の有効な部分文字列の長さは5 − 3 = 2 5-3になります。 =25−3=2; - 3 番目
)
(添字が6
) に遭遇した場合、スタックの内容は次のようになります。[0, 3]
照合のためにスタックの先頭をポップした後、スタックの先頭は となり0
、現在の有効な部分文字列の最長の長さは6 − 0 = 6 6-0 になります。 =66−0=6; - 4 番目
)
(添字が7
) が見つかった場合、スタックの内容は次のようになり、と[0]
一致させるためにスタックの先頭をポップした後でスタックは空になり、現在の最長の有効な部分文字列の長さは7 − 0 + 1 = 8 7- になります。 0 +1=87−0+1=8;
【コード】
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> stk;
int res = 0;
for (int i = 0, start = 0; i < s.size(); i++) // start表示每一段的起始点
{
if (s[i] == '(') stk.push(i); // 左括号直接入栈
else if (stk.size()) // 右括号先检查栈里是否有元素,没有则说明不合法
{
stk.pop();
if (stk.size()) res = max(res, i - stk.top()); // 还没匹配到最左端的左括号
else res = max(res, i - start + 1); // 已经匹配到最左端了
}
else start = i + 1; // 右括号已经多于左括号,为这一段的分界点,更新start从下一段的起点开始
}
return res;
}
};
LeetCode 33. 回転ソート配列の検索 (中)
[タイトル説明]
整数の配列昇順nums
に並べ替えられ、配列内の値は互いに区別されます。関数に渡される前に、これまで知られていなかった添字( ) に対して回転が実行され、配列は(から数えて添字) になります。たとえば、インデックスは回転後に になる可能性があります。回転された配列と整数を指定します。このターゲット値が に存在する場合はその添字を返し、それ以外の場合は を返します。時間計算量O (logn) O(log n)を設計する必要があります。nums
k
0 <= k < nums.length
[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
0
[0,1,2,4,5,6,7]
3
[4,5,6,7,0,1,2]
nums
target
nums
target
-1
O ( log n )アルゴリズムはこの問題を解決します。
【例1】
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
【例2】
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
【例3】
输入:nums = [1], target = 0
输出:-1
【ヒント】
1 ≤ 数値。長さ ≤ 5000 1\le nums.length\le 50001≤数値。_ _ _ 長さ_ _ _ _≤5000
− 1 0 4 ≤ nums [ i ] ≤ 1 0 4 -10^4\the nums[i]\the 10^4− 1 04≤数値[ i ] _ _ _≤1 04
− 1 0 4 ≤ ターゲット ≤ 1 0 4 -10^4\ターゲット\ザ 10^4− 1 04≤ターゲット_ _ _ _ _≤1 04
nums
の各値は一意であり、
質問データはnums
事前に不明なインデックスに基づいてローテーションされることが保証されています。
【分析する】
順序付けされた配列で特定の値を見つけるには、二分法を使用できますが、この質問では、配列を回転した後、2 つの独立した昇順配列を持つことと同じになります。したがって、最初のステップは、これら 2 つの段落を見つけることです。
最初のセクションの数値はすべてより大きく nums[0]
、他のセクションの数値はすべてより小さい nums[0]
ため、この機能を使用して 2 つのセクション間の分割点、つまりnums[0]
より大きい最後の数値を見つけることができます。
target
2 つのセグメントの分解点を見つけた後、との関係からそれがどのセグメントにあるかを知ることができますnums[0]
。両方のセグメントは昇順であるため、 二分 によってそれらの間を知ることができますtarget
。
【コード】
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (nums[mid] >= nums[0]) l = mid;
else r = mid - 1;
}
if (target >= nums[0]) l = 0; // 在第一段区间中搜索,右端点即为二分出来的边界点r
else l = r + 1, r = nums.size() - 1; // 在第二段区间中搜索,左右端点都要改
while (l < r)
{
int mid = l + r >> 1;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
if (nums[r] != target) return -1; // 不存在target
else return r;
}
};
LeetCode 34. ソートされた配列内の要素の最初と最後の位置を検索する (中)
[タイトル説明]
降順でない整数の配列nums
と目標値が与えられますtarget
。配列内の指定されたターゲット値の開始位置と終了位置を見つけてください。
ターゲット値が配列内に存在しない場合にtarget
返します[-1, -1]
。
時間計算量O (logn) O(log n) を設計および実装する必要があります。O ( log n )アルゴリズムはこの問題を解決します。
【例1】
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
【例2】
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
【例3】
输入:nums = [], target = 0
输出:[-1,-1]
【ヒント】
0 ≤ 数値。長さ ≤ 1 0 5 0\le nums.length\le 10^50≤数値。_ _ _ 長さ_ _ _ _≤1 05
− 1 0 9 ≤ nums [ i ] ≤ 1 0 9 -10^9\the nums[i]\the 10^9− 1 09≤数値[ i ] _ _ _≤1 09
− 1 0 9 ≤ ターゲット ≤ 1 0 9 -10^9\ターゲット\ザ 10^9− 1 09≤ターゲット_ _ _ _ _≤1 09は非減少配列
nums
です
【分析する】
同様に、二分探索を使用して、target
より大きいか等しい最小の添字と、target
より小さいかそれ以下の最大の添字を見つけます。
【コード】
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.empty()) return {
-1, -1 }; // 注意需要特判
int l = 0, r = nums.size() - 1, L;
while (l < r)
{
int mid = l + r >> 1;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
if (nums[r] != target) return {
-1, -1 };
L = l, r = nums.size() - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (nums[mid] <= target) l = mid;
else r = mid - 1;
}
return {
L, r };
}
};
LeetCode 35. 挿入位置検索(簡易)
[タイトル説明]
ソートされた配列とターゲット値を指定すると、配列内でターゲット値を検索し、そのインデックスを返します。ターゲット値が配列内に存在しない場合は、その値が順番に挿入される位置を返します。
時間計算量O (logn) O(log n)を使用してください。O ( log n )アルゴリズム. _
【例1】
输入: nums = [1,3,5,6], target = 5
输出: 2
【例2】
输入: nums = [1,3,5,6], target = 2
输出: 1
【例3】
输入: nums = [1,3,5,6], target = 7
输出: 4
【ヒント】
1 ≤ 数値。長さ ≤ 1 0 4 1\le nums.length\le 10^41≤数値。_ _ _ 長さ_ _ _ _≤1 04
− 1 0 4 ≤ nums [ i ] ≤ 1 0 4 -10^4\the nums[i]\the 10^4− 1 04≤数値[ i ] _ _ _≤1 04
− 1 0 4 ≤ ターゲット ≤ 1 0 4 -10^4\ターゲット\ザ 10^4− 1 04≤ターゲット_ _ _ _ _≤1 04は重複要素のない昇順の配列
nums
です。
【分析する】
簡単な2点なので、あまり説明する必要はありません。
【コード】
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0, r = nums.size(); // 可能会插入到末尾,因此r的最大值需要比末尾元素大1
while (l < r)
{
int mid = l + r >> 1;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
return r;
}
};