相关介绍:

 二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树(英语:ordered binary tree),排序二叉树(英语:sorted binary tree),二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。

二叉查找树的定义:

 二叉查找树或者是一棵空树,或者是一棵具有以下性质的二叉树:

  1. 当左子树不为空,则左子树上所有节点的值均小于根节点的值

  2. 当右子树不为空,则右子树上所有节点的值均大于根节点的值

  3. 它的左右子树也都是二叉查找树

下图显示的二叉树便是一棵二叉查找树:

有趣的是,对二叉查找树进行中序遍历,得到的结果,其是一个按照关键字有序的记录序列

二叉查找树的相关知识点:

 对于二叉查找树,其主要的方法是查找、插入、删除相关的节点,以下介绍其相关的操作过程及给出相应的代码,在给出相关的操作之前,给出相关节点类的代码描述

二叉查找树的节点类

/**
* 二叉查找树的相关节点类
* @author 学徒
*
*/
class BinarySearchTreeNode
{
//节点的左子树指针
BinarySearchTreeNode left;
//节点的右子树指针
BinarySearchTreeNode right;
//关键字值
Comparable key;
//该节点的相关数据
Object data;
public BinarySearchTreeNode(BinarySearchTreeNode left,BinarySearchTreeNode right,Comparable key,Object data)
{
this.left=left;
this.right=right;
this.key=key;
this.data=data;
}
public BinarySearchTreeNode(Comparable key,Object data)
{
this(null,null,key,data);
}
}

二叉查找树的查找过程

 若将查找表组织为一棵二叉查找树,则根据二叉查找树的特点,查找过程的主要步骤归纳如下:

  1. 若查找树为空,则查找失败
  2. 若查找树非空,则
    1. 若给定值k等于根节点的关键字值,则查找成功,结束查找过程,否则转步骤2
    2. 若给定值k小于根节点的关键字值,则继续在根节点的左子树上进行,否则转3
    3. 若给定值k大于根节点的关键字值,则继续在根节点的右子树上进行

相关代码

/**
* 二叉查找树的查找操作
* @param data 需要进行查找的相关数据
* @return 查找到的相关的结果,当查找失败时,返回null
*/
public Object search(Comparable key)
{
if(key==null)
{
return null;
}
return toSearch(root,key);
}
/**
* 在二叉查找树中进行递归的查找的方法
* @param data 需要进行查找的数据
* @param node 二叉树中某个子树的根节点
* @return 查找的节点类的结果
*/
private Object toSearch(BinarySearchTreeNode node,Comparable key)
{
if(node==null)
{
return null;
}
//与根节点的相关值进行比较并选择合适的分支
int compara=node.key.compareTo(key);
if(compara==0)
return node.data;
else if(compara<0)
return toSearch(node.right,key);
else
return toSearch(node.left,key);
}

二叉查找树的插入操作

 二叉查找树中,插入一个新节点的过程为,假设待插入节点的关键字值为key,为了将其插入到表中,先要将它放入二叉排序树中进行查找,若查找成功,则按二叉查找树的定义,待插入节点已经存在,不必插入;否则,将新节点插入到表中。因此,新插入的节点一定是作为叶子节点添加到表中去的。其相关示例代码如下:

相关代码

/**
* 用于往二叉树中插入一个新的节点
* @param key 该节点的关键字
* @param data 该节点的相关数据
* @return 插入的结果,返回false有两种情况,一种是节点本身已存在,一种是需要插入的节点的关键字值为null
*/
public boolean insert(Comparable key,Object data)
{
if(key==null)
return false;
if(root==null)
{
root=new BinarySearchTreeNode(key,data);
return true;
}
else
return insertNode(root,key,data);
}
/**
* 往二叉查找树中插入相关的节点
* @param key 节点的关键字值
* @param data 节点的相关数据
* @param node 需要进行插入的子树的根节点
* @return 插入的结果
*/
public boolean insertNode(BinarySearchTreeNode node,Comparable key,Object data)
{
int compare=key.compareTo(node.key);
//已存在节点时,返回false
if(compare==0)
return false;
//要进行插入的节点比根节点的关键字值小的时候
else if(compare<0)
{
//左子树为空则插入左子树
if(node.left==null)
{
node.left=new BinarySearchTreeNode(key,data);
return true;
}
//非空则继续递归的查找
else
return insertNode(node.left,key,data);
}
else
{
if(node.right==null)
{
node.right=new BinarySearchTreeNode(key,data);
return true;
}
else
return insertNode(node.right,key,data);
}
}

二叉树的删除操作

 与在二叉树上进行插入操作的要求相同,从二叉查找树中删除一个节点,要保证删除后仍然是一棵二叉查找树。根据二叉查找树的结构特征,删除操作可分为4种情况来考虑:

  1. 若待删除的节点是叶子节点,则直接删除该节点即可。若该节点同时也是根节点,则删除后二叉查找树变为空树

  2. 若待删除的节点只有左子树,没有右子树,则根据二叉排序树的特点,可以直接将其左子树的根节点替代被删除节点的位置,即若被删除的节点为其双亲节点的左孩子,则将被删除节点的唯一左孩子收为其双亲节点的左孩子;否则收其为双亲节点的右孩子

  3. 待删除的节点只有右子树,而无左子树。则可以直接将其右子树的根节点替代被删除节点的位置,即若被删除的节点为其双亲节点的左孩子,则将被删除节点的唯一右孩子收其为双亲节点的左孩子;否则收为其双亲节点的右孩子

  4. 待删除节点既有左子树,又有右子树。根据二叉查找树的特点,可以用被删除节点的中序遍历序列下的前驱节点(或者中序遍历序列下的后继节点)替代被删除节点,同时删除其中序遍历序列下的前驱节点(或中序遍历序列下的后继节点)。而被删除节点(指的是新的删除节点)在中序遍历下的前驱无右子树(被删除节点在中序遍历下的无后继左子树)因而问题转化为情况2或者情况3

以下代码中,当其删除节点的左右子树均存在的时候,考虑的是选择其中序遍历序列下的后继节点替代被删除节点

相关代码

/**
* 用于删除操作
* @param key 删除的节点的关键字
* @return 返回所删除节点所对应的数据,当不存在关键字时返回null
*/
public Object delete(Comparable key)
{
if(key==null)
return null;
else
return remove(root,key,null);
} /**
* 用于删除操作所调用的一个方法,在以node为根的二叉查找树中删除关键字值为key的节点,parent为node的父节点,采用递归的方式
* @param node 以此节点为根的二叉树
* @param key 需要进行删除节点的关键字
* @param parent 以node节点为根的二叉树的node节点的父节点
* @return 所删除节点的数据
*/
public Object remove(BinarySearchTreeNode node,Comparable key,BinarySearchTreeNode parent)
{
if(node!=null)
{
int compare =key.compareTo(node.key);
//从左子树中进行删除
if(compare<0)
{
return remove(node.left,key,node);
}
//从右子树中进行删除
else if(compare>0)
{
return remove(node.right,key,node);
}
//当前节点即为要进行删除的节点
else
{
//当前要进行删除的节点的数据
Object result=node.data;
//当前要进行删除的节点的左右子树均存在
if(node.left!=null&&node.right!=null)
{
//寻找要进行删除节点的替换节点
BinarySearchTreeNode innext=node.right;
//进行进行替换的节点的父节点
BinarySearchTreeNode innextParent=node;
//寻找右子树下的最左孩子节点
while(innext.left!=null)
{
innextParent=innext;
innext=innext.left;
}
//改变删除节点的相关数据
node.data=innext.data;
node.key=innext.key;
//递归的从二叉查找树中删除要进行替换的节点
remove(node.right,innext.key,node);
}
//以下考虑的情况均当前删除节点缺少左子树或者右子树的情况
else
{
//当前要进行删除的节点不为根节点的时候
if(parent!=null)
{
//当左子树不为空的时候
if(node.left!=null&&node.right==null)
{
//当前节点为其左子树节点的时候
if(node==parent.left)
{
parent.left=node.left;
}
//当前节点为其右子树节点的时候
else
{
parent.right=node.left;
}
}
//当右子树不为空的时候
else if(node.left==null&&node.right!=null)
{
//当前节点为其左子树节点的时候
if(node==parent.left)
{
parent.left=node.right;
}
//当前节点为其右子树节点的时候
else
{
parent.right=node.right;
}
}
}
//当前删除的节点为根节点的时候
else
{
if(node.left!=null)
root=node.left;
else
root=node.right;
}
}
//返回其进行删除的节点的值
return result;
}
}
return null;
}

其完整代码如下:

package all_in_tree;
/**
* 该类用于演示二叉查找树相关的操作
* @author 学徒
*
*/
public class BinarySearchTree
{
//用于指向一棵二叉查找树的根节点的指针
private BinarySearchTreeNode root; /**
* 二叉查找树的查找操作
* @param data 需要进行查找的相关数据
* @return 查找到的相关的结果,当查找失败时,返回null
*/
public Object search(Comparable key)
{
if(key==null)
{
return null;
}
return toSearch(root,key);
}
/**
* 在二叉查找树中进行递归的查找的方法
* @param data 需要进行查找的数据
* @param node 二叉树中某个子树的根节点
* @return 查找的节点类的结果
*/
private Object toSearch(BinarySearchTreeNode node,Comparable key)
{
if(node==null)
{
return null;
}
//与根节点的相关值进行比较并选择合适的分支
int compara=node.key.compareTo(key);
if(compara==0)
return node.data;
else if(compara<0)
return toSearch(node.right,key);
else
return toSearch(node.left,key);
} /**
* 用于往二叉树中插入一个新的节点
* @param key 该节点的关键字
* @param data 该节点的相关数据
* @return 插入的结果,返回false有两种情况,一种是节点本身已存在,一种是需要插入的节点的关键字值为null
*/
public boolean insert(Comparable key,Object data)
{
if(key==null)
return false;
if(root==null)
{
root=new BinarySearchTreeNode(key,data);
return true;
}
else
return insertNode(root,key,data);
}
/**
* 往二叉查找树中插入相关的节点
* @param key 节点的关键字值
* @param data 节点的相关数据
* @param node 需要进行插入的子树的根节点
* @return 插入的结果
*/
public boolean insertNode(BinarySearchTreeNode node,Comparable key,Object data)
{
int compare=key.compareTo(node.key);
//已存在节点时,返回false
if(compare==0)
return false;
//要进行插入的节点比根节点的关键字值小的时候
else if(compare<0)
{
//左子树为空则插入左子树
if(node.left==null)
{
node.left=new BinarySearchTreeNode(key,data);
return true;
}
//非空则继续递归的查找
else
return insertNode(node.left,key,data);
}
else
{
if(node.right==null)
{
node.right=new BinarySearchTreeNode(key,data);
return true;
}
else
return insertNode(node.right,key,data);
}
} /**
* 用于删除操作
* @param key 删除的节点的关键字
* @return 返回所删除节点所对应的数据,当不存在关键字时返回null
*/
public Object delete(Comparable key)
{
if(key==null)
return null;
else
return remove(root,key,null);
} /**
* 用于删除操作所调用的一个方法,在以node为根的二叉查找树中删除关键字值为key的节点,parent为node的父节点,采用递归的方式
* @param node 以此节点为根的二叉树
* @param key 需要进行删除节点的关键字
* @param parent 以node节点为根的二叉树的node节点的父节点
* @return 所删除节点的数据
*/
public Object remove(BinarySearchTreeNode node,Comparable key,BinarySearchTreeNode parent)
{
if(node!=null)
{
int compare =key.compareTo(node.key);
//从左子树中进行删除
if(compare<0)
{
return remove(node.left,key,node);
}
//从右子树中进行删除
else if(compare>0)
{
return remove(node.right,key,node);
}
//当前节点即为要进行删除的节点
else
{
//当前要进行删除的节点的数据
Object result=node.data;
//当前要进行删除的节点的左右子树均存在
if(node.left!=null&&node.right!=null)
{
//寻找要进行删除节点的替换节点
BinarySearchTreeNode innext=node.right;
//进行进行替换的节点的父节点
BinarySearchTreeNode innextParent=node;
//寻找右子树下的最左孩子节点
while(innext.left!=null)
{
innextParent=innext;
innext=innext.left;
}
//从二叉查找树中删除该要进行替换的节点即节点innext
innextParent.left=null;
//改变删除节点的相关数据
node.data=innext.data;
node.key=innext.key;
}
//以下考虑的情况均当前删除节点缺少左子树或者右子树的情况
else
{
//当前要进行删除的节点不为根节点的时候
if(parent!=null)
{
//当左子树不为空的时候
if(node.left!=null&&node.right==null)
{
//当前节点为其左子树节点的时候
if(node==parent.left)
{
parent.left=node.left;
}
//当前节点为其右子树节点的时候
else
{
parent.right=node.left;
}
}
//当右子树不为空或者删除节点为叶子节点的时候
else
{
//当前节点为其左子树节点的时候
if(node==parent.left)
{
parent.left=node.right;
}
//当前节点为其右子树节点的时候
else
{
parent.right=node.right;
}
}
}
//当前删除的节点为根节点的时候
else
{
if(node.left!=null)
root=node.left;
else
root=node.right;
}
}
//返回其进行删除的节点的值
return result;
}
}
return null;
}
}

分析:对于二叉查找树,其树结构是在插入和删除节点中动态进行生成的,为此,其查找树的深度依赖于插入节点的顺序。为此,其可能出现如下图所示的一种情况。当插入节点的顺序为有序的时候(节点关键字为有序的)其可能会出现严重“偏拐”的现象而导致其插入和删除的效率均变为O(n)。而平衡二叉树的提出便是为了解决这个问题。详情请点击查看相关博文K:平衡二叉树(AVL)

回到目录|·(工)·)

K:二叉查找树(BST)的更多相关文章

  1. 【查找结构 2】二叉查找树 [BST]

    当所有的静态查找结构添加和删除一个数据的时候,整个结构都需要重建.这对于常常需要在查找过程中动态改变数据而言,是灾难性的.因此人们就必须去寻找高效的动态查找结构,我们在这讨论一个非常常用的动态查找树— ...

  2. 查找系列合集-二叉查找树BST

    一. 二叉树 1. 什么是二叉树? 在计算机科学中,二叉树是每个结点最多有两个子树的树结构. 通常子树被称作“左子树”(left subtree)和“右子树”(right subtree). 二叉树常 ...

  3. 二叉查找树BST 模板

    二叉查找树BST 就是二叉搜索树 二叉排序树. 就是满足 左儿子<父节点<右儿子 的一颗树,插入和查询复杂度最好情况都是logN的,写起来很简单.   根据BST的性质可以很好的解决这些东 ...

  4. 二叉查找树(BST)

    二叉查找树(BST):使用中序遍历可以得到一个有序的序列

  5. [学习笔记] 二叉查找树/BST

    平衡树前传之BST 二叉查找树(\(BST\)),是一个类似于堆的数据结构, 并且,它也是平衡树的基础. 因此,让我们来了解一下二叉查找树吧. (其实本篇是作为放在平衡树前的前置知识的,但为了避免重复 ...

  6. 二叉查找树(BST)的实现

    一.二叉树介绍 二叉查找树(Binary Search Tree,BST),又称二叉排序树,也称二叉搜索树,它或者是一颗空树,或者具有如下性质的树:若它的左子树不为空,则左子树上所有节点的值都小于根节 ...

  7. 3.2 符号表之二叉查找树BST

    一.插入和查找 1.二叉查找树(Binary Search Tree)是一棵二叉树,并且每个结点都含有一个Comparable的键,保证每个结点的键都大于其左子树中任意结点的键而小于其右子树的任意结点 ...

  8. 从一段简单算法题来谈二叉查找树(BST)的基础算法

    先给出一道很简单,喜闻乐见的二叉树算法题: 给出一个二叉查找树和一个目标值,如果其中有两个元素的和等于目标值则返回真,否则返回假. 例如: Input: 5 / \ 3 6 / \ \ 2 4 7 T ...

  9. 二叉查找树BST

    每棵子树头节点的值都比各自左子树上所有节点值要大,也都比各自右子树上所有节点值要小. 二叉查找树的中序遍历序列一定是从小到大排列的. 一个节点的后继节点是指,这个节点在中序遍历序列中的下一个节点.相应 ...

随机推荐

  1. PHP 访问链接的3种方式

    对于php访问url的方法比价多,对于一些防护比较低的网站,可以轻易的实现刷网站浏览量的可能 1.fopen方式 function access_url($url) { if ($url=='') r ...

  2. GCD介绍:Dispatch_source

    [转自:GCD介绍(三): Dispatch Sources] 何为Dispatch Sources 简单来说,dispatch source是一个监视某些类型事件的对象.当这些事件发生时,它自动将一 ...

  3. js中数组的操作方法

    今天给大家带来一篇有关数组操作方法的文章. 新建数组 方法一:通过new运算符创建一个数组构造函数. var arr = new Array(); 方法二:通过方括号直接创建直接量数组. var ar ...

  4. (RaspBerry Pi) Python GPIO 基本操作

    目前打算由潛入深慢慢學習RaspBerry Pi, 所以先由最容易下手的Python進入樹莓派的世界 首先要使用 GPIO 需要利用RPI.GPIO package想當然爾必須先安裝 所以先執行下列命 ...

  5. php中各种http报错的状态码分析

    HTTP会经常遇见错误,本文主要和大家分享php中各种http报错的状态码,希望能帮助到大家. HTTP 错误 400 400 请求出错 由于语法格式有误,服务器无法理解此请求.不作修改,客户程序就无 ...

  6. chm 已取消到该网页的导航,打不开!

    方法 11. 双击此 .chm 文件. 2. 在“打开文件安全警告”对话框,单击以清除“打开此文件前始终询问”复选框. 3. 单击“打开”. 方法 21. 右键单击该 CHM 文件,然后单击“属性”. ...

  7. springboot项目:Redis分布式锁的使用(模拟秒杀系统)

    模拟秒杀系统: 第一步:编写Service package com.payease.service; /** * liuxiaoming * 2017-12-14 */ public interfac ...

  8. Python离线安装依赖包

    1.制作requirement.txt pip freeze > requirement.txt 2.下载离线Pytho安装包 pip download -r requirement.txt - ...

  9. eclips中maven解决jsp报错的问题

    加入如下的pom依赖: <!-- 解决jsp报错的依赖包第一个 --> <dependency> <groupId>javax.servlet</groupI ...

  10. c++ 网络编程课设入门超详细教程 ---目录

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9663167.html c++ 网络编程(一)TCP/UDP windows/linux 下入门 ...