二叉树系列 - 二叉搜索树 - 线性时间内把有序链表转化为BST
引言
本文来自于Google的一道题目:
how to merge two binary search tree into balanced binary search tree. how to merge two binary search tree into balanced binary search tree.. Let there be m elements in first tree and n elements in the other tree. Your merge function should take O(m+n) time and do it in place.
http://www.careercup.com/question?id=5261732222074880
要线性时间内完成,而且要求in place,不难想到把BST转换为Double linked list。
然后将两个dll merge。但是,存在线性时间复杂度的算法,把dll转化成BST吗?
下面就是本文要记述的内容,算法参考自
http://www.geeksforgeeks.org/in-place-conversion-of-sorted-dll-to-balanced-bst/
已排序的 Double linked list 转化为 BST
因为要求线性时间,所以必须保证DLL上的每个节点只被访问 consant 次,最好是一次。
既然要求每个节点只能被访问一次,那么从根节点构造肯定是不行的。这种算法让BST从叶节点开始被构造,通过灵活地运用递归,巧妙地实现了自底向上构造BST的过程,而且还能保证BST是平衡的。
#include<iostream>
#include<stack>
using namespace std; struct BSTNode{
int val;
BSTNode *left;
BSTNode *right;
BSTNode(int v): val(v), left(NULL), right(NULL){}
}; class BSTtoDLL{
public:
BSTNode *func(BSTNode *root){
BSTtoDLLCore(root);
return head;
} private:
BSTNode* pre = NULL;
BSTNode* head = NULL;
void BSTtoDLLCore(BSTNode *root){
if(!root) return;
BSTtoDLLCore(root -> left);
if(pre){
pre -> right = root;
root -> left = pre;
}else{
head = root;
}
pre = root;
BSTtoDLLCore(root -> right);
}
}; class DLLtoBalancedBST{
public:
BSTNode* func(BSTNode* head){
if(!head) return head;
int n = ;
for(BSTNode *p = head; p; ++n, p = p -> right);
return DLLtoBalancedBSTCore(&head, n);
}
private:
//DLL to BST in place, Time O(N), Space O(LogN) for stack, N is the amount of nodes.
//DLL needs to be sorted.
BSTNode* DLLtoBalancedBSTCore(BSTNode** headref, int n){
if(n == ) return NULL;
BSTNode* left = DLLtoBalancedBSTCore(headref, n/);
BSTNode *root = *headref;
root -> left = left;
*headref = root -> right;
root -> right = DLLtoBalancedBSTCore(headref, n-n/-);
return root;
} }; void InorderPrint(BSTNode* root){
if(!root) return;
stack<BSTNode *> st;
while(!st.empty() || root){
if(!root){
root = st.top();
st.pop();
cout << root -> val << ' ';
root = root -> right;
}else{
st.push(root);
root = root -> left;
}
}
cout << endl;
} int main(){ //Construct oringal BST
BSTNode *root = new BSTNode();
BSTNode *left3 = new BSTNode();
BSTNode *left1 = new BSTNode();
BSTNode *left2 = new BSTNode();
BSTNode *right6 = new BSTNode();
BSTNode *right7 = new BSTNode(); root -> left = left2;
left2 -> left = left1;
left2 -> right = left3;
root -> right = right7;
right7 -> left = right6; cout << "-------Inorder print BST-------\n";
InorderPrint(root); //Convert BST to DLL
BSTtoDLL bstdll;
BSTNode *head = bstdll.func(root);
BSTNode *p = head;
cout << "-------print converted double linked list----------\n";
for(; p->right != NULL; cout << p -> val << ' ', p = p -> right);
cout << endl;
for(; p != NULL; cout << p -> val << ' ', p = p -> left);
cout << endl; //Convert DLL back to Balenced BST
DLLtoBalancedBST dllbst;
BSTNode *con_root = dllbst.func(head);
cout << "-------Inorder print converted BST-------\n";
InorderPrint(con_root); return ;
}
高亮部分为转化过程。
每次递归,headaddr这个指向节点的指针向末尾移动一次。因此每个节点只被访问一次,时间复杂度是线性的。
我们可以发现,这种算法对单向链表也适用。当然单链表不能保证in place,必须新申明节点,但是时间复杂度依然是线性的。
下面给出单向链表上的实现。
已排序的单向Linked list 转化为BST
#include<iostream>
#include<stack>
using namespace std; struct ListNode{
int val;
ListNode* next;
ListNode(int v): val(v), next(NULL){}
}; struct BSTnode{
int val;
BSTnode* left;
BSTnode* right;
BSTnode(int v): val(v), left(NULL), right(NULL){}
}; BSTnode *LLtoBSTCore(ListNode **headaddr, int n){
if(n <= ) return NULL;
BSTnode *left = LLtoBSTCore(headaddr, n/);
BSTnode *root = new BSTnode((*headaddr) -> val);
root -> left = left;
*headaddr = (*headaddr) -> next;
root -> right = LLtoBSTCore(headaddr, n-n/-);
return root;
} BSTnode *LLtoBST(ListNode *head){
if(!head) return NULL;
int n = ; ListNode *p = head;
for(; p; ++n, p = p -> next);
return LLtoBSTCore(&head, n);
} int main(){
ListNode *head = new ListNode();
ListNode *end = head;
for(int i = ; i <= ; end -> next = new ListNode(i++), end = end -> next);
cout << "List: \n";
for(ListNode *p = head; p; cout << p -> val << ' ', p = p -> next);
cout << endl; BSTnode *root = LLtoBST(head); cout << "BST inorder: " << endl;
stack<BSTnode *> st;
while(!st.empty() || root){
if(!root){
root = st.top();
st.pop();
cout << root -> val << " ";
root = root -> right;
}else{
st.push(root);
root = root -> left;
}
}
cout << endl; }
高亮部分为转化过程。
给一个已排序的序列的 Iterator,将序列转化为BST
再推而广之,对于给定一个只能向 next 移动的iterator,通过这个算法也能构造将 iterator 经过的节点构造为BST。不过我们事先知道或者计算出序列的长度。
这里给出C#的实现代码。IEnumerator 就是一个迭代器,支持MoveNext() - 将迭代器往后移,Current 属性 - 返回迭代器当前所指向的值。
高亮部分为基于IEnumrator的构造过程。
因为接口IEnumerator 不是.NET CLR 中的 primitive type(基元类型),因此将 IEnumerator 赋值或者作为函数参数时,都是按引用传递,这正是我们需要的。因此C#的实现代码省去了C++中使用 两个**的麻烦。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApplication1
{
public class BSTNode<T>
{
public T val;
public BSTNode<T> left;
public BSTNode<T> right;
public BSTNode(T v)
{
val = v;
left = null;
right = null;
}
} /// <summary>
/// Build Binary search tree from given iterator
/// </summary>
public class BuildBST
{
public BSTNode<T> BuildFromIEt<T>(IEnumerator<T> it, int n)
{
if (n == ) return null;
BSTNode<T> left = BuildFromIEt(it, n / );
it.MoveNext();
BSTNode<T> node = new BSTNode<T>(it.Current);
node.left = left;
node.right = BuildFromIEt(it, n - n / - ); return node;
}
} /// <summary>
/// A class to out put nodes in a tree
/// </summary>
public class TreeTrav
{
public static void Inorde<T>(BSTNode<T> root)
{
if (root == null) return;
Stack<BSTNode<T>> st = new Stack<BSTNode<T>>();
while (root != null || st.Count > )
{
if (root == null)
{
root = st.Peek();
st.Pop();
Console.Write(root.val.ToString() + ' ');
root = root.right;
}
else
{
st.Push(root);
root = root.left;
}
}
Console.WriteLine();
}
} class Program
{ static void Main(string[] args)
{
IEnumerable<int> list = new List<int>() { , , , , , }; Console.WriteLine("----Original list----");
for (IEnumerator<int> it = list.GetEnumerator(); it.MoveNext(); Console.Write(it.Current.ToString() + ' ')) ;
Console.WriteLine(); BuildBST buildBST = new BuildBST();
BSTNode<int> root = buildBST.BuildFromIEt(list.GetEnumerator(), list.Count());
Console.WriteLine("----Inorder traverse generated BST----");
TreeTrav.Inorde(root);
Console.Read();
}
}
}
二叉树系列 - 二叉搜索树 - 线性时间内把有序链表转化为BST的更多相关文章
- 二叉树系列 - 二叉搜索树 - [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 ke ...
- 二叉树、二叉搜索树、平衡二叉树、B树、B+树的精确定义和区别探究
概述 关于树的概念很多,B树,B+树,红黑树等等. 但是你去翻翻百度百科,或者用百度或者谷歌搜索一下中文的树结构的介绍,全都是狗屁.没有哪个中文网站是真正精确解释树的定义的,尤其是百度百科. 下面我要 ...
- 数据结构中的树(二叉树、二叉搜索树、AVL树)
数据结构动图展示网站 树的概念 树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>=1)个有限节点组成一个具有 ...
- 把二叉搜索树转化成更大的树 · Convert BST to Greater Tree
[抄题]: 给定二叉搜索树(BST),将其转换为更大的树,使原始BST上每个节点的值都更改为在原始树中大于等于该节点值的节点值之和(包括该节点). Given a binary search Tree ...
- B-Tree 漫谈 (从二叉树到二叉搜索树到平衡树到红黑树到B树到B+树到B*树)
关于B树的学习还是需要做点笔记. B树是为磁盘或者其他直接存取辅助存储设备而设计的一种平衡查找树.B树与红黑树的不同在于,B树可以有很多子女,从几个到几千个.比如一个分支因子为1001,高度为2的B树 ...
- BinarySearchTree二叉搜索树的实现
/* 二叉搜索树(Binary Search Tree),(又:二叉查找树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; ...
- 【数据结构05】红-黑树基础----二叉搜索树(Binary Search Tree)
目录 1.二分法引言 2.二叉搜索树定义 3.二叉搜索树的CRUD 4.二叉搜索树的两种极端情况 5.二叉搜索树总结 前言 在[算法04]树与二叉树中,已经介绍过了关于树的一些基本概念以及二叉树的前中 ...
- Java二叉搜索树实现
树集合了数组(查找速度快)和链表(插入.删除速度快)的优点 二叉树是一种特殊的树,即:树中的每个节点最多只能有两个子节点 二叉搜索树是一种特殊的二叉树,即:节点的左子节点的值都小于这个节点的值,节点的 ...
- HDU-3719 二叉搜索树
http://acm.hdu.edu.cn/showproblem.php?pid=3791 用数组建立二叉树: 二叉搜索树 Time Limit: 2000/1000 MS (Java/Others ...
随机推荐
- web前端基础——jQuery编程进阶
1 jQuery本质 jQuery不是一门独立的语言,它是JavaScript的一个类库或框架.jQuery的核心思想就是:选取元素,对其操作.很多时候写jQuery代码的关键就是怎样设计合适的选择器 ...
- mysql组合索引与字段顺序
很多时候,我们在mysql中创建了索引,但是某些查询还是很慢,根本就没有使用到索引!一般来说,可能是某些字段没有创建索引,或者是组合索引中字段的顺序与查询语句中字段的顺序不符. 看下面的例子:假设有一 ...
- 今天踩过的坑——structs和phpmyadmin
phpmyadmin 错误:缺少 mcrypt 扩展解决mv -i /etc/php5/conf.d/mcrypt.ini /etc/php5/mods-available/sudo php5enmo ...
- IPv6正则表达式
斯蒂芬·瑞恩写了一个非常有用的正则表达式,可用于匹配任何一个合法的IPv6地址.以下为正则表达式的代码: ^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|: ...
- 《STL系列》之map原理及实现
上一篇文章<STL系列>之vector原理及实现,介绍了vector的原理及实现,这篇文章介绍map的原理及实现.STL实现源码下载.STL中map的实现是基于RBTree的,我在实现的时 ...
- [Aaronyang] 写给自己的WPF4.5 笔记21 [3d课 2/4]
1. 当然复杂的3d模型我们是可以通过更专业的工具做出来,然后导入项目中,我们只是方便演示,选择简单的图形. Tip: 关于摄像机的NearPlaneDistance和FarPlaneDistance ...
- Scala 深入浅出实战经典 第63讲:Scala中隐式类代码实战详解
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- mysql自动化安装
MySQL安装一般使用RPM或者源码安装的方式.RPM安装的优点是快速,方便.缺点是不能自定义安装目录.如果需要调整数据文件和日志文件的存放位置,还需要进行一些手动调整.源码安装的优点是可以自定义安装 ...
- 理解TCP为什么需要进行三次握手(白话)
原文地址:http://www.cnblogs.com/yuilin/archive/2012/11/05/2755298.html 首先简单介绍一下TCP三次握手 在TCP/IP协议中,TCP协议提 ...
- <《巴菲特之道 (第三版)》>读书笔记
以便宜的价格买入长期绩优的股票 他把对公司的投资看做是拥有公司的一部分股权,看重的是公司的长期经济价值 别人越是草率,我们越要加倍慎重 如果你发现自己已经在陷阱中,最重要的是想办法让自己不要再往下陷 ...