diff --git a/.gitignore b/.gitignore index 3d921587..6eac2867 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ *.exe *.pyc __pycache__ -.vscode *.class -.vscode/launch.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..1be3b2d4 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -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 +} \ No newline at end of file diff --git a/.vscode/note.code-snippets b/.vscode/note.code-snippets new file mode 100644 index 00000000..851a1925 --- /dev/null +++ b/.vscode/note.code-snippets @@ -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" + // } +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..32207109 --- /dev/null +++ b/.vscode/settings.json @@ -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" + } +} \ No newline at end of file diff --git a/Vscode/C/hello.cpp b/Vscode/C/hello.cpp index 45957fa5..4a87d45d 100644 --- a/Vscode/C/hello.cpp +++ b/Vscode/C/hello.cpp @@ -5,6 +5,7 @@ using namespace std; int main() { vector vec; + vec.push_back(1); cout << "hello world" << endl; return 0; } diff --git a/数据结构/3.1 单调栈.md b/数据结构/3.1 单调栈.md index 729da925..d699323d 100644 --- a/数据结构/3.1 单调栈.md +++ b/数据结构/3.1 单调栈.md @@ -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. 当前元素出栈的时候,说明当前元素是倒数第二小的元素。 + + +一般会采取怎样的操作呢?具体再总结。 \ No newline at end of file diff --git a/算法/A类:基本算法/5.4 最长回文串.md b/算法/A类:基本算法/5.4 最长回文串.md new file mode 100644 index 00000000..ae753db6 --- /dev/null +++ b/算法/A类:基本算法/5.4 最长回文串.md @@ -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> vec(n+1,vector(n,0)); + for(int i=0;imax){ + max=i; + result = s.substr(j,i); + } + } + } + } + return result; + } +}; +``` \ No newline at end of file diff --git a/算法/B类:数据结构算法/3.1 单调栈-局部顺序.md b/算法/B类:数据结构算法/3.1 单调栈-局部顺序.md index 8ee126c1..90b10510 100644 --- a/算法/B类:数据结构算法/3.1 单调栈-局部顺序.md +++ b/算法/B类:数据结构算法/3.1 单调栈-局部顺序.md @@ -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(); } }; -``` \ No newline at end of file +``` + diff --git a/算法/B类:数据结构算法/3.3 接雨水问题.md b/算法/B类:数据结构算法/3.3 接雨水问题.md new file mode 100644 index 00000000..4f8ff866 --- /dev/null +++ b/算法/B类:数据结构算法/3.3 接雨水问题.md @@ -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& height) { + int n=height.size(); + vector left(n,0); + vector right(n,0); + + int max_left=0,max_right=0; + for(int i=0;imax_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;iheight[top],则得到一个可以接雨水的区域,该区域的宽度i−left−1,高度是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& height) { + int ans = 0; + stack 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& 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; + } +}; +``` diff --git a/算法/B类:数据结构算法/4.3 数组与滑动窗口.md b/算法/B类:数据结构算法/4.3 数组与滑动窗口.md index 3bd34875..e6bbfcee 100644 --- a/算法/B类:数据结构算法/4.3 数组与滑动窗口.md +++ b/算法/B类:数据结构算法/4.3 数组与滑动窗口.md @@ -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 轴共同构成的容器可以容纳最多的水。 + +说明:你不能倾斜容器。 + +![](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& height) { + int i=0,j=height.size()-1; + int max_val=0; + while(i'9'){//非纯数字,返回false + return false; + } + else{ + result=true; + } + } + return result; + } - - - - - - - - - - - - - - - - - - - - - - - - - - - - + bool judgeS(string s)//判断指数是否合法 + { + bool result=false; + //注意指数不能出现小数点,所以出现除符号位的非纯数字表示指数不合法 + for(int i=0;i'9'){//非纯数字,返回false + return false; + } + else{ + result=true; + } + } + return result; + } +}; +``` diff --git a/算法/C类:问题类型算法/image/2021-04-12-21-52-57.png b/算法/C类:问题类型算法/image/2021-04-12-21-52-57.png new file mode 100644 index 00000000..549f7ff3 Binary files /dev/null and b/算法/C类:问题类型算法/image/2021-04-12-21-52-57.png differ