二叉搜索树(BST)基本操作
什么是二叉搜索树?
二叉搜索树也叫做二叉排序树、二叉查找树,它有以下性质:
- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 没有键值相等的节点。
二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低,为O(log n)。
基本操作
1.定义BST对象
public class BST<E extends Comparable<E>> {
/**
* 定义节点
*/
private class Node {
private E e;
private Node left;
private Node right;
Node(E e) {
this.e = e;
}
}
private Node root;
private int size;
/**
* 获取节点数
*
* @return
*/
public int size() {
return this.size;
}
}
2.添加节点
1.采用递归,终止条件是当前节点为null时,创建新的节点;
2.如果当前节点不为null,则通过比较插入值和当前节点值的大小,递归其左/右子节点。
public void add(E element){
root = add(root, element);
}
private void _add(Node node, E element){
if(node == null){
size++;
return new Node(element);
}
if(element.compareTo(node.e) > 0){
node.right = _add(node.right, element);
}else if(element.compareTo(node.e) < 0){
node.left = _add(node.left, element);
}
return node;
}
3.查询节点
1.通过比较当前节点值的大小,递归查找左/右子节点。
public boolean contains(E element){
return _contains(root, element);
}
private boolean _contains(Node node,E element){
if(node == null) return false;
if(element.compareTo(node.e) == 0){
return true;
}else if(element.compareTo(node.e) > 0){
return _contains(node.right, element);
}else{
return _contains(node.left, element);
}
return false;
}
4.打印二叉树
1.通过递增树的深度dept,层级打印;
2.叶子节点通过##表示。
public String toString(){
StringBuilder builder = new StringBuilder();
_toString(root, 0, builder);
return builder.toString();
}
private void _toString(Node node, int dept, StringBuilder builder){
if(int i=0; i<dept; i++){
builder.append("--");
}
if(node == null){
builder.append("##\n");
return;
}
builder.append(node.e).append("\n");
_toString(node.left, dept+1, builder);
_toString(right, dept+1, builder);
}
测试运行
public static void main(String[] args) {
BST<Integer> bst = new BST<>();
bst.add(30);
bst.add(10);
bst.add(22);
bst.add(50);
bst.add(18);
bst.add(34);
bst.add(5);
System.out.println(bst);
}
输出结果为:
30
--10
----5
------##
------##
----22
------18
--------##
--------##
------##
--50
----34
------##
------##
----##
遍历操作
二叉树的遍历分为:前序遍历、中序遍历、后序遍历,
这里的前中后指的是父节点的顺序,所以三种遍历的顺序是:
- 前序遍历(父-左-右)
- 中序遍历 (左-父-右)
- 后序遍历 (左-右-父)
1.前序遍历
1.递归遍历,终止条件是当前节点为null;
2.先遍历输出父节点,再遍历左、右子节点
public void preOrder(){
StringBuilder builer = new StringBuilder();
_preOrder(root, builer);
System.out.println(builer.toString);
}
private void _preOrder(Node node,StringBuilder builder){
if(node == null) return;
builder.append(node.e).append(" ");
_preOrder(node.left, builder);
_preOrder(node.right, builder);
}
2.前序遍历2(非递归)
1.借助栈,首先将节点放入栈中,开始遍历栈;
2.遍历取出节点后再将其右子节点和左子节点放入压入栈中;
public void preOrder2(){
StringBuilder builder = new StringBuilder();
Stack<Node> stack = new Stack();
stack.push(root);
while(!stack.isEmpty()){
Node node = stack.pop();
if(node == null) continue;
builder.append(node.e).append(" ");
stack.push(node.right);
stack.push(node.left);
}
System.out.println(builder.toString());
}
3.中序遍历
1.同前序遍历,只是输出的顺序不同
public void inOrder(){
StringBuilder builer = new StringBuilder();
_inOrder(root, builer);
System.out.println(builer.toString);
}
private void _inOrder(Node node,StringBuilder builder){
if(node == null) return;
_inOrder(node.left, builder);
builder.append(node.e).append(" ");
_inOrder(node.right, builder);
}
输入你会发现,二叉搜索树的中序遍历的结果是有序的(从小到大)。
4.后序遍历
1.同前序遍历,只是输出的顺序不同
public void postOrder(){
StringBuilder builer = new StringBuilder();
_postOrder(root, builer);
System.out.println(builer.toString);
}
private void _postOrder(Node node,StringBuilder builder){
if(node == null) return;
_postOrder(node.left, builder);
_postOrder(node.right, builder);
builder.append(node.e).append(" ");
}
5.层序遍历
层序遍历指的是从上到下,从左到右一层层遍历,其遍历方法和上面的前序遍历类似,只是这里借助的是队列;
1.需要借助队列,首选将根节点入队,开始遍历队列;
2.如果当前节点不为null,则将其左子节点和右子节点入队,继续遍历;
public void levelOrder() {
StringBuilder builder = new StringBuilder();
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
if (node == null) continue;
builder.append(node.e).append(" ");
queue.offer(node.left);
queue.offer(node.right);
}
System.out.println(builder.toString());
}
删除
1.删除最小/最大节点
以删除最小为例,最小节点一定是树的最左边的那个:
1.因为删除的节点可能是遍历的当前节点,所以需要通过return的形式返回节点;
2.判断左子节点是否为null:
如果不为null则递归遍历左子节点;
如果为null则要删除的是当前节点,要把它的右节点返回;
public Node removeMinimum() {
root = _removeMinimun(root);
return root;
}
private Node _removeMinimun(Node node) {
if (node == null) return null;
if (node.left == null) {
//如果左子节点为null,则把右子节点返回
Node tmp = node.right;
node.right = null;
size--;
return tmp;
}
node.left = _removeMinimun(node.left);
return node;
}
2.删除任意节点
分三种情况:
- 如果该节点没有左子节点:返回其右子节点;
- 如果该节点没有右子节点:返回其左子节点;
- 如果该节点同时拥有左右子节点:
节点的后继节点是大于它并最接近它的那个节点,也就是该节点的右子树中最小的节点,就是其右子树中最左边的那个,如下图中15的后继节点是17。
1.需要找到其后继节点;
2.将后继节点删除;
3.将后继节点的左子树等于该节点的左子树;
4.将后继节点的右子树等于该节点的右子树删除后继节点返回的树。
/**
* 删除任意节点,并返回删除后的根节点
*
* @param e
* @return
*/
public void removeNode(E e) {
root = _removeNode(root, e);
}
private Node _removeNode(Node node, E e) {
if (node == null) return null;
if (e.compareTo(node.e) < 0) {
node.left = _removeNode(node.left, e);
} else if (e.compareTo(node.e) > 0) {
node.right = _removeNode(node.right, e);
} else {
//1.查找到对应的节点
//2.左子树为空的情况,直接把右子树返回
if (node.left == null) {
Node right = node.right;
node.right = null;
return right;
}
//3.右子树为空的情况,直接把左子树放回
if (node.right == null) {
Node left = node.left;
node.left = null;
return left;
}
//4.找到后继节点(右子树中最小的节点)
Node successor = _mininum(node.right);
//5.将右子树中最小的节点删除
successor.right = _removeMinimun(node.right);
//6.将节点的左子树等于后继节点的左子树
successor.left = node.left;
node.left = node.right = null;
//7.返回后继节点
return successor;
}
return node;
}
/**
* 查找最小的节点
*
* @param node
* @return
*/
private Node _mininum(Node node) {
if (node == null) return null;
if (node.left != null) {
return _mininum(node.left);
}
return node;
}
完整代码BST.java
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
/**
* Binary Search Tree
*
* @param <E>
*/
public class BST<E extends Comparable<E>> {
/**
* 定义节点
*/
private class Node {
private E e;
private Node left;
private Node right;
Node(E e) {
this.e = e;
}
}
private Node root;
private int size;
/**
* 获取节点数
*
* @return
*/
public int size() {
return this.size;
}
/**
* 添加节点
*
* @param element
*/
public void add(E element) {
root = add(root, element);
}
private Node add(Node node, E element) {
if (node == null) {
size++;
return new Node(element);
}
if (element.compareTo(node.e) > 0) {
node.right = add(node.right, element);
} else if (element.compareTo(node.e) < 0) {
node.left = add(node.left, element);
}
return node;
}
/**
* 是否含有节点
*
* @param element
* @return
*/
public boolean contains(E element) {
return contains(root, element);
}
private boolean contains(Node node, E element) {
if (node == null) return false;
if (element.equals(node.e)) return true;
if (element.compareTo(node.e) < 0) {
return contains(node.left, element);
} else {
return contains(node.right, element);
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
toString(root, 0, builder);
return builder.toString();
}
private void toString(Node node, int dept, StringBuilder builder) {
for (int i = 0; i < dept; i++) {
builder.append("--");
}
if (node == null) {
builder.append("##\n");
return;
}
builder.append(node.e);
builder.append("\n");
toString(node.left, dept + 1, builder);
toString(node.right, dept + 1, builder);
}
/**
* 前序遍历
*/
public void preOrder() {
StringBuilder builder = new StringBuilder();
preOrder(root, builder);
System.out.println(builder.toString());
}
private void preOrder(Node root, StringBuilder builder) {
if (root == null) return;
builder.append(root.e).append(" ");
preOrder(root.left, builder);
preOrder(root.right, builder);
}
/**
* 前序遍历(非递归)
*/
public void preOrderTraverse2() {
StringBuilder builder = new StringBuilder();
Stack<Node> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
Node node = stack.pop();
if (node == null) continue;
builder.append(node.e).append(" ");
stack.push(node.right);
stack.push(node.left);
}
System.out.println(builder.toString());
}
/**
* 中序遍历
*/
public void inOrder() {
StringBuilder builder = new StringBuilder();
inOrder(root, builder);
System.out.println(builder.toString());
}
private void inOrder(Node root, StringBuilder builder) {
if (root == null) return;
inOrder(root.left, builder);
builder.append(root.e).append(" ");
inOrder(root.right, builder);
}
/**
* 后序遍历
*/
public void postOrder() {
StringBuilder builder = new StringBuilder();
postOrder(root, builder);
System.out.println(builder.toString());
}
private void postOrder(Node root, StringBuilder builder) {
if (root == null) return;
postOrder(root.left, builder);
postOrder(root.right, builder);
builder.append(root.e).append(" ");
}
/**
* 层序遍历
*/
public void levelOrder() {
StringBuilder builder = new StringBuilder();
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
if (node == null) continue;
builder.append(node.e).append(" ");
queue.offer(node.left);
queue.offer(node.right);
}
System.out.println(builder.toString());
}
/**
* 删除最小节点,返回删除后的根节点
*/
public Node removeMinimum() {
root = _removeMinimun(root);
return root;
}
private Node _removeMinimun(Node node) {
if (node == null) return null;
if (node.left == null) {
//如果左子节点为null,则把右子节点返回
Node tmp = node.right;
node.right = null;
size--;
return tmp;
}
node.left = _removeMinimun(node.left);
return node;
}
/**
* 删除最大节点,,返回删除后的根节点
*
* @return
*/
public Node removeMaximum() {
root = _removeMaxinum(root);
return root;
}
private Node _removeMaxinum(Node node) {
if (node == null) return null;
if (node.right == null) {
Node tmp = node.left;
node.left = null;
size--;
return tmp;
}
node.right = _removeMaxinum(node.right);
return node;
}
/**
* 删除任意节点,并返回删除后的根节点
*
* @param e
* @return
*/
public void removeNode(E e) {
root = _removeNode(root, e);
}
private Node _removeNode(Node node, E e) {
if (node == null) return null;
if (e.compareTo(node.e) < 0) {
node.left = _removeNode(node.left, e);
} else if (e.compareTo(node.e) > 0) {
node.right = _removeNode(node.right, e);
} else {
//1.查找到对应的节点
//2.左子树为空的情况,直接把右子树返回
if (node.left == null) {
Node right = node.right;
node.right = null;
return right;
}
//3.右子树为空的情况,直接把左子树放回
if (node.right == null) {
Node left = node.left;
node.left = null;
return left;
}
//4.找到后继节点(右子树中最小的节点)
Node successor = _mininum(node.right);
//5.将右子树中最小的节点删除
successor.right = _removeMinimun(node.right);
//6.将节点的左子树等于后继节点的左子树
successor.left = node.left;
node.left = node.right = null;
//7.返回后继节点
return successor;
}
return node;
}
/**
* 查找最小的节点
*
* @param node
* @return
*/
private Node _mininum(Node node) {
if (node == null) return null;
if (node.left != null) {
return _mininum(node.left);
}
return node;
}
}
二叉搜索树(BST)基本操作的更多相关文章
- C++版 - 剑指offer 面试题24:二叉搜索树BST的后序遍历序列(的判断) 题解
剑指offer 面试题24:二叉搜索树的后序遍历序列(的判断) 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true.否则返回false.假设输入的数组的任意两个 ...
- 数据结构-二叉搜索树(BST binary search tree)
本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 二叉搜索树简介 顾名思义,二叉搜索树是以一棵二叉树来组织的,这样的一棵树可以用一个链表数据结构来 ...
- 萌新笔记之二叉搜索树(BST)
前言,以前搞过线段树,二叉树觉得也就那样= =.然后数据结构的课也没怎么听过,然后下周期中考... 本来以为今天英语考完可以好好搞ACM了,然后这个数据结构期中考感觉会丢人,还是好好学习一波. 二叉搜 ...
- 给定一个二叉搜索树(BST),找到树中第 K 小的节点
问题:给定一个二叉搜索树(BST),找到树中第 K 小的节点. 出题人:阿里巴巴出题专家:文景/阿里云 CDN 资深技术专家. 考察点: 1. 基础数据结构的理解和编码能力 2. 递归使用 参考答案 ...
- 二叉搜索树(BST)学习笔记
BST调了一天,最后遍历参数错了,没药救了-- 本文所有代码均使用数组+结构体,不使用指针! 前言--BFS是啥 BST 二叉搜索树是基于二叉树的一种树,一种特殊的二叉树. 二叉搜索树要么是一颗空树, ...
- 看动画学算法之:二叉搜索树BST
目录 简介 BST的基本性质 BST的构建 BST的搜索 BST的插入 BST的删除 简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的非线性的数据结构. 树是由很多个节点组 ...
- 在二叉搜索树(BST)中查找第K个大的结点之非递归实现
一个被广泛使用的面试题: 给定一个二叉搜索树,请找出其中的第K个大的结点. PS:我第一次在面试的时候被问到这个问题而且让我直接在白纸上写的时候,直接蒙圈了,因为没有刷题准备,所以就会有伤害.(面完的 ...
- 二叉搜索树 (BST) 的创建以及遍历
二叉搜索树(Binary Search Tree) : 属于二叉树,其中每个节点都含有一个可以比较的键(如需要可以在键上关联值), 且每个节点的键都大于其左子树中的任意节点而小于右子树的任意节点的键. ...
- [LeetCode] Convert BST to Greater Tree 将二叉搜索树BST转为较大树
Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original B ...
- 二叉搜索树(BST)
(第一段日常扯蛋,大家不要看)这几天就要回家了,osgearth暂时也不想弄了,毕竟不是几天就能弄出来的,所以打算过完年回来再弄.这几天闲着也是闲着,就掏出了之前买的算法导论看了看,把二叉搜索树实现了 ...
随机推荐
- 分布式文件管理系统HDFS
Hadoop 分布式文件管理系统HDFS可以部署在廉价硬件之上,能够高容错. 可靠地存储海量数据(可以达到TB甚至PB级),它还可以和Yam中的MapReduce 编程模型很好地结合,为应用程序提供高 ...
- bootstrap如何去除自带的样式----导航栏中的菜单实现平滑的过渡到对应的菜单区域-------动态跟换模态框中的内容
问题1:如何去除bootstap中css中自带的overflow:hidden这个样式 今天遇见在bootstap中轮播图上的 附带图 片不能够显示出来,图片始终有一部分的高度 被隐藏了 后来通 ...
- Tensorflow之多元线性回归问题(以波士顿房价预测为例)
一.根据波士顿房价信息进行预测,多元线性回归+特征数据归一化 #读取数据 %matplotlib notebook import tensorflow as tf import matplotlib. ...
- day3_7.1
1.python的注释 python中为了使代码更容易被人看懂,提高团队开发效率,可使用注释.代码中的注释不加入编译中. 注释有单行注释,如 #这是一段注释 和多行注释: ''' 这是一行注释 ''' ...
- 【Ribbon篇四】自定义负载均衡策略(4)
官方文档特别指出:自定义的负载均衡配置类不能放在 @componentScan 所扫描的当前包下及其子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就是说我们达不到特殊化定制 ...
- CSP2019-S1 游记
估分 83 分qwq 上午照常起床,先跑去学校考了一场化学(黑人问号),然后8:30从学校开溜. 8:50到考点,发现淮安S组只有两个考场... 在考点外遇到一群 金湖中学 的. 不怕了,有水军帮忙垫 ...
- svg 画地图
下载一个svgDeveloper软件,破解版下载 1.首先找一张地图作为绘制模板;(当然你也可以自己画,不准确怪我咯!) 2.新建svg文件:File --> New --& ...
- 【oracle】获取字符串长度
lengthb(string)计算string所占的字节长度:返回字符串的长度,单位是字节 length(string)计算string所占的字符长度:返回字符串的长度,单位是字符
- Bootstrap分页查询
前台方法: function show() { $('#reportTable').bootstrapTable({ method: 'get', url: "@Url.Action(&qu ...
- mac使用技巧和快捷键
mac快捷键 shift+command+delete 清除缓存 shift+command+. 查看隐藏文件 ctrl+空格 切换输入法 shift+option+F11/F12 精细调节声音 fn ...