二分搜索树实现
1.1 基本介绍
二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为BST又被称为:二叉查找树、二叉排序树,任意一个节点的值都大于其左子树所有节点的值,任意一个节点的值都小于其右子树所有节点的值,它的左右子树也是一棵二叉搜索树。二叉搜索树可以大大提高搜索数据的效率,二叉搜索树存储的元素必须具备可比较性。
1.2 存储结构
1、顺序存储
二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能体现出之间的逻辑关系。
顺序存储结构在极端情况下浪费空间,只适合平衡树的存储。
2、链式存储
既然顺序表存储适用性性不强,则考虑链式存储结构,二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域,这种链表也叫作二叉链表。
其中data
是数据域,lchild
和rchild
都是指针域,分别存放指向左孩子和右孩子的指针。二叉链表示意图如下:
1.3 添加结点
1、思路分析
如果当前二分搜索树一个元素都没有,添加一个新元素【43
】,则当前元素为根结点。
二次添加新元素比如添加【23
】和【59
】,直接让它跟根节点进行比较,大于插入根节点右边,小于插入根节点左边。
依次添加元素
再次添加元素,添加【60
】这个新节点,先跟根结点【41
】比较,发现【60
】比根结点大,插入根节点的右边,继续跟右边结点【59】比较,发现大于【59】,则插入到【59】的右边。
再次插入【28
】这个新节点,先跟根结点【41
】进行比较,发现小于根结点,往左边插入。一直比较,最终插入到结点【35
】的左边。
1.4 删除结点
删除二分搜索树最小值
所谓二分搜索树的最小值,一定是一颗二叉树从根节点开始,不停的向左走,直到走不动为止,那个最左的节点一定是最小值。
对于上面这颗二叉搜索树来说,删除最小节点,直接删除就好了。
特殊情况,此时二分搜索树最小节点是【23
】,该节点存在叶子数。
此时只需要将【23
】这个节点删除,然后将【23
】整个右子树直接接上根结点的左子树就好了。
删除二分搜索树最大值
对于下面这颗二叉搜索树来说,删除最大节点【如果是叶子节点】,直接删除就好了。
删除叶子结点成功
如果不是叶子节点,存在左子树。先将结点【59
】删除,然后将58
的左子树整体当成41
的右子树,接上即可。
删除二分搜索树任意节点
比如删除58这个节点,该节点左右都有孩子的节点,假设删除的结点为d
。
先找到删除节点的右子树的相应最小值的结点,s = min(d->min)
,s
节点是d
的后继,然后在d
的右子树中,删除最小的值。
让S的右子树等于被删除节点的右子树,s->right = removeMin(d->right)
。
最后让s
的左子树等价于d
的左子树,最后删除d
,S
是新的子树的根,s->left = d->left
。
1.5 深度优先遍历
深度优先遍历主要包括:先序遍历,中序遍历,后序遍历
1、先序遍历
规则是若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树。
2、中序遍历
规则是若树为空,则空操作返回,否则从根节点开始【注意并不是先访问根节点】,中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
3、后序遍历
规则是若树为空,则空操作返回,否则从根结点开始,后序遍历根节点的左子树,然后后序遍历右子树,最后访问根节点。
1.6 广度优先遍历
1、层序遍历
规则是若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。
1.7 代码实现
接口类:List
package cn.binarysearch.demo;
public interface List<E> extends Iterable<E>{
void add(E element);
void add(int index, E element) ;
void remove(E element);
E remove(int index);
E get(int index);
E set(int index, E element) ;
int size();
int indexOf(E element) ;
boolean contains(E element);
boolean isEmpty();
void clear();
}
链表类实现:LinkedList
package cn.binarysearch.demo;
import cn.set.demo.List;
import java.util.Iterator;
// 链表
public class LinkedList<E> implements List<E> {
// 创建Node节点
private class Node {
// 数据域
E data;
// 指向直接前驱的指针
Node prev;
// 指向直接后继的指针
Node next;
// 构造函数
public Node() {
this(null, null, null);
}
public Node(E data) {
this(data, null, null);
}
public Node(E data, Node prev, Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (prev != null) {
sb.append(prev.data);
} else {
sb.append("null");
}
sb.append("->").append(data).append("->");
if (next != null) {
sb.append(next.data);
} else {
sb.append("null");
}
return sb.toString();
}
}
// 链表元素的数量
private int size;
// 声明头结点
private Node head;
// 声明尾节点
private Node tail;
// 初始化头结点
public LinkedList() {
head = null;
tail = null;
size = 0;
}
public LinkedList(E[] arr) {
for (E e : arr) {
add(e);
}
}
//默认向表尾添加元素
@Override
public void add(E element) {
add(size, element);
}
//在链表当中指定索引index处添加一个元素
@Override
public void add(int index, E element) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("add index out of bounds");
}
// 创建新的结点对象
Node node = new Node(element);
// 链表为空
if(isEmpty()){
head = node;
tail = node;
tail.next = head;
head.prev = tail;
}else if(index == 0){ // 在链表头部添加元素
// 头结点的上一跳指向新节点的上一跳
node.prev = head.prev;
node.next = head;
head.prev = node;
head = node;
tail.next = head;
}else if(index == size){ // 在链表尾部添加元素
node.next = tail.next;
tail.next = node;
node.prev = tail;
tail = node;
head.prev = tail;
}else{
// 在链表中添加元素
Node p,q; // 定义两个指针变量
if(index <= size / 2){
p = head;
for(int i =0; i < index -1 ; i++){
p = p.next;
}
q = p.next;
p.next = node;
node.prev = p;
q.prev = node;
node.next = q;
}else{
p = tail;
for(int i=size -1; i > index; i--){
p = p.prev;
}
q = p.prev;
q.next = node;
node.prev = q;
p.prev = node;
node.next = p;
}
}
size++;
}
//删除链表中指定的元素element
@Override
public void remove(E element) {
int index = indexOf(element);
if(index != -1){
remove(index);
}
}
//删除链表中指定角标处index的元素
@Override
public E remove(int index) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("remove index out of bounds");
}
// 定义ret变量
E ret = null;
Node node;
// 当链表只剩一个元素
if(size ==1){
ret = head.data;
head = null;
tail = null;
// 删除表头
}else if(index == 0){
ret = head.data;
node = head.next;
head.next = null;
node.prev = head.prev;
head.prev = null;
head = node;
tail.next = head;
// 删除表尾
}else if(index == size -1){
ret = tail.data;
node = tail.prev;
tail.prev = null;
node.next = tail.next;
tail.next = null;
tail = node;
head.prev = tail;
}else{
// 删除链表中间的某一个元素
Node p, q, r;
if(index <= size / 2){
p = head;
for(int i=0; i < index-1; i++){
p = p.next;
}
q = p.next;
ret = q.data;
r = q.next;
p.next = r;
r.prev = p;
q.next = null;
q.prev = null;
}else{
p = tail;
for(int i = size -1; i > index + 1; i--){
p = p.prev;
}
q = p.prev;
ret = q.data;
r = q.prev;
r.next = p;
p.prev = r;
q.next = null;
q.prev = null;
}
}
size --;
return ret;
}
//获取链表中指定索引处的元素
@Override
public E get(int index) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("get index out of bounds");
}
// 获取头
if(index == 0){
return head.data;
}else if(index == size -1){
// 获取尾部
return tail.data;
}else{
// 获取中间
Node p = head;
for (int i = 0; i < index; i++) {
p = p.next;
}
return p.data;
}
}
// 修改链表中指定索引index的元素为element
@Override
public E set(int index, E element) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("set index out of bounds");
}
E ret = null;
// 获取头
if(index == 0){
// 修改头
ret = head.data;
head.data = element;
}else if(index == size -1){
// 修改尾部元素
ret = tail.data;
tail.data = element;
}else{
// 修改中间
Node p = head;
for (int i = 0; i < index; i++) {
p = p.next;
}
ret = p.data;
p.data = element;
}
return ret;
}
@Override
public int size() {
return size;
}
//查找元素在链表中第一次出现的索引
@Override
public int indexOf(E element) {
if(isEmpty()){
return -1;
}
Node p = head;
int index = 0;
while (!p.data.equals(element)){
p = p.next;
index++;
if(p == head){
return -1;
}
}
return index;
}
//在链表中判断是否包含元素element
@Override
public boolean contains(E element) {
return indexOf(element) != -1;
}
@Override
public boolean isEmpty() {
return size== 0 && head == null && tail == null;
}
@Override
public void clear() {
head = null;
tail = null;
size = 0;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("size=").append(size).append(", [");
Node node = head;
for (int i = 0; i < size; i++) {
if (i != 0) {
res.append(", ");
}
res.append(node);
node = node.next;
}
res.append("]");
return res.toString();
}
@Override
public Iterator<E> iterator() {
return new DoubleCircleLinkedListIterator();
}
class DoubleCircleLinkedListIterator implements Iterator<E>{
private Node cur = head;
private boolean flag = true;
@Override
public boolean hasNext() {
if(isEmpty()){
return false;
}
return flag;
}
@Override
public E next() {
E ret = cur.data;
cur = cur.next;
if(cur == head){
flag = false;
}
return ret;
}
}
}
二分搜索树实现:BinarySearchTree
package cn.binarysearch.demo;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
// 二分搜索树
public class BinarySearchTree<E extends Comparable<E>> implements Iterable<E>{
// 二分搜索树结点定义
private class Node {
// 数据域
public E e;
// 左右指针域
public Node left, right;
// 构造方法
public Node (E e){
this.e = e;
left = null;
right = null;
}
}
// 二分搜索树的根节点的指针
private Node root;
// 二分搜索树的元素的个数
private int size;
// 创建一颗空的二分搜索树
public BinarySearchTree(){
root = null;
size = 0;
}
// 获取二分搜索树的元素个数
public int size(){
return size;
}
// 判断二分搜索树是否为空
public boolean isEmpty(){
return size == 0 && root == null;
}
/***
* 向二分搜索树中添加一个元素e
* @param e
*/
public void add(E e){
root = add(root, e);
}
/***
* 向以node为根的二分搜索树中添加元素e
* 并且返回新结点插入之后的二分搜索树新的根
* @param node
* @param e
* @return
*/
private Node add(Node node, E e){
if(node == null){
size ++;
return new Node(e);
}
if(e.compareTo(node.e) < 0){
// 以node左子树为根节点,再添加一个元素
node.left = add(node.left, e);
}else if(e.compareTo(node.e) > 0){
// 以node右子树为根节点,再添加一个元素
node.right = add(node.right, e);
}
return node;
}
/***
* 判断二分搜索树中是否包含元素e
* @param e
* @return
*/
public boolean contains(E e){
return contains(root, e);
}
/***
* 以node为根节点的树中,判断e是否存在
* @param node
* @param e
* @return
*/
private boolean contains(Node node, E e){
if(node == null){
return false;
}
if(e.compareTo(node.e) == 0){
return true;
}
// 以当前节点的左孩子为根的左子树,去寻找元素e
if(e.compareTo(node.e) < 0){
return contains(node.left, e);
}else {
// 以当前节点的右孩子为根的右子树,去寻找元素e
return contains(node.right, e);
}
}
// 前序遍历
public void preOrder(){
preOrder(root);
}
/***
* 前序遍历递归实现
* @param node
*/
public void preOrder(Node node){
if(node == null){
return;
}
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
// 前序遍历的非递归实现方式
public void preOrderNR(){
// 先访问根节点,然后前序遍历左子树,再前序遍历右子树
LinkedList<Node> stack = new LinkedList<>();
// 入栈根节点
stack.push(root);
while (!stack.isEmpty()){
// 先弹出一个元素
Node cur = stack.pop();
System.out.println(cur.e);
// 判断左右孩子是否为空,然后执行入栈操作
if(cur.right != null){
stack.push(cur.right);
}
if(cur.left != null){
stack.push(cur.left);
}
}
}
// 中序遍历
public void inOrder(){
inOrder(root);
}
/***
* 中序遍历递归实现
* @param node
*/
private void inOrder(Node node){
if(node == null){
return;
}
inOrder(node.left);
System.out.println(node.e);
inOrder(node.right);
}
// 中序遍历非递归实现
public void inOrderNR(){
// 中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树
LinkedList<Node> stack = new LinkedList<>();
// 创建pre指针
Node prev = root;
// 将现将根节点和根节点的左边全部入栈
while (prev != null){
stack.push(prev);
prev = prev.left;
}
while (!stack.isEmpty()){
// 先弹出元素
Node cur = stack.pop();
System.out.println(cur.e);
// 判断该结点右边是否为空
if(cur.right != null){
// 如果右孩子不为空,继续执行入栈操作
prev = cur.right;
while (prev != null){
// 将左孩子执行入栈操作
stack.push(prev);
prev = prev.left;
}
}
}
}
// 后序遍历
public void postOrder(){
postOrder(root);
}
/***
* 后序遍历递归实现
* @param node
*/
private void postOrder(Node node){
if(node == null){
return;
}
postOrder(node.left);
postOrder(node.right);
System.out.println(node.e + " ");
}
// 后序遍历非递归实现
public void postOrderNR(){
if(root == null){
return;
}
// 申请两个栈s1,s2,头节点入栈s1
LinkedList<Node> s1 = new LinkedList<>();
LinkedList<Node> s2 = new LinkedList<>(); // 辅助栈,存储根 -> 右节点 ->左节点 的结果
// 先插入根节点
s1.push(root);
// 如果栈s1不为空,执行以下操作:弹出一个元素,入栈s2
while (!s1.isEmpty()){
Node cur = s1.pop();
s2.push(cur);
// 如果该节点左孩子不空入栈s1,如果该节点右孩子不空入栈s1.
if(cur.left != null){
s1.push(cur.left);
}
if(cur.right != null){
s1.push(cur.right);
}
}
// 将栈s2中的节点一次出栈,打印
while (!s2.isEmpty()){
Node cur = s2.pop();
System.out.println(cur.e + " ");
}
}
// 层序遍历
public void levelOrder(){
// 创建队列
Queue<Node> queue = new LinkedList<>();
// 先入队根节点
queue.offer(root);
while (!queue.isEmpty()){
// 先出队一个元素
Node cur = queue.poll();
System.out.println(cur.e);
// 分别入队左右子树
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
}
// 返回二分搜索树的最小值【迭代实现】
public E minimum2(){
if (isEmpty()){
throw new IllegalArgumentException("BST is empty!!!");
}
// 定义指针prev指向根节点
Node prev = root;
while (prev.left != null){
prev = prev.left;
}
return prev.e;
}
// 返回二分搜索树的最小值【递归实现】
public E minimum(){
if(isEmpty()){
throw new IllegalArgumentException("BST is empty!!!");
}
Node minNode = minimum(root);
return minNode.e;
}
/***
* 返回以node为根的二分 搜索树的最小值所在的节点
* @param node
* @return
*/
private Node minimum(Node node){
if(node.left == null){
return node;
}
return minimum(node.left);
}
// 返回二分搜索树的最大值【递归实现】
public E maximum(){
if(isEmpty()){
throw new IllegalArgumentException("BST is empty!!!");
}
Node maxNode = maximum(root);
return maxNode.e;
}
/***
* 返回以node为根的二分 搜索树的最大值所在的节点
* @param node
* @return
*/
private Node maximum(Node node){
if(node.right == null){
return node;
}
return maximum(node.right);
}
// 从二分搜索树中删除最小值所在节点, 返回最小值
public E removeMin(){
// 调用函数
E ret = minimum();
root = removeMin(root);
return ret;
}
/***
* 删除掉以node为根的二分搜索树中的最小节点
* 返回删除节点后新的二分搜索树的根
* @param node
* @return
*/
private Node removeMin(Node node){
// 向左走再也走不动了
if(node.left == null){
// 定义变量保存当前结点的右子树
Node rightNode = node.right;
node.right = null;
size --;
return rightNode;
}
// 以node的左边为根节点,删除当前左子树的最小值,然后返回给左边接上
node.left = removeMin(node.left);
return node;
}
// 从二分搜索树中删除最大值所在节点, 返回最大值
public E removeMax(){
// 调用函数
E ret = maximum();
root = removeMax(root);
return ret;
}
/***
* 删除掉以node为根的二分搜索树中的最大节点
* 返回删除节点后新的二分搜索树的根
* @param node
* @return
*/
private Node removeMax(Node node){
if(node.right == null){
// 定义变量保存当前结点的左子树
Node leftNode = node.left;
node.left = null;
size --;
return leftNode;
}
// 以node的右边为根节点,删除当前右子树的最大值,然后返回给右边接上
node.right = removeMax(node.right);
return node;
}
// 从二分搜索树中删除元素为e的节点
public void remove(E e){
root = remove(root, e);
}
/***
* 删除掉以node为根的二分搜索树中值为e的节点,递归算法
* 返回删除节点后新的二分搜索树的根
* @param node
* @param e
* @return
*/
private Node remove(Node node, E e){
if(node == null){
return null;
}
if(e.compareTo(node.e) < 0){
node.left = remove(node.left, e);
return node;
}else if(e.compareTo(node.e) > 0){
node.right = remove(node.right, e);
return node;
}else { // e.compareTo(node.e) == 0
// 如果左子树为空
if(node.left == null){
Node rightNode = node.right;
// 置空操作
node.right = null;
size --;
return rightNode;
}
// 待删除节点右子树为空的情况
if(node.right == null){
Node leftNode = node.left;
// 置空操作
node.left = null;
size --;
return leftNode;
}
// 待删除节点左右子树均不为空的情况
// 找到node结点的后继
Node successor = minimum(node.right);
// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
successor.right = removeMin(node.right);
// 用这个节点顶替待删除节点的位置
successor.left = node.left;
// 置空操作
node.left = node.right = null;
return successor;
}
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
// 前序遍历实现
generateBST(root, 0, sb);
return sb.toString();
}
// 生成以node为根节点,深度为depth的描述二叉树的字符串
private void generateBST(Node node, int depth, StringBuilder sb){
if(node == null){
sb.append(generateDepth(depth) + "null\n");
return;
}
// 当前结点存储的元素
sb.append(generateDepth(depth) + node.e + "\n");
// 访问当前结点的左子树
generateBST(node.left, depth + 1, sb);
// 访问当前结点的右子树
generateBST(node.right, depth + 1, sb);
}
private String generateDepth(int depth){
StringBuilder sb = new StringBuilder();
for(int i = 0 ; i < depth ; i ++)
sb.append("--");
return sb.toString();
}
@Override
public Iterator<E> iterator() {
return new BinarySearchTreeIterator();
}
private class BinarySearchTreeIterator implements Iterator<E>{
private LinkedList<E> list = new LinkedList<>();
public BinarySearchTreeIterator(){
LinkedList<Node> stack = new LinkedList<>();
// 创建pre指针
Node prev = root;
// 将现将根节点和根节点的左边全部入栈
while (prev != null){
stack.push(prev);
prev = prev.left;
}
while (!stack.isEmpty()){
// 先弹出元素
Node cur = stack.pop();
list.add(cur.e);
// 判断该结点右边是否为空
if(cur.right != null){
// 如果右孩子不为空,继续执行入栈操作
prev = cur.right;
while (prev != null){
// 将左孩子执行入栈操作
stack.push(prev);
prev = prev.left;
}
}
}
}
@Override
public boolean hasNext() {
return !list.isEmpty();
}
@Override
public E next() {
return list.removeFirst();
}
}
}
测试类:BinarySearchTreeDemo
package cn.binarysearch.demo;
public class BinarySearchTreeDemo {
public static void main(String[] args) {
BinarySearchTree<Integer> tree = new BinarySearchTree<>();
// 向二叉树中添加节点
tree.add(6);
tree.add(9);
tree.add(1);
tree.add(3);
tree.add(11);
tree.add(5);
// 判断元素是否存在
System.out.println(tree.contains(11));
System.out.println(tree.contains(10));
// 调用前序遍历
System.out.println("===前序遍历【递归】===");
tree.preOrder();
System.out.println("===前序遍历【非递归】===");
tree.preOrderNR();
System.out.println("==========");
System.out.println(tree);
// 调用中序遍历
/*
System.out.println("===中序遍历【递归】===");
tree.inOrder();
System.out.println("===中序遍历【非递归】===");
tree.inOrderNR();
*/
// 调用后序遍历
/*System.out.println("===后序遍历【递归】===");
tree.postOrder();
System.out.println("===后序遍历【非递归】===");
tree.postOrderNR();*/
// 层序遍历
/* System.out.println("===层序遍历===");
tree.levelOrder();*/
// 返回最小值
/*System.out.println("二叉搜索树最小节点:" + tree.minimum1());
System.out.println("二叉搜索树最小节点:" + tree.minimum2());
System.out.println("二叉搜索树最大节点:" + tree.maximum());*/
// 删除二叉搜索树中的最小值
/*System.out.println("二叉树最小节点[删除前]:" + tree.minimum());
System.out.println(tree.removeMin());
System.out.println("二叉树最小节点[删除后]:" + tree.minimum());
System.out.println("===========");
// 删除二叉搜索树中的最大值
System.out.println("二叉树最大节点[删除前]:" + tree.maximum());
System.out.println(tree.removeMax());
System.out.println("二叉树最大节点[删除后]:" + tree.maximum());*/
// 中序遍历
// tree.inOrderNR();
//tree.remove(3);
//System.out.println(tree);
}
}