一、二叉树介绍

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

  结论:中序遍历一颗二叉查找树可以得到一个按关键字递增的有序序列。简单来说,比根小的往左边放  比根大的往右边放。

    

二、代码实现

  1、BST结点类:

 public class BSTNode<K, V> {
public K key;
public V value;
public BSTNode<K, V> left;
public BSTNode<K, V> right;
public BSTNode<K, V> parent;
public boolean isLeftChild;
public int height;
public int num;
public boolean isRed = true; // 后面才学习的红黑树 public BSTNode() {
} public BSTNode(K key, V value, BSTNode<K, V> left, BSTNode<K, V> right, BSTNode<K, V> parent) {
super();
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.parent = parent;
} public boolean isLeft() {
return isLeftChild;
} public boolean isRight() {
return !isLeftChild;
} @Override
public String toString() {
return (isRed ? "红色" : "黑色") + " [" + key + "]<-" + (parent == null ? "" : parent.key);
}
}

  2、BST接口:

 import java.util.List;
import java.util.function.Consumer; public interface IBinarySearchTree<K, V> {
/**
* 新增节点
* @param k 关键字
* @param v 值
*/
BSTNode<K, V> insert(K k, V v); /**
* 中序遍历
* @param con 处理中序遍历的每个元素的函数
*/
void inorder(Consumer<K> con); /**
* 查找元素
* @param key
* @return
*/
V lookupValue(K key); /**
* 获取最小关键字
* @return
*/
K min(); /**
* 获取最大关键字
* @return
*/
K max(); /**
* 移除关键字对应的节点
* @param key
*/
void remove(K key); /**
* x的后继——比x大的第一个元素 1、是其右子树的最小值
* 2、没有右子树,则向上追溯,直到某个祖先节点是左孩子,返回这个祖先节点的父节点,它就是x的后继
*
* @param x
* @return
*/
K successor(K x); /**
* 前驱
* @param x 关键字
* @return
*/
K predecessor(K x); boolean isBalance(); /**
* 返回节点数
* @return
*/
int getSize(); /**
* 高度
* @return
*/
int getHeight(); List<List<BSTNode<K, V>>> levelOrder();
}

   3、BST实现:

 import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
import java.util.function.Consumer; /**
* 二叉搜索树
*
*/
public class BinarySearchTree<K, V> implements IBinarySearchTree<K, V> {
/**
* 根节点
*/
protected BSTNode root;
/**
* 元素个数
*/
protected int size;
private Comparator comparator; public BinarySearchTree() {
} public BinarySearchTree(Comparator comparator) {
this.comparator = comparator;
} // parent curr 双指针
@Override
public BSTNode<K, V> insert(K key, V value) {
if (!(key instanceof Comparable)) {
throw new ClassCastException();
} BSTNode<K, V> parent = null;
BSTNode<K, V> curr = root;
while (curr != null) {
parent = curr;
if (compare(key, curr.key) < 0) {
curr = curr.left;
} else if (compare(key, curr.key) > 0) {
curr = curr.right;
} else {
curr.value = value;
return curr;
}
}
curr = new BSTNode(key, value, null, null, null);
//link current to parent
curr.parent = parent;
if (parent == null) {
root = curr;
} else if (compare(key, parent.key) < 0) {
parent.left = curr;
curr.isLeftChild = true;
} else {
parent.right = curr;
curr.isLeftChild = false;
} size++;
updateHeight(curr);
return curr;
} private void updateHeight(BSTNode<K, V> curr) {
if (curr.parent == null) return;//util root BSTNode<K, V> p = curr.parent;
if (p.height == curr.height) {
p.height++;
updateHeight(p);//递归
}
} @SuppressWarnings({"unchecked", "rawtypes"})
private int compare(K key1, K key2) {
if (null == comparator) {
return ((Comparable) key1).compareTo((Comparable) key2);
} else {
return comparator.compare(key1, key2);
}
} /**
* 中序遍历
* @param con 处理中序遍历的每个元素的函数
*/
@Override
public void inorder(Consumer<K> con) {
if (root != null)
// inorder2(root, con);
inorder(root, con);
} /* 递归形式 */
private void inorder(BSTNode<K, V> p, Consumer<K> con) {
if (p != null) {
inorder(p.left, con);
con.accept(p.key);
inorder(p.right, con);
}
} /*迭代形式*/
private void inorder2(BSTNode<K, V> p, Consumer<K> con) {
Stack<BSTNode<K, V>> stack = new Stack<>();
BSTNode<K, V> curr = p;
//curr不为空或者栈不为空,都可以继续处理
while (curr != null || !stack.isEmpty()) {//没有生产也没有消费,就退出循环了
//沿左支线一撸到底,全部入栈
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
//处理栈顶
if (!stack.isEmpty()) {
BSTNode<K, V> pop = stack.pop();
con.accept(pop.key);
// curr指向pop的右子树,继续外层循环
curr = pop.right;//有可能为空,为空,只消费栈中内容,不为空,就要向栈中生产若干内容
}
}
} // 二叉查找树查找之所以快 每次丢弃一半 lgn
@Override
public V lookupValue(K key) {
BSTNode<K, V> lookupNode = lookupNode(key);
return lookupNode == null ? null : lookupNode.value;
} protected BSTNode<K, V> lookupNode(K key) {
BSTNode<K, V> p = root;
//只要p不为空,并且没找到
while (p != null && compare(key, p.key) != 0) {
if (compare(key, p.key) < 0)
p = p.left;
else
p = p.right;
}
return p;
} // 最左边的结点
@Override
public K min() {
return minNode(root).key;
} protected BSTNode<K, V> minNode(BSTNode p) {
while (p.left != null) {
p = p.left;
}
return p;
} // 最右边的结点
@Override
public K max() {
return maxNode(root).key;
} protected BSTNode<K, V> maxNode(BSTNode p) {
while (p.right != null) {
p = p.right;
}
return p;
} /*右单旋
* p
* q
* */
protected void rightRotate(BSTNode p, BSTNode q) {
boolean pIsLeft = p.isLeft();
BSTNode pp = p.parent; BSTNode x = q.right;
p.left = x;
if (x != null) {
x.parent = p;
x.isLeftChild = true;
}
q.right = p;
p.parent = q;
p.isLeftChild = false; //设定p和gg的关系
q.parent = pp;
if (pp == null) {
root = q;
return;
}
if (pIsLeft) {
pp.left = q;
q.isLeftChild = true;
} else {
pp.right = q;
q.isLeftChild = false;
}
} /*左单旋*/
protected void leftRotate(BSTNode p, BSTNode q) {
boolean pIsLeft = p.isLeft();
BSTNode pp = p.parent;
//p和q的左子——B的关系
BSTNode B = q.left;
p.right = B;
if (B != null) {
B.parent = p;
B.isLeftChild = false;
} //p,q的关系
q.left = p;
p.parent = q;
p.isLeftChild = true; //p和pp的关系
q.parent = pp;
//p是根节点
if (pp == null) {
root = q;
return;
} if (pIsLeft) {
pp.left = q;
q.isLeftChild = true;
} else {
pp.right = q;
q.isLeftChild = false;
}
} @Override
public void remove(K key) {
removeNode(lookupNode(key));
size--;// 记得减少元素个数
} protected void removeNode(BSTNode<K, V> x) {
if (x != null) {
if (x.left == null && x.right == null) {// leaf node. 第一种情况 没有子节点
if (x.parent == null) {
root = null;
return;
}
if (x.isLeftChild) {
x.parent.left = null;
} else {
x.parent.right = null;
}
x.parent = null;
x = null;
} else if (x.left == null) {// 第二种情况 有子节点,但左子为空,有右孩子
if (x.isLeftChild) {
BSTNode<K, V> c = x.right;
BSTNode<K, V> parent = x.parent;
parent.left = c;
c.isLeftChild = true;
c.parent = parent;
} else {
if (x.parent != null) {
x.parent.right = x.right;
x.right.parent = x.parent;
} else {// 根节点
root = x.right;
}
}
x = null;
} else if (x.right == null) {// 第三种情况 有子节点,但右子为空,有左孩子
if (x.isLeftChild) {
x.parent.left = x.left;
x.left.parent = x.parent;
} else {
if (x.parent != null) {
x.parent.right = x.left;
x.left.isLeftChild = false;
x.left.parent = x.parent;
} else { // 根节点
root = x.left;
}
}
x = null;
} else { // 第四种情况 都不为空
BSTNode<K, V> minOfRight = minNode(x.right);
x.key = minOfRight.key;// 更换x的内容
removeNode(minOfRight); // 删掉右子树种最小的元素
}
}
} // 有右子树 ,则后继为右子树最小
// 否则往上回溯,找到一个是左结点的祖先,则后继是该结点的父亲
@Override
public K successor(K x) {
BSTNode<K, V> xNode = lookupNode(x);
if (xNode == null) {
return null;
}
BSTNode<K, V> yNode = successor(xNode);
return yNode == null ? null : yNode.key; } protected BSTNode<K, V> successor(BSTNode<K, V> xNode) {
if (xNode == null) {
return null;
}
if (xNode.right != null) {
return minNode(xNode.right);
}
BSTNode<K, V> yNode = xNode.parent;
while (yNode != null && xNode == yNode.right) {
xNode = yNode;
yNode = yNode.parent;
}
return yNode;
} // 与找后继结点对称
@Override
public K predecessor(K x) {
BSTNode<K, V> xNode = lookupNode(x);
if (xNode == null) {
return null;
}
if (xNode.left != null) {
return maxNode(xNode.left).key;
}
BSTNode<K, V> yNode = xNode.parent;
while (yNode != null && xNode.isLeftChild) {
xNode = yNode;
yNode = yNode.parent;
}
return yNode == null ? null : yNode.key;
} @Override
public boolean isBalance() {
return !unBalance(root);
} protected boolean unBalance(BSTNode g) {
if (g == null) return false;
int minus = getHeight(g.left) - getHeight(g.right);
return Math.abs(minus) > 1
|| unBalance(g.right)
|| unBalance(g.left);
} /**
* 获取树的节点数
* @return
*/
@Override
public int getSize() {
return size;
} @Override
public int getHeight() {
return getHeight(root);
} protected int getHeight(BSTNode node) {
if (node == null) return 0;
int l = getHeight(node.left);
int r = getHeight(node.right);
return 1 + Math.max(l, r);
} public List<List<BSTNode<K, V>>> levelOrder(BSTNode<K, V> x) {
// int num=x.num;//累进的编号
List<List<BSTNode<K, V>>> res = new ArrayList<>();
Queue<BSTNode<K, V>> q = new LinkedList<>();
q.add(x);
BSTNode<K, V> last = x;
BSTNode<K, V> nLast = null;
List<BSTNode<K, V>> l = new ArrayList<>();
res.add(l);
while (!q.isEmpty()) {
BSTNode<K, V> peek = q.peek();
//把即将弹出的节点的子节点加入队列
if (peek.left != null) {
peek.left.num = peek.num * 2;
q.add(peek.left);
nLast = peek.left;
}
if (peek.right != null) {
peek.right.num = peek.num * 2 + 1;
q.add(peek.right);
nLast = peek.right;
} l.add(q.poll());//弹出,加入到当前层列表
if (peek == last && !q.isEmpty()) {//如果现在弹出的节点是之前标记的最后节点,就要换列表
l = new ArrayList<>();
res.add(l);
last = nLast;
}
}
return res;
} // 层次遍历
@Override
public List<List<BSTNode<K, V>>> levelOrder() {
root.num = 1;
return levelOrder(root);
} // 按照格式打印
@Override
public String toString() {
StringBuilder res = new StringBuilder();
List<List<BSTNode<K, V>>> lists = levelOrder();
int level = 1;
int height = getHeight();
for (List<BSTNode<K, V>> l :
lists) {
int gap = ex(2, height - level) - 1;//gap
// printGap(gap);//打印左边margin
int beginNum = ex(2, level - 1);
for (BSTNode<K, V> node : l) {
while (beginNum != node.num) {
//打印gap
for (int i = 0; i < 2 * gap; i++) {
res.append(" ");
}
res.append("**");
//打印gap
for (int i = 0; i < 2 * gap; i++) {
res.append(" ");
}
res.append(" ");
beginNum++;
}
//打印gap
for (int i = 0; i < 2 * gap; i++) {
res.append(" ");
}
res.append(node.key);
//打印gap
for (int i = 0; i < 2 * gap; i++) {
res.append(" ");
}
res.append(" "); beginNum++;
}
level++;
res.append("\n");
}
return res.toString();
} private void printGap(int margin) {
for (int i = 0; i < margin; i++) {
System.out.print(" ");
}
} private void printGap(int gap, String s, int gap1) {
for (int i = 0; i < gap; i++) {
System.out.print(" ");
}
System.out.printf("%2s", s);
for (int i = 0; i < gap; i++) {
System.out.print(" ");
} } public static int ex(int a, int n) {
if (n == 0)
return 1;
if (n == 1)
return a;
int temp = a; // a 的 1 次方
int res = 1;
int exponent = 1;
while ((exponent << 1) < n) {
temp = temp * temp;
exponent = exponent << 1;
} res *= ex(a, n - exponent); return res * temp;
}
}

二叉查找树(BST)的实现的更多相关文章

  1. 二叉查找树(BST)

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

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

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

  3. 二叉查找树BST 模板

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

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

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

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

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

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

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

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

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

  8. 二叉查找树BST

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

  9. K:二叉查找树(BST)

    相关介绍:  二叉查找树(英语:Binary Search Tree),也称二叉搜索树.有序二叉树(英语:ordered binary tree),排序二叉树(英语:sorted binary tre ...

随机推荐

  1. java 文件目录树

    1. 目标格式,使用tree命令时,目录树格式如下. public class TreeTest { public static void main(String[] args) { File roo ...

  2. MapReduce流量统计

    准备数据access.log 要用到的只有第二个手机号,倒数第三上行流量,倒数第二下行流量. 1363157985066 13726230503 00-FD-07-A4-72-B8:CMCC 120. ...

  3. break退出循环分析

    函数功能: 查找dest字符串中连续不包含在src字符串中的字符数量 1 #include<stdio.h> #include<stdlib.h> void strccspn( ...

  4. kityminder-editor + MongoDB 思维导图数据自动实时保存方案

    最近开始做自己的第一个开源项目:一个基于思维导图的测试用例管理系统MinderCase,在做了一周的技术调研后,决定采用kityminder-editor作为思维导图编辑器,为了支持实时存储,当思维导 ...

  5. ubuntu配置小飞机

    现在有两种方式在ubuntu配置本地shadowsocks(前提已经在服务器上搭建好了ss) windows,ios和安卓配置都是gui,没什么好说的.然后前期工作什么买vps啊,比较无脑,不想记录这 ...

  6. 基本urllib库

    urlib库 urllib库是Python中一个最基本的网络请求库.可以模拟浏览器的行为,向指定的服务器发送一个请求,并可以保存服务器返回的数据. urlopen函数: 在Python3的urllib ...

  7. SQL反模式学习笔记2 乱穿马路

    程序员通常使用逗号分隔的列表来避免在多对多的关系中创建交叉表, 将这种设计方式定义为一种反模式,称为“乱穿马路”. 目标:  存储多属性值,即多对一 反模式:将多个值以格式化的逗号分隔存储在一个字段中 ...

  8. qt之数据库对照片的存取

    需要确保数据库连接上 QOCI为驱动 //oracle 数据库连接 //需要在执行文件目录添加 oci.dll oraociei11.dll QSqlDatabase db = QSqlDatabas ...

  9. 防御xss 大致理解

    前端 发送留言内容 包含 js 代码 后台 管理员 查看 留言 代码被执行 拿到cookie 成功登陆 解决办法 对录入 进行相关的过滤处理 其他解决方法 正在学习中

  10. Android 博客导航

    Android 博客导航 一. 基础知识 Android 常用知识点 Android 常见问题解决 Android 常用控件 控件常用属性 Material Design 常用控件 二.常用知识点 动 ...