引言

本文来自于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的更多相关文章

  1. 二叉树系列 - 二叉搜索树 - [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 ...

  2. 二叉树、二叉搜索树、平衡二叉树、B树、B+树的精确定义和区别探究

    概述 关于树的概念很多,B树,B+树,红黑树等等. 但是你去翻翻百度百科,或者用百度或者谷歌搜索一下中文的树结构的介绍,全都是狗屁.没有哪个中文网站是真正精确解释树的定义的,尤其是百度百科. 下面我要 ...

  3. 数据结构中的树(二叉树、二叉搜索树、AVL树)

    数据结构动图展示网站 树的概念 树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>=1)个有限节点组成一个具有 ...

  4. 把二叉搜索树转化成更大的树 · Convert BST to Greater Tree

    [抄题]: 给定二叉搜索树(BST),将其转换为更大的树,使原始BST上每个节点的值都更改为在原始树中大于等于该节点值的节点值之和(包括该节点). Given a binary search Tree ...

  5. B-Tree 漫谈 (从二叉树到二叉搜索树到平衡树到红黑树到B树到B+树到B*树)

    关于B树的学习还是需要做点笔记. B树是为磁盘或者其他直接存取辅助存储设备而设计的一种平衡查找树.B树与红黑树的不同在于,B树可以有很多子女,从几个到几千个.比如一个分支因子为1001,高度为2的B树 ...

  6. BinarySearchTree二叉搜索树的实现

    /* 二叉搜索树(Binary Search Tree),(又:二叉查找树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; ...

  7. 【数据结构05】红-黑树基础----二叉搜索树(Binary Search Tree)

    目录 1.二分法引言 2.二叉搜索树定义 3.二叉搜索树的CRUD 4.二叉搜索树的两种极端情况 5.二叉搜索树总结 前言 在[算法04]树与二叉树中,已经介绍过了关于树的一些基本概念以及二叉树的前中 ...

  8. Java二叉搜索树实现

    树集合了数组(查找速度快)和链表(插入.删除速度快)的优点 二叉树是一种特殊的树,即:树中的每个节点最多只能有两个子节点 二叉搜索树是一种特殊的二叉树,即:节点的左子节点的值都小于这个节点的值,节点的 ...

  9. HDU-3719 二叉搜索树

    http://acm.hdu.edu.cn/showproblem.php?pid=3791 用数组建立二叉树: 二叉搜索树 Time Limit: 2000/1000 MS (Java/Others ...

随机推荐

  1. .NET Remoting学习笔记(三)信道

    目录 .NET Remoting学习笔记(一)概念 .NET Remoting学习笔记(二)激活方式 .NET Remoting学习笔记(三)信道 参考:♂风车车.Net .NET Framework ...

  2. php中的邮件技术

    前言:程序员不谈恋爱就是对物质世界太贪恋 一.通过smtp服务来发送邮件 使用php中的mail()实现邮件的发送; bool mail ( string $to , string $subject ...

  3. ps、grep和kill联合使用杀掉进程

    例如要杀掉hello这个进程,使用下面这个命令就能直接实现.   ps -ef |grep hello |awk '{print $2}'|xargs kill -9 这里是输出ps -ef |gre ...

  4. Maven学习总结(一副本)——Maven配置和搭建

    环境准备: JDK 1.6 Maven 3.0.4 myeclipse 8.6.1 安装 Maven 之前要求先确定你的 JDK 已经安装配置完成.Maven是 Apache 下的一个项目,目前最新版 ...

  5. Leetcode 130 Surrounded Regions DFS

    将内部的O点变成X input X X X XX O O X X X O XX O X X output X X X XX X X XX X X XX O X X DFS的基本框架是 void dfs ...

  6. Windows server 2008 R2充当路由器实现网络的互联(转)

    1.路由器的工作原理 当IP子网中的一台主机发送IP分组给同一IP子网的另一台主机时,它将直接把IP分组送到网络上,对方就能收到.而要送给不同IP子网上的主机时,它要 选择一个能到达目的子网上的路由器 ...

  7. Docker实践(5)—资源隔离

    Docker使用cgroup实现CPU,内存和磁盘IO等系统资源的限制. CPU Docker现在有2个与CPU资源相关的参数,-c可以指定CPU的占比,--cpuset可以绑定CPU.例如,指定容器 ...

  8. 将Sql Server迁移到Always on集群 - 账号的同步

    Always on环境的建立,网上资料很多,主要是windows集群的建立以及Sql Server Always on的建立,略 容易忽略的是Sql server账号同步问题(Always on能实现 ...

  9. WCF 数据服务 4.5

    .NET Framework 4.5 其他版本 WCF 数据服务(以前称为"ADO.NET Data Services")是 .NET Framework 的一个组件.可以使用此组 ...

  10. End of HTML blink tag

    Recently I have read a news which said "Firfox 23 nixes support for outdated blink HTML tag&quo ...