写在最前
「时间」与「空间」复杂度
- 常见的时间复杂度量级:
常数阶O(1)
线性阶O(n)
平方阶O(n²)
对数阶O(logn):成倍数缩减搜索范围
线性对数阶O(nlogn)
像O(1), O(ln(n)), O(n^a) 等,叫做多项式级复杂度,因为它的规模n出现在底数的位置;另一种像是O(a^n)和 O(n!)等,它是非多项式级的复杂度。
l ogn为什么忽略底数:如log2^n = log2^10 * log10^n, log2^10是一个常数,而计算时间复杂度是忽略常数项系数的,抽象一下就是在时间复杂度的计算过程中,logi^n等于logj^n,忽略了i,直接说是logn。
- 不常见的时间复杂度:
递归算法的时间复杂度: 如果递归函数中,只进行一次递归调用,递归深度为depth;在每个递归的函数中,时间复杂度为T;则总体的时间复杂度为O(T * depth)。
最好、最坏情况时间复杂度: 指的是特殊情况下的时间复杂度。
平均时间复杂度: 可用代码在所有可能情况下执行次数的加权平均值表示。
均摊复杂度: 一个相对比较耗时的操作,如果能保证它不会每次都被触发,那么这个相对比较耗时的操作,它所相应的时间是可以分摊到其它的操作中来的。 - 空间复杂度:
是对算法运行过程中临时占用空间大小的度量,取决于分配的空间会不会随着处理数据量的变化而变化,又是如何变化。常见的复杂度不多,从低到高排列: O(1) 、 O(log(n)) 、 O(n) 、 O(n2)。
如,循环里的局部变量因重新定义,并不会一直存在,故空间复杂度是O(1)。
如,递归调用时需要消耗栈空间,栈空间的大小取决于递归调用的深度。
「递归」与「回溯」
- 递归(recursion):
一种算法结构
是一种直接或间接调用自身的方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
递归思想:大化小且相似。
必要条件:
① 存在限制停止条件;
② 递归调用后会接近这个限制条件。
递归三部曲:
①确定递归函数的参数和返回值
②确定终止条件
③确定单层递归的逻辑 - 回溯(backtrack):
一种算法思想,可以用递归实现
类似于穷举法,但回溯有“剪枝”功能,即自我判断过程。当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
题目
1.移动零( LeetCode 283 )
难度: 简单
题目表述:
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
代码(C++):
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int left = 0;
int right = 0;
while (right < nums.size()) {
if (nums[right] != 0) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left++;
}
right++;
}
}
};
题解: 双指针
左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
时间复杂度:O(n)
空间复杂度:O(1)
2.颜色分类( LeetCode 75 )
难度: 中等
题目表述:
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
代码(C++):
class Solution {
public:
void sortColors(vector<int>& nums) {
int red = 0;
int i = 0;
int blue = nums.size() - 1;
while (i <= blue) {
if (nums[i] == 0) {
int tmp = nums[i];
nums[i] = nums[red];
nums[red] = tmp;
red++;
i++;
} else if (nums[i] == 2) {
int tmp = nums[i];
nums[i] = nums[blue];
nums[blue] = tmp;
blue--;
} else {
i++;
}
}
}
};
题解: 双指针
3.删除有序数组中的重复项( LeetCode 26 )
难度: 简单
题目表述:
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
代码(C++):
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int i = 0;
int j = i + 1;
while (j < nums.size()) {
if (nums[i] != nums[j]) {
nums[i + 1] = nums[j];
i++;
}
j++;
}
return i + 1;
}
};
题解: 双指针
4.合并两个有序数组( LeetCode 88 )
难度: 简单
题目表述:
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
代码(C++):
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m - 1, j = n - 1, k = m + n - 1, cur;
while (i >= 0 || j >= 0) {
if (i == -1) {
cur = nums2[j--];
} else if (j == -1) {
cur = nums1[i--];
} else if (nums1[i] > nums2[j]) {
cur = nums1[i--];
} else {
cur = nums2[j--];
}
nums1[k--] = cur;
}
}
};
题解: 逆向双指针
5.二分查找( LeetCode 704 )
难度: 简单
题目表述:
代码(C++):
class Solution {
public:
// 左闭右闭 [left, right]
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] > target) {
r = mid - 1;
} else {
l = mid + 1;
}
}
return -1;
}
// 左闭右开 [left, right)
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size();
while (l < r) {
int mid = (l + r) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] > target) {
r = mid;
} else {
l = mid + 1;
}
}
return -1;
}
};
题解:
6.移除元素( LeetCode 27 )
难度: 简单
题目表述:
代码(C++):
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n = nums.size();
int l = 0, r = n - 1;
while (l <= r) {
while (l <= r && nums[l] != val) {
l++;
}
while (l <= r && nums[r] == val) {
r--;
}
if (l < r) {
nums[l++] = nums[r--];
}
}
return l;
}
};
题解:
7.有序数组的平方( LeetCode 977 )
难度: 简单
题目表述:
代码(C++):
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
vector<int> res(n);
int l = 0, r = n - 1, k = n - 1;
while (l <= r) {
if (nums[l] * nums[l] > nums[r] * nums[r]) {
res[k--] = nums[l] * nums[l];
l++;
} else {
res[k--] = nums[r] * nums[r];
r--;
}
}
return res;
}
};
题解:
逆序放入答案并移动指针
8.长度最小的子数组( LeetCode 209 )
难度: 中等
题目表述: 其和 ≥ target 的长度最小的 连续子数组
代码(C++):
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int len = INT_MAX;
int l = 0, r = 0, n = nums.size(), sum = 0;
while (r < n) {
sum += nums[r];
while (sum >= target) {
len = min(len, r - l + 1);
sum -= nums[l];
l++;
}
r++;
}
return len == INT_MAX ? 0 : len;
}
};
题解: 滑动窗口
⭐9.螺旋矩阵 II( LeetCode 59 )
难度: 中等
题目表述:
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
代码(C++):
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int starti = 0, endi = n - 1, startj = 0, endj = n - 1, cnt = 1;
vector<vector<int>> ans(n, vector<int>(n));
while (cnt <= n * n) {
for (int j = startj; j <= endj; j++) ans[starti][j] = cnt++;
starti++;
for (int i = starti; i <= endi; i++) ans[i][endj] = cnt++;
endj--;
for (int j = endj; j >= startj; j--) ans[endi][j] = cnt++;
endi--;
for (int i = endi; i >= starti; i--) ans[i][startj] = cnt++;
startj++;
}
return ans;
}
};
题解:
小结
双指针