查找树是一种数据结构,它支持多种动态集合操作。包含Search、Minimum、Maximum、PreDecessor、Successor、Insert、Delete等。它既能够用作字典,也能够用作优先级队列;在二叉查找树(Binary Search Tree)上执行基本操作的时间与树的高度成正比,对于一颗含有n个结点的全然二叉树,基本操作的最坏情况执行时间为floor(logn)。

本章讨论二叉查找树的基本性质以及上面提及的基本操作的实现。

GitHub 程序实现代码

1 二叉查找树

1.1 性质

例如以下图所看到的。一颗二叉查找树是依照二叉树结构来组织的。这种树一般用链表结构表示,每个结点是一个对象,包含关键字key、父亲结点parent、左儿子结点left以及右儿子结点right四个属性。



明显的,对于二叉查找树中关键字的存储方式总是满足这种性质:

设x为二叉查找树中的一个结点。假设y是x左子树中的一个结点,则y−>key<=x−>key,假设y是x右子树中的一个结点,则y−>key>=x−>key。

1.2 基本操作

1.2.1 遍历

依据二叉查找树的性质。能够用一个递归算法依照排列顺序依次输出全部关键字,这就是中序遍历,遍历顺序为根、左子树、右子树。相同的,有前序遍历,根的关键字在左右子树之前输出。后序遍历。根的关键字在其左右子树之后输出。

1.2.2 查找

对于二叉查找树,最常见的操作就是查找树中的某个关键字。

查找操作相同採用递归的形式实现,其复杂度等于树的高度。

操作步骤例如以下图所看到的:

1.2.3 求最大、最小关键字

对于用作优先级队列的结构。求最大最小关键字是不可缺少的操作。

要查找二叉查找树中的最小关键字,依据树的性质,仅仅要从根节点開始,沿着各个结点的left指针查找下去,直到遇到NULL为止。

同理,要查找二叉查找树中的最大关键字,依据树的性质。仅仅要从根节点開始,沿着各个结点的right指针查找下去,直到遇到NULL为止。

这两个操作的执行时间都是O(h)。

1.2.4 求前驱、后继

我们知道。中序遍历二叉查找树得到的是一组有序序列,有时候须要求指定结点的前驱与后继。对于给定结点x的后继结点是具有大于或等于x−>key中关键字的最小结点;同理,对于给定结点x的前驱结点是具有小于x−>key中关键字的最大结点。依据二叉查找树的性质,不用对关键字做不论什么比較就能够得到给定结点的前驱和后继结点。

1.2.5 插入

插入和删除操作会引起整个二叉查找树表示的集合的动态变化。要反应出这种变化。就要改动数据结构,可是在改动的同一时候。还要保持整棵树的性质不变。

将新值插入到一颗二叉查找树中的步骤例如以下:

1.2.6 删除

相对于插入操作。删除更加复杂一些。下图具体展示了删除不同结点须要的步骤:



对高度为h的二叉查找树,动态集合操作Insert与Delete的执行时间都是O(n)。

2 二叉查找树程序实现

以下给出的程序实现。包含了全部以上提及的基本操作:

(1)BinarySearchTree.h

#ifndef _BINARYSEARCHTREE_H_
#define _BINARYSEARCHTREE_H_ #include <iostream> typedef struct BSTNode{
BSTNode *left;
BSTNode *right;
BSTNode *parent; int key; BSTNode(int data) : left(NULL), right(NULL), parent(NULL), key(data){}
}; class BinarySearchTree{
public:
BinarySearchTree();
~BinarySearchTree(); //插入删除操作
void Insert(int data);
BSTNode *Delete(int data);
BSTNode *root;
}; //查找操作
BSTNode *Search(BSTNode * node, int data); //遍历操作
void InOrderWalk(BSTNode * node);
void PreOrderWalk(BSTNode * node);
void PostOrderWalk(BSTNode * node); //查询最大最小值
BSTNode *Maximum(BSTNode * node);
BSTNode *Minimum(BSTNode * node); //查找前驱与后继
BSTNode *PreDecessor(BSTNode *node);
BSTNode *Successor(BSTNode *node); #endif

(2)BinarySearchTree.cpp

#include "BinarySearchTree.h"
#include <iostream> BinarySearchTree::BinarySearchTree()
{
root = NULL;
} BinarySearchTree::~BinarySearchTree()
{
delete root;
} //向二分查找树中插入数据data
void BinarySearchTree::Insert(int data)
{
BSTNode *node = new BSTNode(data);
BSTNode *p = root, *q = NULL;
while (p != NULL)
{
q = p;
if (p->key > data)
p = p->left;
else
p = p->right;
}
node->parent = q;
if (q == NULL)
root = node;
else if (q->key > data)
q->left = node;
else
q->right = node;
} //从二分查找树中删除数据
BSTNode *BinarySearchTree::Delete(int data)
{
BSTNode *node = Search(root, data);
BSTNode *ret , *tmp; if (node == NULL)
return node; //假设目标结点仅仅有一个子女则删除该结点,否则删除其后继结点
if (node->left == NULL || node->right == NULL)
ret = node;
else
ret = Successor(node); //假设被删结点有左右孩子,将其链接到被删结点的父节点
if (ret->left != NULL)
tmp = ret->left;
else
tmp = ret->right; if (tmp != NULL)
tmp->parent = ret->parent;
//假设被删结点的父节点为空,则说明要删的是根节点
if (ret->parent == NULL)
root = tmp;
else if (ret == ret->parent->left)
ret->parent->left = tmp;
else
ret->parent->right = tmp; if (ret != node)
node->key = ret->key; return ret;
} //查找以node结点为根的子树中值为data的结点
BSTNode *Search(BSTNode * node, int data)
{
if (node == NULL || node->key == data)
return node;
if (data < node->key)
return Search(node->left, data);
else
return Search(node->right, data);
} //遍历操作
void InOrderWalk(BSTNode * node)
{
if (node != NULL)
{
InOrderWalk(node->left);
std::cout << node->key << "\t";
InOrderWalk(node->right);
}
} void PreOrderWalk(BSTNode * node)
{
if (node != NULL)
{
std::cout << node->key << "\t";
InOrderWalk(node->left);
InOrderWalk(node->right);
}
} void PostOrderWalk(BSTNode * node)
{
InOrderWalk(node->left);
InOrderWalk(node->right);
std::cout << node->key << "\t";
} //查询最大最小值
BSTNode *Maximum(BSTNode * node)
{
while (node->left != NULL)
node = node->left;
return node;
} BSTNode *Minimum(BSTNode * node)
{
while (node->right != NULL)
node = node->right;
return node;
} //查找前驱与后继
BSTNode *PreDecessor(BSTNode *node)
{
if (node->left != NULL)
return Maximum(node->left); BSTNode *p = node->parent;
while (p != NULL && node == p->left)
{
node = p;
p = node->parent;
} return p;
} BSTNode *Successor(BSTNode *node)
{
if (node->right != NULL)
return Minimum(node->right);
BSTNode *p = node->parent;
while (p != NULL && node == p->right)
{
node = p;
p = node->parent;
} return p;
}

(3)main.cpp

#include "BinarySearchTree.h"
#include <iostream>
#include <cstdlib>
#include <ctime> using namespace std; const int MAX = 101;
const int N = 10; int main()
{
BinarySearchTree *bst = new BinarySearchTree(); //设置随机化种子,避免每次产生相同的随机数
srand(time(0)); //构造一个随机数组成的二分查找树
for (int i = 0; i < N; i++)
bst->Insert(rand() % MAX); //遍历查找树
cout << "先序遍历二分查找树bst:" << endl;
PreOrderWalk(bst->root); //遍历查找树
cout << endl << "中序遍历二分查找树bst:" << endl;
InOrderWalk(bst->root); //遍历查找树
cout << endl << "后序遍历二分查找树bst:" << endl;
PostOrderWalk(bst->root); int x = 45;
BSTNode *node = Search(bst->root, x);
if (node)
{
cout << endl << "二分查找树bst中存在结点x = " << x << endl;
bst->Delete(x); cout << endl << "删除二分查找树中结点x = " << x << "成功!" << endl; //遍历查找树
cout << endl << "先序遍历二分查找树bst:" << endl;
PreOrderWalk(bst->root); //遍历查找树
cout << endl << "中序遍历二分查找树bst:" << endl;
InOrderWalk(bst->root); //遍历查找树
cout << endl << "后序遍历二分查找树bst:" << endl;
PostOrderWalk(bst->root);
cout << endl << endl;
}
else{
cout << endl << "二分查找树bst中不存在结点x = " << x << endl;
cout << endl << endl;
} system("pause");
return 0;
}

測试结果(查找失败的情况):



測试结果(查找成功并删除):

3 随机构造的二叉查找树

在本章的最后介绍了一种随机构造二叉查找树的理论方法,这主要是针对普通二叉查找树基本操作执行时间O(h)考虑的。这种方式能够使得:

一棵在n个关键字上随机构造的二叉查找树的期望高度为O(logn)。

这一部分感觉应用不多吧,木有细致看,^||^~~

《算法导论》— Chapter 12 二叉查找树的更多相关文章

  1. 基于visual Studio2013解决算法导论之030二叉查找树

     题目 二叉查找树 解决代码及点评 // 12二叉查找树.cpp : 定义控制台应用程序的入口点. // // 3 - 10二叉搜索树查找.cpp : 定义控制台应用程序的入口点. // #in ...

  2. 算法导论——lec 12 平摊分析与优先队列

    在平摊分析中,运行一系列数据结构操作所须要的时间是通过对运行的全部操作求平均得出.反映在不论什么情况下(即最坏情况下),每一个操作具有平均性能.掌握了平摊分析主要有三种方法,聚集分析.记账方法.势能方 ...

  3. [算法导论]二叉查找树的实现 @ Python

    <算法导论>第三版的BST(二叉查找树)的实现: class Tree: def __init__(self): self.root = None # Definition for a b ...

  4. (搬运)《算法导论》习题解答 Chapter 22.1-1(入度和出度)

    (搬运)<算法导论>习题解答 Chapter 22.1-1(入度和出度) 思路:遍历邻接列表即可; 伪代码: for u 属于 Vertex for v属于 Adj[u] outdegre ...

  5. 《算法导论》 — Chapter 7 高速排序

    序 高速排序(QuickSort)也是一种排序算法,对包括n个数组的输入数组.最坏情况执行时间为O(n^2). 尽管这个最坏情况执行时间比較差.可是高速排序一般是用于排序的最佳有用选择.这是由于其平均 ...

  6. "《算法导论》之‘树’":二叉查找树

    树的介绍部分摘取自博文二叉查找树(一).二叉查找树(二).二叉查找树. 1. 树的介绍 1.1 树的定义 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合. 把它叫做“ ...

  7. 《算法导论》— Chapter 15 动态规划

    序 算法导论一书的第四部分-高级设计和分析技术从本章开始讨论,主要分析高效算法的三种重要技术:动态规划.贪心算法以及平摊分析三种. 首先,本章讨论动态规划,它是通过组合子问题的解而解决整个问题的,通常 ...

  8. 《算法导论》 — Chapter 7 快速排序

    序 快速排序(QuickSort)也是一种排序算法,对包含n个数组的输入数组,最坏情况运行时间为O(n^2).虽然这个最坏情况运行时间比较差,但是快速排序通常是用于排序的最佳实用选择,这是因为其平均性 ...

  9. 基本数据结构(2)——算法导论(12)

    1. 引言     这一篇博文主要介绍链表(linked list),指针和对象的实现,以及有根树的表示. 2. 链表(linked list) (1) 链表介绍      我们在上一篇中提过,栈与队 ...

随机推荐

  1. 关于ibatis的实体类部分属性无法映射

    今天在编码中不小心就遇到了一个ibatis初学者偶尔会遇到的问题. 先来看这张图中的DictionPo的部分属性赋值都为空.其实,数据库中是有数据的. 再来看看mapper的写法 上面那个对象查询语句 ...

  2. 几种通讯协议的比较RMI > Httpinvoker >= Hessian >> Burlap >> web service (转)

    一.综述 本文比较了RMI,Hessian,Burlap,Httpinvoker,web service等5种通讯协议的在不同的数据结构和不同数据量时的传输性能.RMI是java语言本身提供的通讯协议 ...

  3. 利用端口映射解决:拥有公网IP有限,内网需要访问因特网

    动态端口映射:   内网中的一台电脑要访问新浪网,会向NAT网关发送数据包,包头中包括对方(就是新浪网)IP.端口和本机IP.端口,NAT网关会把本机IP.端口替换成自己的公网IP.一个未使用的端口, ...

  4. 微信公众平台消息接口PHP版

    使用前提条件:拥有一个公网上的HTTP服务器主机空间,具有创建目录.上传文件等权限.推荐新浪的SAE.http://sae.sina.com.cn/ 首先请注册微信公众平台的账号,注册地址:http: ...

  5. cat /proc/iomem

    在proc目录下有iomem和ioports文件,其主要描述了系统的io内存和io端口资源分布. 对于外设的访问,最终都是通过读写设备上的寄存器实现的,寄存器不外乎:控制寄存器.状态寄存器和数据寄存器 ...

  6. [bug]未能从程序集“System.ServiceModel, Version=3.0.0.0问题解决

    在Windows Server 2008中的IIS服务器中部署WCF服务程序时,通过浏览器访问报出如下错误: 未能从程序集“System.ServiceModel, Version=3.0.0.0, ...

  7. java开源内容管理系统J4CMS支持真正静态化

    原理非常easy,使用httpclient请求遍历整个站点的菜单.文章链接.请求下来以后,生成html文件.即静态化了 把它们稍作调整,直接扔在88元购买的阿里云主机上.站点就完毕了 这是我的 静态站 ...

  8. Netty(四):粘包问题描述及解决

    拆包粘包问题解决 netty使用tcp/ip协议传输数据.而tcp/ip协议是类似水流一样的数据传输方式.多次访问的时候有可能出现数据粘包的问题,解决这种问题的方式如下: 1 定长数据流 客户端和服务 ...

  9. drawer navigation, tabhostFragment 默认导向

    问题: 项目结构,在一个抽屉导航中的第一个抽屉中加入了一个tabHostFragment,每次进入项目都是在抽屉导航的默认界面,须要把抽屉划出来再选择某个抽屉. 可是想让APP直接跳转到第一个抽屉界面 ...

  10. iOS之美: UIView 与 UIWindow之间的关系

    转自:http://leopard168.blog.163.com/blog/static/168471844201381584533466/ 面对iOS初学者,总会被问到一些不常被关注的问题,比如: ...