二叉树系列 - 二叉搜索树 - [LeetCode] 中序遍历中利用 pre节点避免额外空间。题:Recover Binary Search Tree,Validate Binary Search Tree
二叉搜索树是常用的概念,它的定义如下:
- The left subtree of a node contains only nodes with keys less than the node's key.
- The right subtree of a node contains only nodes with keys greater than the node's key.
- Both the left and right subtrees must also be binary search trees.
我们以LeetCode上的一个例题开始。
例题一:
Given a binary tree, determine if it is a valid binary search tree (BST).
要求的函数如下:
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode *root) {
}
};
如果对二叉搜索树不够了解,可能会在思路上犯一个错误:将current结点的值和左右孩子比较,如果满足要求(即current结点的值大于左孩子,小于右孩子),就递归调用isValidBST 验证左右孩子为根结点的子树。
这样的验证方式是不对的,因为二叉搜索树的要求是:current 结点值大于左子树所有结点值,小于右子树所有结点值。上面的验证方式只能保证左右子树的根结点满足这种要求。
一个正确的验证思路是:利用二叉搜索树中序遍历是递增序列的特点,来完成验证。
那么,如何实行呢?最简单的方法是将中序遍历结果存到一个数组中,然后从头到尾扫描一遍数组,完成验证。但这样的结法除了递归遍历所需要的O(logn)空间外,还需要 O(n)的辅助空间来做数组。有没有不需要辅助空间的办法?
这就是这篇博文被记录的目的:中序遍历中利用pre节点,来避免使用额外空间。
pre节点其实就是一个额外的TreeNode,它的作用是存储上一次遍历的结点。
TreeNode *pre = NULL;
func(cur){
func(cur -> left);
pre = cur;
func(cur -> right);
}
这道例题就可以这样解决:
class Solution {
public:
bool isValidBST(TreeNode *root) {
if(!root) return true;
if(!isValidBST(root -> left)) return false;
if(pre && pre -> val >= root -> val) return false;
pre = root;
if(!isValidBST(root -> right)) return false;
return true;
}
private:
TreeNode* pre = NULL;
};
例题二:
Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
有了之前的基本思想,这道题其实就可以转化为:一个递增序列中有两个值被交换了位置,如何恢复递增序列?
我们依然可以运用上面的技巧,在二叉搜索树的遍历过程中,不停比较pre结点和current结点的值,出现 pre值比current值还大的情况,就将要交换的结点保存下来。具体存储哪两个结点作为交换节点,这个在代码的注释中解释了:如果只碰到一处比较异常,那么最后交换这两个结点的值即可;如果碰到两处比较异常,那么我们将第一次异常的pre的值和第二次异常的current值交换。
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void recoverTree(TreeNode *root) {
if(!root) return;
findWrongNd(root);
int temp = wnd1 -> val;
wnd1 -> val = wnd2 -> val;
wnd2 -> val = temp;
}
private:
TreeNode* wnd1 = NULL;
TreeNode* wnd2 = NULL;
TreeNode* pre = NULL; void findWrongNd(TreeNode* root){
if(!root) return;
findWrongNd(root -> left);
if(pre){
if(pre -> val > root -> val){
if(!wnd1){
wnd1 = pre; //If only one descending pair has been found, save this pair into wnd1, wnd2.
wnd2 = root;
}else{
wnd2 = root; //if second descending pair is found, swap the bigger one in first pair, with smaller one in second pair. So wnd1 does not need to be changed, wnd2 = root, because currently root's value < pre's value.
}
}
}
pre = root;
findWrongNd(root -> right);
}
};
总结:这种引入Pre指针的小技巧,虽然简单,但是可以节省空间。
二叉树系列 - 二叉搜索树 - [LeetCode] 中序遍历中利用 pre节点避免额外空间。题:Recover Binary Search Tree,Validate Binary Search Tree的更多相关文章
- 剑指 Offer 33. 二叉搜索树的后序遍历序列 + 根据二叉树的后序遍历序列判断对应的二叉树是否存在
剑指 Offer 33. 二叉搜索树的后序遍历序列 Offer_33 题目详情 题解分析 本题需要注意的是,这是基于一颗二叉排序树的题目,根据排序二叉树的定义,中序遍历序列就是数据从小到大的排序序列. ...
- 剑指OFFER之从二叉搜索树的后序遍历序列(九度OJ1367)
题目描述: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 输入: 每个测试案例包括2行: 第一行为1个整数 ...
- 剑指offer面试题24:二叉搜索树的后序遍历序列
题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是返回true,否则返回false. 假设输入的数组任意两个数字都不相同 解题思路:二叉搜索树的特点是根节点的左子树的值小于等 ...
- 【Java】 剑指offer(33) 二叉搜索树的后序遍历序列
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如 ...
- 【剑指offer】判断一个序列是否是二叉搜索树的后序遍历,C++实现
原创文章,转载请注明出处! 本题牛客网地址 博客文章索引地址 博客文章中代码的github地址 1.题目 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出N ...
- 剑指Offer的学习笔记(C#篇)-- 二叉搜索树的后序遍历序列
题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 一 . 解题思想与二叉搜索树概念 (1). 二叉树 ...
- 【Offer】[33] 【二叉搜索树的后序遍历序列】
题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果.如果是则返回true, 否则返回false. 假设输入的数组的任意两个数字 ...
- 剑指Offer-23.二叉搜索树的后序遍历序列(C++/Java)
题目: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 分析: 二叉树的后序遍历也就是先访问左子树,再访问右 ...
- 剑指offer 24:二叉搜索树的后序遍历序列
题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 解题思路 后序遍历,顾名思义根节点位于尾部,故可将 ...
随机推荐
- 怎么给kibana加上权限?
更新:2016-05-20 09:36 通过向Elastic了解,他们目前开发的5.0版本支持更好的权限定制,粒度达到字段级别.他们预计今年就可以上线,如果你不是有一个旧版本的kibanba非要维护不 ...
- Python中如何Getting Help
在Python中Gettting Help有如下两种方法: 1 使用dir函数,dir的参数可以是一个真正的对象实例,也可以是一个数据类型,无论哪种情形,dir函数都返回与这个对象或者数据类型相关联的 ...
- Code obfuscatio (翻译!)
Description Kostya likes Codeforces contests very much. However, he is very disappointed that his so ...
- Ubuntu 基础操作 基础命令 热键 man手册使用 关机 重启等命令使用
. : 关机, 如果将Linux默认运行等级设置为0, 系统将无法启动; -- : 多用户模式, 允许使用网络文件系统, 一般不使用图形界面登陆就是这种模式; -- : 多用户图形界面模式, 该模式下 ...
- 【IdentityServer4文档】- 支持协议
IdentityServer 实现了以下协议: OpenID Connect OpenID Connect Core 1.0 (spec) OpenID Connect Discovery 1.0 ( ...
- ubuntu 安装lua错误
转自:http://www.cnblogs.com/softidea/archive/2016/03/02/5236498.html lua.c:80:31: fatal error: readlin ...
- return 返回字符串
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...
- 【python】python字符串前面加u,r,b的含义
1.字符串前加 u 例:u"我是含有中文字符组成的字符串." 作用:后面字符串以 Unicode 格式 进行编码,一般用在中文字符串前面,防止因为源码储存格式问题,导致再次使用时出 ...
- BZOJ 1005 明明的烦恼(prufer序列+高精度)
有一种东西叫树的prufer序列,一个树的与一个prufer序列是一一对应的关系. 设有m个度数确定的点,这些点的度为dee[i],那么每个点在prufer序列中出现了dee[i]-1次. 由排列组合 ...
- winform全局异常捕获
/// <summary> /// 应用程序的主入口点. /// </summary> public static ApplicationContext context; [S ...