数据结构之Binary Search Tree (Java)
二叉查找树简介
二叉查找树(Binary Search Tree), 也成二叉搜索树、有序二叉树(ordered binary tree)、排序二叉树(sorted binary tree),
是指一棵空树或者具有下列性质的的二叉树:
1. 若任意节点的左子树不空,在左子树上所有结点的值均小于或等于它的根结点的值;
2. 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3. 任意节点的左子树、右子树也分别为二叉查找树。
4. 没有键值相等的结点(no duplicate nodes)
二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度低。为O(log n)。二叉查找树是基础的数据结构,
用于构建更为抽象的数据结构,如集合,关联数组等。
结构示意图
简单的二叉查找树
实现
创建内部类Node作为树的节点,Node结点拥有左孩子(Left)和右孩子(Right)两个结点以及结点自身的值。
对于二叉查找树的基本算法插入(Insertion)、查找(Searching)、删除(Deletion)、遍历,递归方法使用的比较多。
毕竟树本身就是一种递归结构。~v~。广度优先遍历(breadthFisrt)使用到了队列这一数据结构。
删除元素算法相比其它算法而言有些复杂,我们需要考虑四种情况:
1. 待删除的元素是叶子节点;
2. 待删除的元素有右子树无左子树;
3. 待删除的元素有左子树无右子树;
4. 待删除的元素既有左子树又有右子树。
第一种情况: 删除叶子节点,只需要判断该叶子结点是父节点的左孩子还是右孩子,相应的将父节点的左孩子或右孩子
设为null。
第二种情况: 有右子树无左子树,判断待删除节点是父节点的左孩子还是右孩子,相应的将父节点的左孩子或右孩子设为
待删除节点的右子树。
第三种情况: 有左子树无右子树,判断待删除节点是父节点的左孩子还是右孩子,相应的将父节点的左孩子或右孩子设为
待删除节点的左子树。
第四种情况: 既有左子树又有右子树,这种情况的做法不唯一,此处只列出一种:从待删除节点的左子树找出最大值替代
待删除节点的值。
首先假定待删除节点(node)左子树的最大值为node的左孩子left,如果left右子树为空,则left的值为最大值,
替换掉node的值,相应的将left余下的节点上移。
若果left右子树不为空,找出left右子树最大值(最右边的节点)即为node整个左子树的最大值largestNode,
替换掉node的值,并将largestNode父节点的右子树设为null。
整体实现的代码如下:
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.NoSuchElementException;
import java.util.Queue;
/**
* data structure unbalanced binary search tree
* @author michael
* @param <E>
*/
public class BinarySearchTree<E extends Comparable<E>> { /**
* 二叉树节点个数
*/
int size = 0; /**
* 根节点
*/
Node<E> root; public BinarySearchTree() {
// TODO Auto-generated constructor stub
} /**
* 插入一个元素
* 若根节点为空,新建一个节点作为根节点并把element(需要插入的值)赋给根节点;
* 根节点不为空,将element与根节点的值进行比较
* 若小于等于根节点的值,判断根节点的左子树是否为空
* 如果为空,新建左子树并赋值;
* 否则,递归调用比较。
* 若大于根节点的值,判断根节点的右子树是否为空
* 如果为空,新建右子树并赋值;
* 否则,递归调用比较。
* @param element 需要插入的值
*/
public void add(E element) {
if(root == null) {
root = new Node<E>(null, element, null);
} else {
addNode(root, element);
}
size++;
} /**
* 插入一个元素的具体实现(递归)
* @param current 当前节点
* @param element 需要插入的值
*/
private void addNode(Node<E> current, E element) {
// assert current not null
if(element.compareTo(current.item) <= 0) {
if(current.left == null) {
current.left = new Node<E>(null, element, null);
} else {
addNode(current.left, element);
}
} else {
if(current.right == null) {
current.right = new Node<E>(null, element, null);
} else {
addNode(current.right, element);
}
}
} /**
* 查找一个元素是否为二叉树节点值
* 首先和根节点的值进行比较
* 若相等,找到,返回true;
* 不相等,再和左孩子的值进行比较
* 如果小于等于左孩子的值,则将左孩子作为根节点递归调用。
* 否则将右孩子作为根节点递归调用。
* @param element 待查找的元素
* @return 找到返回true;树为空或未找到返回false
*/
public boolean contains(E element) {
return containsNode(root, element);
} /**
* contains具体实现(递归)
* @param current 当前节点
* @param element 待查找的元素
* @return
*/
private boolean containsNode(Node<E> current, E element) {
if(current == null) {
return false;
} if(element.equals(current.item)) {
return true;
} else if(element.compareTo(current.item) <= 0) {
return containsNode(current.left, element);
} else {
return containsNode(current.right, element);
}
} /**
* 获得指定元素的父节点
* 如果element等于根节点的值,本身为根节点,无父节点,返回null
* 如果element小于等于当前节点(current)的值
* 判断当前节点的左孩子(left)是否为空
* 空,二叉树没有element元素,返回null
* 不空,将左孩子的值和element比较,等于返回current;否则,将left作为当前节点递归调用。
* 否则(element大于当前节点的值)
* 判断当前节点的右孩子(right)是否为空
* 空,二叉树没有element元素,返回null
* 不空,将右孩子的值和element比较,等于返回current;否则,将right作为当前节点递归调用。
* @param current 当前节点
* @param element 待查找元素
* @return
*/
private Node<E> getParent(Node<E> current, E element) {
// assert root not null
if(element.equals(root.item)) {
return null;
} if(element.compareTo(current.item) <= 0) {
if(current.left == null) {
return null;
} else if(element.equals(current.left.item)) {
return current;
} else {
return getParent(current.left, element);
}
} else {
if(current.right == null) {
return null;
} else if(element.equals(current.right.item)) {
return current;
} else {
return getParent(current.right, element);
}
}
} /**
* 给定元素值获得该元素节点
* @param root 根节点
* @param element 给定元素
* @return 该元素节点
*/
private Node<E> getNode(Node<E> root, E element) {
if(root == null) {
return null;
} if(element.equals(root.item)) {
return root;
} else if(element.compareTo(root.item) <= 0) {
return getNode(root.left, element);
} else {
return getNode(root.right, element);
}
} /**
* 删除元素
* 删除元素为
* 叶子节点
* 有右子树无左子树
* 有左子树无右子树
* 既有右子树又有左子树
* @param element 待删除的元素
*/
public void remove(E element) {
Node<E> node = getNode(root, element); if(node == null) {
throw new NoSuchElementException();
} Node<E> parent = getParent(root, element); if(size == 1) {
root = null;
} else if(node.left == null && node.right == null) {
removeLeaf(parent, node);
} else if(node.left == null && node.right != null) {
if(element.compareTo(parent.item) < 0) {
parent.left = node.right;
} else {
parent.right = node.right;
}
} else if(node.left != null && node.right == null) {
if(element.compareTo(parent.item) < 0) {
parent.left = node.left;
} else {
parent.right = node.left;
}
} else {
Node<E> largest = node.left;
if(largest.right == null) {
node.item = largest.item;
node.left = largest.left;
largest = null;
} else {
while(largest.right != null) {
largest = largest.right;
}
Node<E> largestParent = getParent(node, largest.item);
largestParent.right = null;
node.item = largest.item;
}
}
size--;
} /**
* 删除叶子节点
* 判断待删除的叶子节点是父节点的左孩子还是右孩子,相应的将父节点的左孩子或右孩子设为null
* @param parent 父节点
* @param leaf 叶子节点
*/
private void removeLeaf(Node<E> parent, Node<E> leaf) {
E element = leaf.item;
if(element.compareTo(parent.item) < 0) {
parent.left = null;
} else {
parent.right = null;
}
} /**
* 先序遍历
* 根结点-左子树-右子树
* @param root 根结点
* @param container 存储容器
*/
public void preOrder(Collection<E> container) {
preOrderNode(root, container);
} private void preOrderNode(Node<E> root, Collection<E> container) {
if(root != null) {
container.add(root.item);
preOrderNode(root.left, container);
preOrderNode(root.right, container);
} } /**
* 中序遍历
* 左子树-根结点-右子树
* @param root 根结点
* @param container 存储容器
*/
public void inOrder(Collection<E> container) {
inOrderNode(root, container);
} private void inOrderNode(Node<E> root, Collection<E> container) {
if(root != null) {
inOrderNode(root.left, container);
container.add(root.item);
inOrderNode(root.right, container);
}
} /**
* 后序遍历
* 左子树-右子树-根结点
* @param root 根结点
* @param container 存储容器
*/
public void postOrder(Collection<E> container) {
postOrderNode(root, container);
} private void postOrderNode(Node<E> root, Collection<E> container) {
if(root != null) {
postOrderNode(root.left, container);
postOrderNode(root.right, container);
container.add(root.item);
}
} /**
* 广度优先遍历
* 使用队列 将同一高度的节点依次入队出队。
* @param container 存储容器
*/
public void breadthFirst(Collection<E> container) {
Node<E> node = root;
Queue<Node<E>> q = new ArrayDeque<Node<E>>();
while(node != null) {
container.add(node.item);
if(node.left != null) {
q.add(node.left);
}
if(node.right != null) {
q.add(node.right);
}
if(!q.isEmpty()) {
node = q.poll();
} else {
node = null;
}
}
} /**
* 获取二叉查找树的大小
* @return size
*/
public int size() {
return size;
} /**
* 最小值
* @return 二叉查找树最小值
*/
public E min() {
return getMin(root);
} /**
* 获取最小值
* 根据性质,最小值为二叉查找树最左边的节点值
* @param root 根结点
* @return 最小值
*/
public E getMin(Node<E> root) {
// assert root not null
if(root.left == null) {
return root.item;
} else {
return getMin(root.left);
}
} /**
* 最大值
* @return 二叉查找树最大值
*/
public E max() {
return getMax(root);
} /**
* 获取最大值
* 根据性质,最大值为二叉查找树最右边的节点值
* @param root
* @return 最大值
*/
public E getMax(Node<E> root) {
// assert root not null
if(root.right == null) {
return root.item;
} else {
return getMax(root.right);
}
} /**
* 内部类 表示树节点
* @author michael
* @param <E>
*/
private static class Node<E> {
E item;
Node<E> left;
Node<E> right; /**
* 构造一个新节点并指定左孩子和右孩子
* @param left 左孩子
* @param item 节点值
* @param right 右孩子
*/
Node(Node<E> left, E item, Node<E> right) {
this.left = left;
this.item = item;
this.right = right;
}
} }
Fork me on GitHub:https://github.com/michaelwong95
数据结构之Binary Search Tree (Java)的更多相关文章
- leetcode 99 Recover Binary Search Tree ----- java
Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...
- leetcode 98 Validate Binary Search Tree ----- java
Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as ...
- 数据结构基础---Binary Search Tree
/// Binary Search Tree - Implemenation in C++ /// Simple program to create a BST of integers and sea ...
- leetcode 109 Convert Sorted List to Binary Search Tree ----- java
Given a singly linked list where elements are sorted in ascending order, convert it to a height bala ...
- leetcode 108 Convert Sorted Array to Binary Search Tree ----- java
Given an array where elements are sorted in ascending order, convert it to a height balanced BST. 给一 ...
- Convert Sorted List to Binary Search Tree java
public TreeNode sortedListToBST(ListNode head) { if(head==null) return new TreeNode(0); ArrayList< ...
- [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法
二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...
- leetcode-173:Binary Search Tree Iterator(Java)
Binary Search Tree Iterator Implement an iterator over a binary search tree (BST). Your iterator wil ...
- 【LeetCode-面试算法经典-Java实现】【109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)】
[109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 ...
随机推荐
- LaTex代码生成器
latex代码生成器 希腊字母 \alpha \beta \gamma \delta \epsilon \zeta \eta \theta \iota \kappa \lambda \mu \nu \ ...
- 【学习笔记】锋利的jQuery(三)事件和动画
一.jQuery事件 1,加载事件 $(document).ready(function(){...}) //等同于$(function(){..}) $(window).load(function( ...
- YARN
YARN 介绍 Apache Hadoop YARN作为hadoop的子项目加入到Hadoop Common (core libraries), Hadoop HDFS (storage) and H ...
- C# 制作Windows服务安装包
C# 制作Windows服务安装包 这两天公司要用C#写一个windows服务,做成安装安装包.制作的过程中遇到了一些问题,写完之后总结一下.如果以后在用到的话可以可以参考一下,而且由于原来没有做 ...
- [实验]通过内核Patch去掉iOS-v4.3.3的沙盒特性
环境: 1.Mac OS X 10.9.2 2.xcode 5.1.1 3.gcc 4.8 4.redsn0w 0.9.15b3 前提: 1.获取 iOS 4.3.3 的kernelcache,并解密 ...
- 模块化开发AraeRegistration
.NET/ASP.NET MVC(模块化开发AraeRegistration) 阅读目录: 1.开篇介绍 2.AreaRegistration注册路由(传递路由上下文进行模块化注册) 1]开篇介绍 A ...
- CLR的组成和运转
CLR的组成和运转 clr基本 CLR(Common Language Runtime)是一个可由多种编程语言使用的“运行时”.(例如:c#,c++/cli,vb,f#,ironpython,iron ...
- jQuery extend函数详解
一 jQuery的扩展方法原型是 $.extend(dest,src1,src2,src3); 含义是将src1,src2,src3合并到dest中,返回值为合并后的dest,该方法合并后,dest的 ...
- Unity3d物体模型(实现旋转缩放平移自动旋转)
基本功能实现:物体通过鼠标左键上下移动,中间键缩放.右键旋转,30秒没操作,物体自动旋转 实例代码: using UnityEngine; using System.Collections; publ ...
- 简单实现android和wp聊天
使用Beetle.NetPackage简单实现android和wp聊天 Beetle.NetPackage是一个多台平开源Client TCP通讯组件,它针对不同平台提供统一的消息描述规则和使用规范可 ...