This commit is contained in:
youngyangyang04
2020-08-13 09:15:50 +08:00
parent 48be0193a7
commit 304e409f48
10 changed files with 199 additions and 52 deletions

View File

@ -2,63 +2,64 @@
## 思路
这道题目比较容易陷入两个陷阱:
* 陷阱1 [10,5,15,null,null,6,20] 这个case 要考虑道
* 陷阱2样例中根节点的val 可能是-2147483648
中序遍历下,输出的二叉搜索树节点的数值是有序序列,有了这个特性,**验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。**
中序遍历之后 输出的顺序就应该是一个从大到小的顺序, 可以使用一个全局变量进行比较
所以代码实现上,我们就使用递归法来中序遍历,遍历的过程中判断节点上的数值是不是递增的就可以了
这道题目比较容易陷入两个陷阱:
* 陷阱1 [10,5,15,null,null,6,20] 这个case 要考虑道
![二叉搜索树](https://img-blog.csdnimg.cn/20200812191501419.png)
10的右子树只能包含大于当前节点的数而右面出现了一个6 这就不符合了!
* 陷阱2样例中根节点的val 可能是int的最小值
问题可以进一步演进如果样例中根节点的val 可能是longlong的最小值 又要怎么办呢?看下文解答!
## C++代码
[10,5,15,null,null,6,20] 为什么预期结果是 false.... 这是经典陷阱
错误代码
定于全局变量初始化为long long最小值
```
class Solution {
public:
bool isValidBST(TreeNode* root) {
if (root == NULL) return true;
if (root->left != NULL && root->right != NULL) {
if (root->val > root->left->val && root->val < root->right->val) {
return true;
} else {
return false;
}
}
if (root->left != NULL && root->right == NULL) {
if (root->val > root->left->val) {
return true;
}else {
return false;
}
}
if (root->left == NULL && root->right != NULL) {
if (root->val < root->right->val) {
return true;
}else {
return false;
}
}
if (root->left == NULL && root->right == NULL) return true;
return isValidBST(root->left) && isValidBST(root->right);
}
};
```
正确代码
```
class Solution {
public:
long long val = LONG_MIN;
long long maxVal = LONG_MIN;
bool isValidBST(TreeNode* root) {
if (root == NULL) return true;
bool left = isValidBST(root->left);
if (val < root->val) val = root->val;// 中序遍历,这里相当于从大到小进行比较
// 中序遍历,验证遍历的元素是不是从小到大
if (maxVal < root->val) maxVal = root->val;
else return false;
bool right = isValidBST(root->right);
return left && right;
}
};
```
其实因为后台数据有int最小值测试用例所以都改成了longlong最小值。
如果测试数据中有 longlong的最小值怎么办不可能在初始化一个更小的值了吧。 建议避免 初始化最小值,如下方法取到最左面的数值:
```
class Solution {
public:
long long maxVal = 0; // 记录中序遍历的过程中出现过的最大值
bool flag = false; // 标记是否取到了最左面节点的数值
bool isValidBST(TreeNode* root) {
if (root == NULL) return true;
bool left = isValidBST(root->left);
if (!flag) {
maxVal = root->val;
flag = true;
} else {
// 中序遍历,这里相当于从大到小进行比较
if (maxVal < root->val) maxVal = root->val;
else return false;
}
bool right = isValidBST(root->right);
return left && right;
}
};
```
> 更多算法干货文章持续更新可以微信搜索「代码随想录」第一时间围观关注后回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等就可以获得我多年整理的学习资料。

View File

@ -177,7 +177,7 @@ public:
然后在反转result数组输出的结果顺序就是左右中了如下图
![前序到后序](https://img-blog.csdnimg.cn/20200808200338924.png)
所以后序遍历只需要前序遍历的代码稍作修改就可以了,代码如下:
**所以后序遍历只需要前序遍历的代码稍作修改就可以了,代码如下:**
```
class Solution {

View File

@ -3,7 +3,21 @@ https://leetcode-cn.com/problems/binary-tree-right-side-view/
## 思路
广度优先搜索模板题目层序遍历的时候将每一层的最后元素放入result数组中
这里再讲一遍二叉树的广度优先遍历(层序遍历)
需要借用一个辅助数据结构队列来实现,**队列先进先出,符合一层一层遍历的逻辑,而是用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。**
使用队列实现广度优先遍历,动画如下:
<video src='../video/102二叉树的层序遍历.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
这样就实现了层序从左到右遍历二叉树。
建议先做一下这道题目[0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md) 在做这道,就会发现这是一道 广度优先搜索模板题目层序遍历的时候将每一层的最后元素放入result数组中
层序遍历的时候,将单层的最后面的元素放进数组中,随后返回数组就可以了。
代码如下:
## C++代码

View File

@ -2,8 +2,15 @@
https://leetcode-cn.com/problems/minimum-size-subarray-sum/
## 思路
这道题目 暴力解法当然是 两个for循环然后不断的寻找符合条件的子序列
这块我们还可以使用滑动窗口的细想来做这道题。所谓滑动窗口,**就是不断的调节子序列的起始位置,从而得出我们要想的结果**
这道题目暴力解法当然是 两个for循环然后不断的寻找符合条件的子序列时间复杂度很明显是O(n^2) 。
还可以使用滑动窗口的细想来做这道题。所谓滑动窗口,**就是不断的调节子序列的起始位置,从而得出我们要想的结果**
这里还是以题目中的示例来举例s=7 数组是 231243来看一下动画效果
<video src='../video/209.长度最小的子数组.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
代码如下:
## 暴力解法

View File

@ -7,12 +7,46 @@ https://leetcode-cn.com/problems/sliding-window-maximum/
暴力方法遍历一遍的过程中每次从窗口中在找到最大的数值这样很明显是O(n * k)的算法。
有的同学可能会想用一个大顶堆也就是优先级队列来存放这个窗口里的k个数字这样就可以知道最大的最大值是多少了 但是问题是这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了。所以不能用大顶堆。
有的同学可能会想用一个大顶堆也就是优先级队列来存放这个窗口里的k个数字这样就可以知道最大的最大值是多少了 **但是问题是这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了。所以不能用大顶堆。**
使用单调队列,即单调递减或单调递增的队列。 它不是一个独立的数据结构,需要使用其他数据结构来实现单调队列,例如: dequedeque是双向队列可以选择 从头部或者尾部pop同样也可以从头部或者尾部push。
**使用单调队列,即单调递减或单调递增的队列。它不是一个独立的数据结构,需要使用其他数据结构来实现单调队列**,例如: dequedeque是双向队列可以选择 从头部或者尾部pop同样也可以从头部或者尾部push。
不要以为实现的单调队列就是 对窗口里面的数进行排序,如果排序的话,那和优先级队列又有什么区别了呢。
使用deque实现的单调队列如下代码详细注释
```
class MyQueue { //单调队列(从大到小)
public:
deque<int> que; // 使用deque来实现单调队列
// 每次弹出的时候比较当前要弹出的数值是否等于队列前端的数值如果相等在pop数据当然也要判断队列当前是否为空。
void pop(int value) {
if (!que.empty() && value == que.front()) {
que.pop_front();
}
}
// 如果push的数值大于后端的数值那么就将队列后端的数值弹出直到push的数值小于等于 队列后端的数值为止。
// 然后再将数值push到队列后端这样就保持了队列里的数值是单调从大到小的了。
void push(int value) {
while (!que.empty() && value > que.back()) {
que.pop_back();
}
que.push_back(value);
}
// 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
int front() {
return que.front();
}
};
```
动画解释如下:
<video src='../video/0239.滑动窗口最大值.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
这样我们就用deque实现了一个单调队列接下来解决滑动窗口最大值的问题就很简单了。
详情看代码吧,已经简洁。
## C++代码
@ -55,5 +89,14 @@ public:
}
};
```
来看一下时间复杂度,时间复杂度: O(n)
有的同学可能想了,在队里中 push元素的过程中还有pop操作呢感觉不是纯粹了O(n)。
其实大家可以自己观察一下单调队列的实现nums 中的每个元素最多也就被 push_back 和 pop_back 一次,没有任何多余操作,所以整体的复杂度还是 O(n)。
空间复杂度因为我们定义一个辅助队列所以是O(k)。
> 更过算法干货文章持续更新可以微信搜索「代码随想录」第一时间围观关注后回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等就可以获得我多年整理的学习资料。

View File

@ -3,7 +3,83 @@ https://leetcode-cn.com/problems/search-in-a-binary-search-tree/
## 思路
注意这里是二叉搜索树,是已经排序的了,两种实现,递归和迭代
### 递归法
先来看递归的实现方式。
依然从递归三要素开始分析:
* 确定递归函数的参数和返回值
* 确定终止条件
* 确定单层递归的逻辑
1. 确定递归函数的参数和返回值
递归函数的参数传入的就是根节点和要搜索的数值,返回的就是以这个搜索数值所在的节点。
代码如下:
```
TreeNode* searchBST(TreeNode* root, int val)
```
2. 确定终止条件
如果root为空或者找到这个数值了就返回root节点。
```
if (root == NULL || root->val == val) return root;
```
3. 确定单层递归的逻辑
来看一下二叉搜索树的单层递归逻辑有何不同, 因为二叉搜索树的节点是有序的所以可以有方向的去搜索如果root->val > val搜索左子树如果root->val < val就搜索右子树最后如果都没有搜索到就返回NULL
代码如下
```
if (root->val > val) return searchBST(root->left, val);
if (root->val < val) return searchBST(root->right, val);
return NULL;
```
这里可能会疑惑在递归遍历的时候什么时候直接return 递归函数的返回值什么时候不用加这个 return 这取决于对递归函数的定义这里定义的递归函数就是返回 要查找的元素所在的节点而这个节点就是我们所求所以直接return递归函数的返回值
整体代码如下
```
TreeNode* searchBST(TreeNode* root, int val) {
if (root == NULL || root->val == val) return root;
if (root->val > val) return searchBST(root->left, val);
if (root->val < val) return searchBST(root->right, val);
return NULL;
}
```
### 迭代法
一提到二叉树遍历的迭代法可能立刻想起使用栈来模拟深度遍历使用队列来模拟广度遍历其实因为二叉搜索树的特殊性也就是节点的有序性可以不使用辅助栈或者队列就可以写出迭代法
对于一般二叉树模拟递归的过程中还有一个回溯的过程例如走一个左方向的分支走到头了那么要调头在走右分支而对于二叉搜索树不需要回溯的过程因为节点的有序性就帮我们确定了搜索的方向
看如下图中的这颗二叉搜索树例如要搜索元素为3的节点我们不需要搜索其他节点也不需要做回溯查找的路径已经规划好了
![二叉搜索树](https://img-blog.csdnimg.cn/20200812190213280.png)
迭代法代码如下
```
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
while (root != NULL) {
if (root->val > val) root = root->left;
else if (root->val < val) root = root->right;
else return root;
}
return NULL;
}
};
```
## C++代码

View File

@ -3,7 +3,13 @@ https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/
## 思路
这道题目就像是我们玩过的游戏对对碰, 可以把字符串放到与一个栈中,然后如果相同的话 栈就弹出,这样最后栈里剩下的元素都是相邻不相同的元素了
这道题目就像是我们玩过的游戏对对碰, 可以把字符串放到与一个栈中,然后如果相同的话 栈就弹出,这样最后栈里剩下的元素都是相邻不相同的元素了
如动画所示:
<video src='../video/1047.删除字符串中的所有相邻重复项.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
从栈中弹出剩余元素此时是字符串ac因为从栈里弹出的元素是倒叙的所以在对字符串进行反转一下就得到了最终的结果。
## C++代码

Binary file not shown.

Binary file not shown.