「コードカプリス」を学ぶ
理論的根拠
配列とは何ですか?
配列は、連続したメモリ空間に格納された同じタイプのデータのコレクションです。
二分探索
解決策は 2 つあります: 左閉じ、右閉じ、左閉じ、右開き
左を閉じて右を閉じる
即[左、右]
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; //定义为左闭右闭
while (left <= right) {
//见注1
//这一步等同于(left + right) / 2,如下处理为了防止内存溢出
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
//说明target在左区间
right = middle - 1;
}else if (nums[middle] < target) {
left = middle + 1;
}else {
return middle;
}
}
return -1;
}
};
ノート
- left-closed と right-closed が定義されているため、left が right になることができます。
<=
左が閉じた状態 右が開いた状態
即[左、右)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); //定义为左闭右开
while (left < right) {
//left取不到right
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle; //左区间是[left,middle)
}else if (nums[middle] < target) {
left = middle + 1;
}else {
return middle;
}
}
return -1;
}
};
要素を削除する
2つの解決策:暴力的方法、ダブルポインタ方法
暴力の法則
2 つの for ループで要素を前方に上書きします
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
//第一个for循环遍历数组元素
if (nums[i] == val) {
for (int j = i + 1; j < size; j++) {
//第二个for循环更新数组
nums[j - 1] = nums[j];
}
i--; //i后的元素都向前移了一位,所以i--
size--;
}
}
return size;
}
};
時間計算量: O(n^2)
空間の複雑さ: O(1)
ダブルポインタ方式
2 つのポインタ (ファスト ポインタとスロー ポインタ) を使用して、2 層の for ループの動作を 1 層の for ループで実現します。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
//若找到该元素,fastIndex++,slowIndex不动
if (nums[fastIndex] != val) {
//在下一次循环,用fastIndex指向的元素覆盖slowIndex的
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
};
時間計算量: O(n)
空間の複雑さ: O(1)
最小の部分配列
2 つの解決策: 暴力的な方法、スライディング ウィンドウ
暴力の法則
最初の for ループはすべての要素を反復し、2 番目の for ループはその要素から配列の末尾まで検索します。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX; //结果
int sum = 0;
int subLength = 0; //记录最小长度
for (int i = 0; i < nums.size(); i++) {
sum = 0;
for (int j = i; j < nums.size(); j++) {
sum += nums[j];
if (sum >= target) {
//一旦和大于target,记录最小长度
subLength = j - i + 1;
result = result < subLength ? result : subLength;
break;
}
}
}
return result == INT32_MAX ? 0 : result;
}
};
時間計算量: O(n^2)
空間の複雑さ: O(1)
スライドウィンドウ
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX;
int sum = 0;
int subLength = 0;
int i = 0; //记录窗口起始位置
for (int j = 0; j < nums.size(); j++) {
sum += nums[j];
while (sum >= target) {
subLength = j - i + 1;
result = result > subLength ? subLength : result;
//滑动窗口的精髓,i一直在变
sum -= nums[i++];
}
}
return result == INT32_MAX ? 0 : result;
}
};
時間計算量: O(n)
空間の複雑さ: O(1)
スパイラルマトリックス
ループ不変条件で永続化: 左閉じ、右開き
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0));
int startx = 0, starty = 0;
int loop = n / 2; //圈数,n=3时就一圈
int mid = n / 2; //如果n为奇数,最后要给中间位置赋值
int count = 1; //记录赋的值
int offset = 1; //控制每圈的长度,但不是长度
int i, j;
while (loop--) {
i = startx;
j = starty;
//四次循环就是一圈
for (j = starty; j < starty + n - offset; j++) {
res[startx][j] = count++;
}
for (i = startx; i < startx + n - offset; i++) {
res[i][j] = count++;
}
for (; j > starty; j--) {
res[i][j] = count++;
}
for (; i > startx; i--) {
res[i][j] = count++;
}
//每次循环后的处理
startx++;
starty++;
offset += 2;
}
//如果是奇数
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};