mirror of
https://github.com/Estom/notes.git
synced 2026-02-03 02:23:31 +08:00
算法
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,4 @@
|
||||
*.exe
|
||||
*.pyc
|
||||
__pycache__
|
||||
.vscode
|
||||
*.class
|
||||
.vscode/launch.json
|
||||
|
||||
22
.vscode/c_cpp_properties.json
vendored
Normal file
22
.vscode/c_cpp_properties.json
vendored
Normal 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
18
.vscode/note.code-snippets
vendored
Normal 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
18
.vscode/settings.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using namespace std;
|
||||
int main()
|
||||
{
|
||||
vector<int> vec;
|
||||
vec.push_back(1);
|
||||
cout << "hello world" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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. 当前元素出栈的时候,说明当前元素是倒数第二小的元素。
|
||||
|
||||
|
||||
一般会采取怎样的操作呢?具体再总结。
|
||||
59
算法/A类:基本算法/5.4 最长回文串.md
Normal file
59
算法/A类:基本算法/5.4 最长回文串.md
Normal 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,表示最大长度为n,i=0,和i=1时,值都初始化为1;j增长的边界是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;
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
162
算法/B类:数据结构算法/3.3 接雨水问题.md
Normal file
162
算法/B类:数据结构算法/3.3 接雨水问题.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 接雨水问题
|
||||
|
||||
> 这个问题也太经典了,能用的这些思路,也太神奇了。
|
||||
|
||||
## 1 接雨水问题
|
||||
|
||||
|
||||
|
||||
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
|
||||
|
||||

|
||||
|
||||
|
||||
上面是由数组 [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 时,如果栈内至少有两个元素,记栈顶元素为top,top 的下面一个元素是 left,则一定有 height[left]≥height[top]。如果 height[i]>height[top],则得到一个可以接雨水的区域,该区域的宽度i−left−1,高度是min(height[left],height[i])−height[top],根据宽度和高度即可计算得到该区域能接的雨水量。
|
||||
|
||||
* 为了得到 left,需要将 top 出栈。在对 top 计算能接的雨水量之后, left 变成新的 top,重复上述操作,直到栈变为空,或者栈顶下标对应的 height 中的元素大于或等于height[i]。
|
||||
|
||||

|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度: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;
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
# 数组与滑动窗口
|
||||
|
||||
## 1.2 最长不含重复字符的子字符串——滑动窗口
|
||||
## 1 最长不含重复字符的子字符串——滑动窗口
|
||||
|
||||
### 问题描述
|
||||
|
||||
@@ -55,4 +55,52 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
## 2 盛最多水的容器
|
||||
|
||||
### 问题描述
|
||||
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
|
||||
|
||||
说明:你不能倾斜容器。
|
||||
|
||||

|
||||
|
||||
```
|
||||
输入:[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)的题非常类似。都是舍弃掉坏的情况,进行线性贪心。
|
||||
|
||||
|
||||
### 算法设计
|
||||
* 双指针贪心思想:对应数字较小的那个指针以后不可能作为容器的边界了,将其丢弃,并移动对应的指针。
|
||||
* 计算公式:两个指针指向的数字中较小值∗指针之间的距离
|
||||

|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度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;
|
||||
}
|
||||
```
|
||||
BIN
算法/B类:数据结构算法/image/2021-04-12-21-46-57.png
Normal file
BIN
算法/B类:数据结构算法/image/2021-04-12-21-46-57.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
算法/B类:数据结构算法/image/2021-04-12-21-55-39.png
Normal file
BIN
算法/B类:数据结构算法/image/2021-04-12-21-55-39.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
BIN
算法/B类:数据结构算法/image/2021-04-12-22-36-09.png
Normal file
BIN
算法/B类:数据结构算法/image/2021-04-12-22-36-09.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
BIN
算法/B类:数据结构算法/image/2021-04-12-22-55-49.png
Normal file
BIN
算法/B类:数据结构算法/image/2021-04-12-22-55-49.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -10,6 +10,8 @@
|
||||
|
||||
* 与一维查找类似。找到中间数字,每次移动可以舍弃掉一半的数字。由于给定的二维数组具备每行从左到右递增以及每列从上到下递增的特点,当访问到一个元素时,可以排除数组中的部分元素。
|
||||
* 从二维数组的右上角开始查找。如果当前元素等于目标值,则返回 true。如果当前元素大于目标值,则移到左边一列。如果当前元素小于目标值,则移到下边一行。
|
||||
* 从二维数组的左下角开始查找。具有完全一致的效果。
|
||||

|
||||
|
||||
|
||||
### 问题分类
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
BIN
算法/C类:问题类型算法/image/2021-04-12-21-52-57.png
Normal file
BIN
算法/C类:问题类型算法/image/2021-04-12-21-52-57.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Reference in New Issue
Block a user