This commit is contained in:
Estom
2021-04-12 23:21:45 +08:00
parent 8aab36604b
commit ebfe8e0679
17 changed files with 428 additions and 34 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,4 @@
*.exe
*.pyc
__pycache__
.vscode
*.class
.vscode/launch.json

22
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,22 @@
{
"configurations": [
{
"name": "Win32",
// 用来设置头文件
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.19041.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29910/bin/Hostx64/x64/cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}

18
.vscode/note.code-snippets vendored Normal file
View File

@@ -0,0 +1,18 @@
{
// Place your Notes 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
// "Print to console": {
// "scope": "javascript,typescript",
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
}

18
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"files.associations": {
"iostream": "cpp",
"ostream": "cpp",
"vector": "cpp",
"deque": "cpp",
"iterator": "cpp",
"list": "cpp",
"queue": "cpp",
"random": "cpp",
"regex": "cpp",
"stack": "cpp",
"xlocbuf": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xstring": "cpp"
}
}

View File

@@ -5,6 +5,7 @@ using namespace std;
int main()
{
vector<int> vec;
vec.push_back(1);
cout << "hello world" << endl;
return 0;
}

View File

@@ -31,6 +31,7 @@
* 求解数组中元素左边第一个比它小的元素的下标从后往前构造单调递增栈。比他小的元素会将其pop掉。
## 代码
* 单调递增栈
```
@@ -52,3 +53,20 @@ for(int i = T.size() - 1; i >= 0; i--){
stk.push(i);
}
```
## 特性
单调栈的关键不在于栈的顺序,不在于是递增栈还是递减栈。
1. **出栈入栈的时候所处的状态**
2. **出栈入栈的时候所采取操作**
以递减栈为例。
1. 当元素出栈的时候,说明后续有更大的元素,栈底的元素也比自己大。所以出栈元素是倒数第二大的元素。
2. 当元素入栈的时候,之前的元素都比自己大的状态。并不知道正向是第几大的元素。
递增栈则相反
1. 当前元素出栈的时候,说明当前元素是倒数第二小的元素。
一般会采取怎样的操作呢?具体再总结。

View File

@@ -0,0 +1,59 @@
# 最长回文串
### 问题描述
给你一个字符串 s找到 s 中最长的回文子串。
### 问题分析
### 策略选择
### 算法设计
1. 问题分解划分阶段:规模增长的方向有两个,第一个是最长回文串的长度,第二个是字符串的长度。
2. 确定状态变量dp[i][j]。表示长度为i以j为起点的字符串是否是回文串。
3. 确定状态转移方程。长度减2起始位置为j+1的回文字符串。
$$
dp[i][j]=dp[i-2][j+1] and s[j]==s[j+1-1]
$$
4. 确定边界实现过程。i增长的边界是n表示最大长度为ni=0,和i=1时值都初始化为1j增长的边界是0到n-i
### 算法分析
* 时间复杂度O(n^2)
* 空间复杂度O(n^2)
### 算法实现
```C++
class Solution {
public:
// 思路1 动态规划
string longestPalindrome(string s) {
int n=s.size();
if(n==0)return "";
if(n==1)return s;
vector<vector<int>> vec(n+1,vector<int>(n,0));
for(int i=0;i<n;i++){
vec[0][i]=1;
vec[1][i]=1;
}
// length
int max=1;
string result=s.substr(0,1);
for(int i=2;i<=n;i++){
for(int j=0;j<=n-i;j++){
if(s[j]==s[j+i-1] && vec[i-2][j+1]){
vec[i][j]=1;
if(i>max){
max=i;
result = s.substr(j,i);
}
}
}
}
return result;
}
};
```

View File

@@ -1,6 +1,15 @@
# 单调栈
* 用来解决**局部顺序问题**
* 柱形图问题多使用单调栈。
> 下边是对柱形图问题的总结。
> * [视野总和](#1-视野总和)
> * [柱形图中的最大矩形](#2-柱状图中的最大矩形)
> * [盛最多水的容器](4.3%20数组与滑动窗口.md)
> * [接雨水问题](3.3%20接雨水问题.md)
> * [接雨水问题2](3.3%20接雨水问题.md)
## 1 视野总和
### 问题描述
@@ -11,11 +20,10 @@
输出2
解释个子为4的可以看到个子为3的发型个子为7可以看到个子为1的身高所以1+1=2
```
* []()
### 问题分析
* 问题分类:
* 思路:观察题之后,我们发现实际上题目转化为找当前数字向右查找的第一个大于他的数字之间有多少个数字,然后将每个 结果累计就是答案但是这里时间复杂度为O(N^2),所以我们使用单调栈来解决这个问题。
* 思路观察题之后我们发现实际上题目转化为找当前数字向右查找的第一个大于他的数字之间有多少个数字然后将每个结果累计就是答案但是这里时间复杂度为O(N^2),所以我们使用单调栈来解决这个问题。
### 策略选择
* 数据结构:单调栈
@@ -320,4 +328,5 @@ public:
return m.top();
}
};
```
```

View File

@@ -0,0 +1,162 @@
# 接雨水问题
> 这个问题也太经典了,能用的这些思路,也太神奇了。
## 1 接雨水问题
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
![](image/2021-04-12-22-36-09.png)
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的直方图,在这种情况下,可以接 6 个单位的水(蓝色部分表示水)。 感谢 Marcos 贡献此图。
示例:
[链接](https://leetcode-cn.com/problems/volume-of-histogram-lcci)
```
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
```
### 问题分析
* 问题抽象
* 问题分类:线性数组
## 1.1 接雨水问题——动态规划
### 策略算则
1. 动态规划
### 算法设计
1. 左扫一遍,求每个位置的左最大值。
2. 右扫一遍,取每个位置的右最大值。
3. 取两个最大值的最小值。作为该位置能接雨水的高度。
### 算法分析
* 时间复杂度O(n)
* 空间复杂度O(n)
### 算法实现
```C++
class Solution {
public:
int trap(vector<int>& height) {
int n=height.size();
vector<int> left(n,0);
vector<int> right(n,0);
int max_left=0,max_right=0;
for(int i=0;i<n;i++){
if(height[i]>max_left)max_left=height[i];
left[i]=max_left;
if(height[n-1-i]>max_right)max_right=height[n-1-i];
right[n-1-i]=max_right;
}
int sum=0;
for(int i=0;i<n;i++){
sum+=min(left[i],right[i])-height[i];
}
return sum;
}
};
```
## 1.2 接雨水问题——单调栈
### 策略选择
* 单调栈
### 算法设计
* 计算思想与动态规划不同。动态规划目标是计算一个位置的上方能存多少水。二单调栈计算任意两个能存水的左右边界,能存多少水。是横向计算水的量。单调栈在出栈的时候恰好能够形成一个蓄水池的左右边界。
* 从左到右遍历数组,遍历到下标 ii 时如果栈内至少有两个元素记栈顶元素为toptop 的下面一个元素是 left则一定有 height[left]≥height[top]。如果 height[i]>height[top]则得到一个可以接雨水的区域该区域的宽度ileft1高度是min(height[left],height[i])height[top],根据宽度和高度即可计算得到该区域能接的雨水量。
* 为了得到 left需要将 top 出栈。在对 top 计算能接的雨水量之后, left 变成新的 top重复上述操作直到栈变为空或者栈顶下标对应的 height 中的元素大于或等于height[i]。
![](image/2021-04-12-22-55-49.png)
### 算法分析
* 时间复杂度O(n)
* 空间复杂度O(n)
### 算法实现
```
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0;
stack<int> stk;
int n = height.size();
for (int i = 0; i < n; ++i) {
while (!stk.empty() && height[i] > height[stk.top()]) {
int top = stk.top();
stk.pop();
if (stk.empty()) {
break;
}
int left = stk.top();
int currWidth = i - left - 1;
int currHeight = min(height[left], height[i]) - height[top];
ans += currWidth * currHeight;
}
stk.push(i);
}
return ans;
}
};
```
## 1.3 接雨水问题——双指针
### 策略选择
* 双指针
### 算法设计
* 本质上与[最大盛水面积](4.3%20数组与滑动窗口.md)中提到的双指针方法是完全一致的。都是一种**贪心思想**。舍弃掉不可能继续增大的区域。
### 算法分析
* 时间复杂度O(n)
* 空间复杂度O(1)O(1)
### 算法实现
```
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0;
int left = 0, right = height.size() - 1;
int leftMax = 0, rightMax = 0;
while (left < right) {
leftMax = max(leftMax, height[left]);
rightMax = max(rightMax, height[right]);
if (height[left] < height[right]) {
ans += leftMax - height[left];
++left;
} else {
ans += rightMax - height[right];
--right;
}
}
return ans;
}
};
```

View File

@@ -1,6 +1,6 @@
# 数组与滑动窗口
## 1.2 最长不含重复字符的子字符串——滑动窗口
## 1 最长不含重复字符的子字符串——滑动窗口
### 问题描述
@@ -55,4 +55,52 @@ $$
}
```
## 2 盛最多水的容器
### 问题描述
给你 n 个非负整数 a1a2...an每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
![](image/2021-04-12-21-46-57.png)
```
输入:[1,8,6,2,5,4,8,3,7]
输出49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下容器能够容纳水表示为蓝色部分的最大值为 49。
```
### 问题分析
### 策略选择
* 双指针。本质上是一种反向贪心思想。虽然我不知道做出选择是不是最好。但是我每次一定都能把更坏的情况舍弃掉。
* 与[二维有序矩阵查找](../C类问题类型算法/1.2%20二维数组查找.md)的题非常类似。都是舍弃掉坏的情况,进行线性贪心。
### 算法设计
* 双指针贪心思想:对应数字较小的那个指针以后不可能作为容器的边界了,将其丢弃,并移动对应的指针。
* 计算公式:两个指针指向的数字中较小值∗指针之间的距离
![](image/2021-04-12-21-55-39.png)
### 算法分析
* 时间复杂度O(n)
* 空间复杂度O(1)
### 算法实现
```C++
int maxArea(vector<int>& height) {
int i=0,j=height.size()-1;
int max_val=0;
while(i<j){
max_val=max(max_val,(j-i)*min(height[i],height[j]));
if(height[i]<height[j]){
i++;
}
else{
j--;
}
}
return max_val;
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -10,6 +10,8 @@
* 与一维查找类似。找到中间数字,每次移动可以舍弃掉一半的数字。由于给定的二维数组具备每行从左到右递增以及每列从上到下递增的特点,当访问到一个元素时,可以排除数组中的部分元素。
* 从二维数组的右上角开始查找。如果当前元素等于目标值,则返回 true。如果当前元素大于目标值则移到左边一列。如果当前元素小于目标值则移到下边一行。
* 从二维数组的左下角开始查找。具有完全一致的效果。
![](image/2021-04-12-21-52-57.png)
### 问题分类

View File

@@ -114,34 +114,73 @@
* 时间复杂度O(n)
* 空间复杂度O()
### 算法实现
```C++
class Solution {
public:
bool isNumber(string s) {
//1、从首尾寻找s中不为空格首尾位置也就是去除首尾空格.经过这一次,我发现。
// 自己真的菜的抠脚。想要完全事项自动状态机。太蠢了。需要判断一个个字符。
// 这个答案完美地绕过了。一个个字符的验证。0-9数字。正负号。小数点。指数符号。总共14个字符的排列组合。可能的所有情况。然后检测可能存在的所有反例。太强了。
int i=s.find_first_not_of(' ');
if(i==string::npos)return false;
int j=s.find_last_not_of(' ');
s=s.substr(i,j-i+1);
if(s.empty())return false;
//2、根据e来划分底数和指数
int e=s.find('e');
int E=s.find('E');
//3、指数为空判断底数
if(e==string::npos && E==string::npos)
return judgeP(s);
else if(E != string::npos)
return judgeP(s.substr(0,E))&&judgeS(s.substr(E+1));
//4、指数不为空判断底数和指数
else
return judgeP(s.substr(0,e))&&judgeS(s.substr(e+1));
}
bool judgeP(string s)//判断底数是否合法
{
bool result=false,point=false;
int n=s.size();
for(int i=0;i<n;++i)
{
if(s[i]=='+'||s[i]=='-'){//符号位不在第一位返回false
if(i!=0)return false;
}
else if(s[i]=='.'){
if(point)return false;//有多个小数点返回false
point=true;
}
else if(s[i]<'0'||s[i]>'9'){//非纯数字返回false
return false;
}
else{
result=true;
}
}
return result;
}
bool judgeS(string s)//判断指数是否合法
{
bool result=false;
//注意指数不能出现小数点,所以出现除符号位的非纯数字表示指数不合法
for(int i=0;i<s.size();++i)
{
if(s[i]=='+'||s[i]=='-'){//符号位不在第一位返回false
if(i!=0)return false;
}
else if(s[i]<'0'||s[i]>'9'){//非纯数字返回false
return false;
}
else{
result=true;
}
}
return result;
}
};
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB