[线索二叉树] [LeetCode] 不需要栈或者别的辅助空间,完成二叉树的中序遍历。题:Recover Binary Search Tree,Binary Tree Inorder Traversal
既上篇关于二叉搜索树的文章后,这篇文章介绍一种针对二叉树的新的中序遍历方式,它的特点是不需要递归或者使用栈,而是纯粹使用循环的方式,完成中序遍历。
线索二叉树介绍
首先我们引入“线索二叉树”的概念:
"A binary tree is threaded by making all right child pointers that would normally be null point to the inorder successor of the node, and all left child pointers that would normally be null point to the inorder predecessor of the node."
线索二叉树的构建方法:
consider a node k that has a right child r. Then the left pointer of r must be either a child or a thread back to k. In the case that r has a left child, that left child must in turn have either a left child of its own or a thread back to k, and so on for all successive left children.
线索二叉树由A. J. Perlis和 C. Thornton发明,他们利用二叉树中那些没有左孩子或者右孩子(或都没有)的结点,将它们的left 和 right指针利用起来,作为辅助指针,指向上层的某一个结点。
这种新的遍历方式的基本思路: 遍历的过程会不断给一些NULL的指针赋值,使其成为辅助指针,来构建线索二叉树;同时也会不断将不再需要的辅助指针置回NULL。完成遍历后,树的结构不会改变。
在实际遍历过程中,辅助指针并不需要left, right都赋值,可以只赋值right或者只赋值 left,就达到中序遍历全部结点的效果。
下面是只赋值right 作为辅助指针完成中序遍历的伪代码:
while(cur != NULL){
if(cur -> left == NULL) output cur;
else{
pre = cur;
TreeNode* lchild = pre -> left;
TreeNode* node = find right most child of lchild, or right child which equals to pre; if(right most child of lchild){ //此时需要构造辅助指针,放入node的right指针中
node -> right = pre;
cur = pre -> left; //搜寻下一级left child
}else{ //若辅助指针已经被构造过,删除辅助指针,输出辅助指针所指向的结点,也就是pre
node -> right = NULL;
output pre;
cur = pre -> right; //搜寻下一级right child
}
}
}
这个遍历方式也是LeetCode中 Binary Tree Inorder Traversal 一题的解法之一。
附题目,Binary Tree Inorder Traversal
Given a binary tree, return the inorder traversal of its nodes' values.
For example:
Given binary tree {1,#,2,3}
,
1
\
2
/
3
return [1,3,2]
.
Note: Recursive solution is trivial, could you do it iteratively?
实现代码,输出部分高亮表示。
vector<int> inorderTraversal(TreeNode *root) {
vector<int> v;
if(NULL == root) return v;
TreeNode *cur, *pre;
cur = root;
while(cur){
if(!cur -> left){
v.push_back(cur -> val);
cur = cur -> right;
}else{
pre = cur;
cur = cur -> left;
while(cur -> right && cur -> right != pre){
cur = cur -> right;
}
if(!cur -> right){
cur -> right = pre;
cur = pre -> left;
}else{
cur -> right = NULL;
v.push_back(pre -> val);
cur = pre -> right;
}
}
}
return v;
}
应用
介绍完了这种遍历方式,我们谈谈它的引申应用。
我们依旧以上篇博文中的第二题为例,但是这一次,加了一个额外的要求:空间复杂度必须为constant。
Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
Note:
A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?
上篇博文中的解题方法是使用递归进行中序遍历,但是递归的调用使得程序需要额外的栈空间。
解题的思路依然是中序遍历,遍历的同时进行比较preTrav和current结点的值。只不过这里中序遍历的方式变成了构建和删除线索二叉树。
将上面的代码做一些修改,把存到vector的部分变成比较和存储结点(高亮部分),就完成了。
/**
* 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;
TreeNode *wnd1 = NULL, *wnd2 = NULL;
TreeNode *cur = root, *pre = NULL, *preTrav = NULL;
while(cur){
if(!cur -> left){
//record nodes to swap
if(preTrav && (preTrav -> val > cur -> val)){
if(!wnd1){
wnd1 = preTrav;
}
wnd2 = cur;
}
preTrav = cur; cur = cur -> right;
}else{
pre = cur;
cur = cur -> left;
while(cur -> right && cur -> right != pre){
cur = cur -> right;
}
if(!cur -> right){
cur -> right = pre;
cur = pre -> left;
}else{
cur -> right = NULL;
//record nodes to swap
if(preTrav && (preTrav -> val > pre -> val)){
if(!wnd1){
wnd1 = preTrav;
}
wnd2 = pre;
}
preTrav = pre; cur = pre -> right;
}
}
}
int temp = wnd1 -> val;
wnd1 -> val = wnd2 -> val;
wnd2 -> val = temp;
}
};
当然,其实这种思路依然是需要额外空间的:那些用来完成线索二叉树的指针,虽然之后被删除,但是会临时占用一部分空间。所以,严格意义上说,这也不是Constant的空间复杂度解法。但是,权且作为一种新的思路,供大家参考。
参考:
水中的鱼 [LeetCode] Recover Binary Search Tree 解题报告
《数据结构(C++描述)》,金远平编著,清华大学出版社 - 4.5.2 中序遍历线索二叉树
http://en.wikipedia.org/wiki/Threaded_binary_tree
[线索二叉树] [LeetCode] 不需要栈或者别的辅助空间,完成二叉树的中序遍历。题:Recover Binary Search Tree,Binary Tree Inorder Traversal的更多相关文章
- [LeetCode] Binary Tree Inorder Traversal 二叉树的中序遍历
Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...
- LeetCode(94):二叉树的中序遍历
Medium! 题目描述: 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗 ...
- 【2】【leetcode-105,106】 从前序与中序遍历序列构造二叉树,从中序与后序遍历序列构造二叉树
105. 从前序与中序遍历序列构造二叉树 (没思路,典型记住思路好做) 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [ ...
- 小小c#算法题 - 11 - 二叉树的构造及先序遍历、中序遍历、后序遍历
在上一篇文章 小小c#算法题 - 10 - 求树的深度中,用到了树的数据结构,树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集.但在那篇文章中,只 ...
- Leetcode 94. 二叉树的中序遍历
1.问题描述 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 2.解法一 ...
- leetcode 94二叉树的中序遍历
递归算法C++代码: /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; ...
- 二叉树中序遍历,先序遍历,后序遍历(递归栈,非递归栈,Morris Traversal)
例题 中序遍历94. Binary Tree Inorder Traversal 先序遍历144. Binary Tree Preorder Traversal 后序遍历145. Binary Tre ...
- LeetCode 94 | 基础题,如何不用递归中序遍历二叉树?
今天是LeetCode专题第60篇文章,我们一起来看的是LeetCode的94题,二叉树的中序遍历. 这道题的官方难度是Medium,点赞3304,反对只有140,通过率有63.2%,在Medium的 ...
- [LeetCode] Construct Binary Tree from Preorder and Inorder Traversal 由先序和中序遍历建立二叉树
Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that ...
随机推荐
- ES6的新特性(15)——Promise 对象
Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了 ...
- USACO 2.4.4 Bessie Come Home 回家(最短路)
Description 现在是晚餐时间,而母牛们在外面分散的牧场中. 农民约翰按响了电铃,所以她们开始向谷仓走去. 你的工作是要指出哪只母牛会最先到达谷仓(在给出的测试数据中,总会有且只有一只速度最快 ...
- C#高级编程 (第六版) 学习 第七章:委托和事件
第七章 委托和事件 回调(callback)函数是Windows编程的一个重要方面,实际上是方法调用的指针,也称为函数指针. .Net以委托的形式实现了函数指针的概念,.Net的委托是类型安全的. 委 ...
- virtualbox 5.0.6 在debian jessie amd64启动报错
通过dmesg发现vboxdrv启动报错: [ 18.844888] systemd[1]: [/lib/systemd/system/vboxdrv.service:5] Failed to add ...
- 【final】Scrum站立会议第2次....11.20
小组名称:nice! 组长:李权 成员:于淼 刘芳芳韩媛媛 宫丽君 项目内容:约跑app(约吧) 时间:2016.11.9 12:00——12:30 地点:传媒西楼220室 本次对fnal阶段 ...
- [历史百科]抗战时期兵团简介 From 百度知道
中央军委1948年11月1日和1949年1月15日两次关于统一全军组织和部队番号的训令,我军先后进行了整编.西北野战军改称第一野战军,司令员兼政治委员彭德怀,第一副司令员张宗逊,第二副司令员赵寿山,参 ...
- mysql 慢查询,查询缓存,索引,备份,水平分割
1.开启慢查询 在mysql的配置文件my.ini最后增加如下命令 [mysqld]port=3306slow_query_log =1long_query_time = 1 2.查看慢查询记录 默认 ...
- php面试必知必会常见问题
1 说出常用的10个数组方法 我觉得数组比较最能体现PHP基础语法的一个数据结构了,下面给大家列一下常用的10个关于操作数组的函数 in_array(判断数组中是否有某个元素) implode(将数组 ...
- 常用的Redis客户端的并发模型(转)
伪代码模型 # get lock : timestamp = current Unix time + lock = SETNX lock.foo timestamp or (now() > ...
- HDU 3579——Hello Kiki
好久没写什么数论,同余之类的东西了. 昨天第一次用了剩余定理解题,今天上百度搜了一下hdu中国剩余定理.于是就发现了这个题目. 题目的意思很简单.就是告诉你n个m[i],和n个a[i].表示一个数对m ...