Update
BIN
pics/26_封面.png
Normal file
After Width: | Height: | Size: 91 KiB |
BIN
pics/27_封面.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
pics/35_封面.png
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
pics/35_搜索插入位置.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
pics/35_搜索插入位置2.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
pics/35_搜索插入位置3.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
pics/35_搜索插入位置4.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
pics/35_搜索插入位置5.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
pics/59_封面.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
pics/leetcode_209.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
pics/螺旋矩阵.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
26
problems/0026.Remove-Duplicates-from-Sorted-Array.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
## 题目地址
|
||||||
|
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
此题就是O(n)的解法,拼速度的话,也就是剪剪枝
|
||||||
|
注意题目中:你不需要考虑数组中超出新长度后面的元素。 说明是要对原数组进行操作的
|
||||||
|
|
||||||
|
## 解法
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int removeDuplicates(vector<int>& nums) {
|
||||||
|
if (nums.empty()) return 0; // 别忘记空数组的判断
|
||||||
|
int slowIndex = 0;
|
||||||
|
for (int fastIndex = 0; fastIndex < (nums.size() - 1); fastIndex++){
|
||||||
|
if(nums[fastIndex] != nums[fastIndex + 1]) { // 发现和后一个不相同
|
||||||
|
nums[++slowIndex] = nums[fastIndex + 1]; //slowIndex = 0 的数据一定是不重复的,所以直接 ++slowIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slowIndex + 1; //别忘了slowIndex是从0开始的,所以返回slowIndex + 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
49
problems/0027.移除元素.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
> 笔者在BAT从事技术研发多年,利用工作之余重刷leetcode,更多原创文章请关注公众号「代码随想录」。
|
||||||
|
|
||||||
|
## 题目地址
|
||||||
|
|
||||||
|
https://leetcode-cn.com/problems/remove-element/
|
||||||
|
|
||||||
|
建议做完这道题,接着再去做 26. 删除排序数组中的重复项, 对这种类型的题目就有有所感觉
|
||||||
|
|
||||||
|
## 暴力解法
|
||||||
|
|
||||||
|
时间复杂度:O(n^2)
|
||||||
|
空间复杂度:O(1)
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int removeElement(vector<int>& nums, int val) {
|
||||||
|
int size = nums.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
|
||||||
|
for (int j = i + 1; j < size; j++) {
|
||||||
|
nums[j - 1] = nums[j];
|
||||||
|
}
|
||||||
|
i--; // 因为下表i以后的数值都向前移动了一位,所以i也向前移动一位
|
||||||
|
size--;// 此时数组的大小-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快慢指针解法
|
||||||
|
时间复杂度:O(n)
|
||||||
|
空间复杂度:O(1)
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int removeElement(vector<int>& nums, int val) {
|
||||||
|
int slowIndex = 0; // index为 慢指针
|
||||||
|
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) { // i 为快指针
|
||||||
|
if (val != nums[fastIndex]) { //将快指针对应的数值赋值给慢指针对应的数值
|
||||||
|
nums[slowIndex++] = nums[fastIndex]; 注意这里是slowIndex++ 而不是slowIndex--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slowIndex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
156
problems/0035.搜索插入位置.md
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
|
||||||
|
## 题目地址
|
||||||
|
|
||||||
|
https://leetcode-cn.com/problems/search-insert-position/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
这道题目其实是一道很简单的题,但是为什么通过率相对来说并不高呢,我理解是大家对 边界处理的判断有所失误,导致的。
|
||||||
|
|
||||||
|
这道题目,我们要在数组中插入目标值,无非是这四种情况
|
||||||
|
|
||||||
|
<img src='../pics/35_搜索插入位置3.png' width=600> </img></div>
|
||||||
|
|
||||||
|
* 目标值在数组所有元素之前
|
||||||
|
* 目标值等于数组中某一个元素
|
||||||
|
* 目标值插入数组中的位置
|
||||||
|
* 目标值在数组所有元素之后
|
||||||
|
|
||||||
|
这四种情况确认清楚了,我们就可以尝试解题了
|
||||||
|
|
||||||
|
暴力解题 不一定时间消耗就非常高,关键看实现的方式,就像是二分查找时间消耗不一定就很低,是一样的。
|
||||||
|
|
||||||
|
这里我给出了一种简洁的暴力解法,和两种二分查找的解法
|
||||||
|
|
||||||
|
|
||||||
|
## 解法:暴力枚举
|
||||||
|
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int searchInsert(vector<int>& nums, int target) {
|
||||||
|
for (int i = 0; i < nums.size(); i++) {
|
||||||
|
// 分别处理如下三种情况
|
||||||
|
// 目标值在数组所有元素之前
|
||||||
|
// 目标值等于数组中某一个元素
|
||||||
|
// 目标值插入数组中的位置
|
||||||
|
if (nums[i] >= target) { // 一旦发现大于或者等于target的num[i],那么i就是我们要的结果
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 目标值在数组所有元素之后的情况
|
||||||
|
return nums.size(); // 如果target是最大的,或者 nums为空,则返回nums的长度
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
效率如下:
|
||||||
|
<img src='../pics/35_搜索插入位置.png' width=600> </img></div>
|
||||||
|
|
||||||
|
时间复杂度:O(n)
|
||||||
|
时间复杂度:O(1)
|
||||||
|
|
||||||
|
|
||||||
|
## 二分法
|
||||||
|
|
||||||
|
既然暴力解法的时间复杂度是On,我们就要尝试一下使用二分查找法。
|
||||||
|
|
||||||
|
<img src='../pics/35_搜索插入位置4.png' width=600> </img></div>
|
||||||
|
|
||||||
|
大家注意这道题目的前提是数组是有序数组,这也是使用二分查找的基础条件
|
||||||
|
|
||||||
|
以后大家**只要看到面试题里给出的数组是有序数组,都可以想一想是否可以使用二分法。**
|
||||||
|
|
||||||
|
同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下表可能不是唯一的。
|
||||||
|
|
||||||
|
大体讲解一下二分法的思路,这里来举一个例子,例如在这个数组中,我们使用二分法寻找元素为5的位置,并返回其下标
|
||||||
|
|
||||||
|
<img src='../pics/35_搜索插入位置5.png' width=600> </img></div>
|
||||||
|
|
||||||
|
二分查找涉及的很多的边界条件,逻辑比较简单,就是写不好
|
||||||
|
|
||||||
|
相信很多同学对二分查找法中边界条件处理不好,例如 到底是 小于 还是 小于等于, 到底是+1 呢,还是要-1呢
|
||||||
|
|
||||||
|
这是为什么呢,主要是**我们对区间的定义没有想清楚,这就是我们的不变量**
|
||||||
|
|
||||||
|
我们要在二分查找的过程中,保持不变量,这也就是**循环不变量** (感兴趣的同学可以查一查)
|
||||||
|
|
||||||
|
### 二分法第一种写法
|
||||||
|
|
||||||
|
以这道题目来举例,以下的代码中我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
|
||||||
|
|
||||||
|
这就决定了我们 这个二分法的代码如何去写,大家看如下代码
|
||||||
|
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int searchInsert(vector<int>& nums, int target) {
|
||||||
|
int n = nums.size();
|
||||||
|
int left = 0;
|
||||||
|
int right = n - 1; // 我们定义target在左闭右闭的区间里,[left, right]
|
||||||
|
while (left <= right) { // 当left==right,区间[left, right]依然有效
|
||||||
|
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
|
||||||
|
if (nums[middle] > target) {
|
||||||
|
right = middle - 1; // target 在左区间,所以[left, middle - 1]
|
||||||
|
} else if (nums[middle] < target) {
|
||||||
|
left = middle + 1; // target 在右区间,所以[middle + 1, right]
|
||||||
|
} else { // nums[middle] == target
|
||||||
|
return middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 分别处理如下四种情况
|
||||||
|
// 目标值在数组所有元素之前 [0, -1]
|
||||||
|
// 目标值等于数组中某一个元素 return middle;
|
||||||
|
// 目标值插入数组中的位置 [left, right],return right + 1
|
||||||
|
// 目标值在数组所有元素之后的情况 [left, right], return right + 1
|
||||||
|
return right + 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
时间复杂度:O(logn)
|
||||||
|
时间复杂度:O(1)
|
||||||
|
|
||||||
|
效率如下:
|
||||||
|
<img src='../pics/35_搜索插入位置2.png' width=600> </img></div>
|
||||||
|
|
||||||
|
### 二分法第二种写法
|
||||||
|
|
||||||
|
如果说我们定义 target 是在一个在左闭右开的区间里,也就是[left, right)
|
||||||
|
|
||||||
|
那么二分法的边界处理方式则截然不同。
|
||||||
|
|
||||||
|
不变量是[left, right)的区间,如下代码可以看出是如何在循环中坚持不变量的。
|
||||||
|
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int searchInsert(vector<int>& nums, int target) {
|
||||||
|
int n = nums.size();
|
||||||
|
int left = 0;
|
||||||
|
int right = n; // 我们定义target在左闭右开的区间里,[left, right) target
|
||||||
|
while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间
|
||||||
|
int middle = left + ((right - left) >> 1);
|
||||||
|
if (nums[middle] > target) {
|
||||||
|
right = middle; // target 在左区间,在[left, middle)中
|
||||||
|
} else if (nums[middle] < target) {
|
||||||
|
left = middle + 1; // target 在右区间,在 [middle+1, right)中
|
||||||
|
} else { // nums[middle] == target
|
||||||
|
return middle; // 数组中找到目标值的情况,直接返回下标
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 分别处理如下四种情况
|
||||||
|
// 目标值在数组所有元素之前 [0,0)
|
||||||
|
// 目标值等于数组中某一个元素 return middle
|
||||||
|
// 目标值插入数组中的位置 [left, right) ,return right 即可
|
||||||
|
// 目标值在数组所有元素之后的情况 [left, right),return right 即可
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
时间复杂度:O(logn)
|
||||||
|
时间复杂度:O(1)
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
希望通过这道题目 ,可以帮助大家对二分法有更深的理解
|
||||||
|
|
||||||
|
> 笔者在BAT从事技术研发多年,利用工作之余重刷leetcode,更多原创文章请关注公众号:「代码随想录」。
|
55
problems/0053.最大子序和.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
> 笔者在BAT从事技术研发多年,利用工作之余重刷leetcode,更多原创技术文章欢迎关注「代码随想录」,校招社招求职内推欢迎通过公众号「代码随想录」联系我,度厂很缺人!
|
||||||
|
|
||||||
|
> 笔者在BAT从事技术研发多年,利用工作之余重刷leetcode,希望结合自己多年的实践经验,把算法讲的更清楚,更多原创文章欢迎关注公众号「代码随想录」。
|
||||||
|
|
||||||
|
## 题目地址
|
||||||
|
https://leetcode-cn.com/problems/maximum-subarray/
|
||||||
|
|
||||||
|
## 暴力解法
|
||||||
|
|
||||||
|
暴力解法的思路,第一层for 就是设置起始位置,第二层for循环遍历数组寻找最大值
|
||||||
|
|
||||||
|
时间复杂度:O(n^2)
|
||||||
|
空间复杂度:O(1)
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int maxSubArray(vector<int>& nums) {
|
||||||
|
int result = INT32_MIN;
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < nums.size(); i++) { // 设置起始位置
|
||||||
|
count = 0;
|
||||||
|
for (int j = i; j < nums.size(); j++) { // 每次从起始位置i开始遍历寻找最大值
|
||||||
|
count += nums[j];
|
||||||
|
result = count > result ? count : result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 贪心解法
|
||||||
|
贪心解法,其实不是很好理解, 看上面暴力的解法是两层for循环,那如何省掉一层for循环呢
|
||||||
|
|
||||||
|
其实**暴力解法中设置起始位置这个for循环是可以省略掉的**,这也是关键的一步,有了这个思路之后,就可以看一下代码了
|
||||||
|
|
||||||
|
时间复杂度:O(n)
|
||||||
|
空间复杂度:O(1)
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int maxSubArray(vector<int>& nums) {
|
||||||
|
int result = INT32_MIN;
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < nums.size(); i++) {
|
||||||
|
count += nums[i];
|
||||||
|
if (count > result) { // 相当于每次取各个终点的最大值
|
||||||
|
result = count;
|
||||||
|
}
|
||||||
|
if (count <= 0) count = 0; // 相当于重置起点,因为遇到负数一定是拉低总体数值的
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
73
problems/0059.螺旋矩阵II.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
> 笔者在BAT从事技术研发多年,利用工作之余重刷leetcode,更多原创文章请关注公众号「代码随想录」。
|
||||||
|
|
||||||
|
## 题目地址
|
||||||
|
https://leetcode-cn.com/problems/spiral-matrix-ii/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
模拟顺时针画矩阵的过程
|
||||||
|
|
||||||
|
填充上行从左到右
|
||||||
|
填充右列从上到下
|
||||||
|
填充下行从右到左
|
||||||
|
填充左列从下到上
|
||||||
|
|
||||||
|
在模拟的过程中最重要的思想是**保持画每一条边的原则一致,即每次都是左闭右开的原则**,如果所示
|
||||||
|
|
||||||
|
<img src='../pics/螺旋矩阵.png' width=600> </img></div>
|
||||||
|
|
||||||
|
很多同学,做这道题目之所以一直写不好,代码越写越乱,就是因为 在画每一条边的时候,没有保证统一原则
|
||||||
|
|
||||||
|
例如:模拟矩阵上边的时候 左闭右开,然后模拟矩阵右列的时候又开始了左闭又闭,那岂能不乱
|
||||||
|
|
||||||
|
## 解法
|
||||||
|
|
||||||
|
```C++
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
vector<vector<int>> generateMatrix(int n) {
|
||||||
|
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
|
||||||
|
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
|
||||||
|
int loop = n / 2; // 每个圈循环几次
|
||||||
|
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(3, 3)
|
||||||
|
int count = 1; // 用来计数
|
||||||
|
int offset = 1; // 每一圈循环,需要偏移的位置
|
||||||
|
int i,j;
|
||||||
|
while (loop --) {
|
||||||
|
i = startx;
|
||||||
|
j = starty;
|
||||||
|
|
||||||
|
// 下面开始的四个for就是模拟转了一圈
|
||||||
|
// 模拟填充上行从左到右(左闭右开)
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
|
||||||
|
startx++;
|
||||||
|
starty++;
|
||||||
|
|
||||||
|
// offset 控制每一圈,遍历的长度
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
|
||||||
|
if (n % 2) {
|
||||||
|
res[mid][mid] = count;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
62
problems/0209.长度最小的子数组.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
## 题目地址
|
||||||
|
https://leetcode-cn.com/problems/minimum-size-subarray-sum/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
这道题目 暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列
|
||||||
|
这块我们还可以使用滑动窗口的细想来做这道题。所谓滑动窗口,**就是不断的调节子序列的起始位置,从而得出我们要想的结果**
|
||||||
|
|
||||||
|
## 暴力解法
|
||||||
|
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int minSubArrayLen(int s, vector<int>& nums) {
|
||||||
|
int result = INT32_MAX; // 最终的结果
|
||||||
|
int sum = 0; // 子序列的数值之和
|
||||||
|
int subLength = 0; // 子序列的长度
|
||||||
|
for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
|
||||||
|
sum = 0;
|
||||||
|
for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
|
||||||
|
sum += nums[j];
|
||||||
|
if (sum >= s) { // 一旦发现子序列和超过了s,更新result
|
||||||
|
subLength = j - i + 1; // 取子序列的长度
|
||||||
|
// result取 result和subLength最小的那个
|
||||||
|
result = result < subLength ? result : subLength;
|
||||||
|
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
|
||||||
|
return result == INT32_MAX ? 0 : result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 滑动窗口
|
||||||
|
|
||||||
|
<img src='../pics/leetcode_209.png' width=600> </img></div>
|
||||||
|
```
|
||||||
|
// 滑动窗口
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int minSubArrayLen(int s, vector<int>& nums) {
|
||||||
|
int result = INT32_MAX;
|
||||||
|
int sum = 0; // 滑动窗口数值之和
|
||||||
|
int i = 0; // 滑动窗口起始位置
|
||||||
|
int subLength = 0; // 滑动窗口的长度
|
||||||
|
for (int j = 0; j < nums.size(); j++) {
|
||||||
|
sum += nums[j];
|
||||||
|
// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
|
||||||
|
while (sum >= s) {
|
||||||
|
subLength = (j - i + 1); // 取子序列的长度
|
||||||
|
// result取 result和subLength最小的那个
|
||||||
|
result = result < subLength ? result : subLength;
|
||||||
|
sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
|
||||||
|
return result == INT32_MAX ? 0 : result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
67
problems/0383.RansomNote.md
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
## 题目地址
|
||||||
|
https://leetcode-cn.com/problems/ransom-note/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
这道题题意很清晰,就是用判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点1.
|
||||||
|
|
||||||
|
* 第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思” 这里*说明杂志里面的字母不可重复使用。*
|
||||||
|
|
||||||
|
* 第二点 “你可以假设两个字符串均只含有小写字母。” *说明只有小写字母*,这一点很重要
|
||||||
|
|
||||||
|
## 一般解法
|
||||||
|
|
||||||
|
那么第一个思路其实就是暴力枚举了,两层for循环,不断去寻找,代码如下:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
// 时间复杂度: O(n^2)
|
||||||
|
// 空间复杂度:O(1)
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
bool canConstruct(string ransomNote, string magazine) {
|
||||||
|
for (int i = 0; i < magazine.length(); i++) {
|
||||||
|
for (int j = 0; j < ransomNote.length(); j++) {
|
||||||
|
if (magazine[i] == ransomNote[j]) {
|
||||||
|
ransomNote.erase(ransomNote.begin() + j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ransomNote.length() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
这里时间复杂度是比较高的,而且里面还有一个字符串删除也就是erase的操作,也是费时的,当然这段代码也可以过这道题
|
||||||
|
|
||||||
|
我们想一想优化解法
|
||||||
|
|
||||||
|
## 优化解法
|
||||||
|
|
||||||
|
因为题目所只有小写字母,那我们可以采用空间换区时间的哈希策略, 用一个长度为26的数组还记录magazine里字母出现的次数,然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。
|
||||||
|
代码如下:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
// 时间复杂度: O(n)
|
||||||
|
// 空间复杂度:O(1)
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
bool canConstruct(string ransomNote, string magazine) {
|
||||||
|
int record[26] = {0};
|
||||||
|
for (int i = 0; i < magazine.length(); i++) {
|
||||||
|
record[magazine[i]-'a'] ++;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < ransomNote.length(); j++) {
|
||||||
|
record[ransomNote[j]-'a']--;
|
||||||
|
if(record[ransomNote[j]-'a'] < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|