0
点赞
收藏
分享

微信扫一扫

第七章 二叉搜索树(BinarySearchTree)


第七章 二叉搜索树(BinarySearchTree)

推荐一些神奇的网站:

  • ​​https://520it.com/binarytrees/​​
  • ​​http://btv.melezinek.cz/binary-search-tree.html​​
  • ​​https://www.cs.usfca.edu/~galles/visualization/BST.html​​

思考:在 n 个动态的整数中搜索某个整数?(查看其是否存在)

  • 假设使用动态数组存放元素,从第 0 个位置开始遍历搜索,平均时间复杂度: O ( n )

第七章 二叉搜索树(BinarySearchTree)_算法

  • 如果维护一个有序的动态数组,使用二分搜索,最坏时间复杂度: O ( logn )
  • 但是添加、删除的平均时间复杂度是: O ( n )

第七章 二叉搜索树(BinarySearchTree)_BST_02

  • 针对这个需求,有没有更好的解决方案?
  • 使用二叉搜索树,添加、删除、搜索的最坏时间复杂度均可优化至: O ( logn )

第七章 二叉搜索树(BinarySearchTree)_数据结构_03

  • 二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为BST
  • 又被称为:二叉查找树二叉排序树
  • 任意一个节点的值都大于子树所有节点的值
  • 任意一个节点的值都小于子树所有节点的值
  • 它的左右子树也是一棵二又搜索树
  • 二叉搜索树可以大大提高搜索数据的效率
  • 二叉搜索树存储的元素必须具备可比较性
  • 比如​​int​​​、​​double​​ 等
  • 如果是自定义类型,需要指定比较方式
  • 不允许为​​null​

注意:对于我们现在使用的二叉树来说,它的元素没有索引的概念

二叉搜索树的接口设计

int size() // 元素的数量
boolean isEmpty() // 是否为空
void clear() // 清空所有元素
void add (E element) // 添加元素
void remove (E element) // 删除元素
boolean contains (E element) // 是否包含某元素

添加元素: add( )

第七章 二叉搜索树(BinarySearchTree)_算法_04

/**
* 添加元素
*
* @param element
*/
public void add(E element) {
elementNotNullCheck(element);

//根节点为空,相当于添加第一个节点
if (root == null) {
root = new Node<>(element, null);
size++;
return;
}
//添加的不是第一个节点
//找到父节点
Node<E> parent = root;
Node<E> node = root;
int cmp = 0;
while (node != null) {
cmp = commpare(element, node.element);
parent = node;
if (cmp > 0) {
node = node.right;
} else if (cmp < 0) {
node = node.left;
} else { //相等直接返回
node.element = element;
return;
}
}
//找到父节点,看看插入到父节点的哪个位置
Node<E> newNode = new Node<>(element, parent); //创建新节点
if (cmp > 0) {
parent.right = newNode;
} else {
parent.left = newNode;
}
size++;
}

删除元素: remove( )

删除节点 – 叶子节点

叶子节点直接删除即可

第七章 二叉搜索树(BinarySearchTree)_二叉搜索树_05

删除节点 – 度为1的节点

删除度为1的节点:用子节点替代原节点的位置

  • 如果要删除的节点不是根节点:如果要删除的节点不是根节点

第七章 二叉搜索树(BinarySearchTree)_数据结构_06

  • 如果要删除的节点是根节点

第七章 二叉搜索树(BinarySearchTree)_BST_07

删除节点 – 度为2的节点

第七章 二叉搜索树(BinarySearchTree)_数据结构_08

public void remove(E element) {
remove(node(element));
}

private void remove(Node<E> node) {
if (node == null) {
return;
}
size--;
if (node.hasTwoChildren()) { //度为2的节点
//找到后继节点
Node<E> s = successor(node);
//用后继节点的值,覆盖度为2的节点的值
node.element = s.element;
//删除后继节点
node = s;
}
//删除node节点(node的度必然是1或者0)
Node<E> replacement = node.left != null ? node.left : node.right;

if (replacement != null) { //node是度为1的节点
//更改parent
replacement.parent = node.parent;
//更改parent的left,right的指向
if (node.parent == null) { //node是度为1的节点并且是根节点
root = replacement;
} else {
if (node == node.parent.left) {
node.parent.left = replacement;
} else { //node == node.parent.right
node.parent.right = replacement;
}
}
} else if (node.parent == null) { //node是叶子节点并且是根节点
root = null;
} else { //node是叶子节点,但不是根节点
if (node == node.parent.left) {
node.parent.left = null;
} else { //node == node.parent.right
node.parent.right = null;
}
}
}

private Node<E> node(E element) {
Node<E> node = root;
while (node != null) {
int cmp = commpare(element, node.element);
if (cmp == 0) {
return node;
} else if (cmp > 0) {
node = node.right;
} else { //cmp < 0
node = node.left;
}
}
return null;
}

BST完整代码

package cn.xx.java.tree;

import java.util.Comparator;
import java.util.LinkedList;
import java.util.Queue;

/**
* 二叉搜索树
*
* @author xiexu
* @create 2021-07-26 12:13 下午
*/
public class BST<E> extends BinaryTree<E> {
private Comparator<E> comparator;

public BST() {
this(null);
}

public BST(Comparator<E> comparator) {
this.comparator = comparator;
}

private void elementNotNullCheck(E element) {
if (element == null) {
throw new IllegalArgumentException("element must not be null");
}
}

/**
* 添加元素
*
* @param element
*/
public void add(E element) {
elementNotNullCheck(element);

//根节点为空,相当于添加第一个节点
if (root == null) {
root = new Node<>(element, null);
size++;
return;
}
//添加的不是第一个节点
//找到父节点
Node<E> parent = root;
Node<E> node = root;
int cmp = 0;
while (node != null) {
cmp = commpare(element, node.element);
parent = node;
if (cmp > 0) {
node = node.right;
} else if (cmp < 0) {
node = node.left;
} else { //相等直接返回
node.element = element;
return;
}
}
//找到父节点,看看插入到父节点的哪个位置
Node<E> newNode = new Node<>(element, parent); //创建新节点
if (cmp > 0) {
parent.right = newNode;
} else {
parent.left = newNode;
}
size++;
}

public void remove(E element) {
remove(node(element));
}

private void remove(Node<E> node) {
if (node == null) {
return;
}
size--;
if (node.hasTwoChildren()) { //度为2的节点
//找到后继节点
Node<E> s = successor(node);
//用后继节点的值,覆盖度为2的节点的值
node.element = s.element;
//删除后继节点
node = s;
}
//删除node节点(node的度必然是1或者0)
Node<E> replacement = node.left != null ? node.left : node.right;

if (replacement != null) { //node是度为1的节点
//更改parent
replacement.parent = node.parent;
//更改parent的left,right的指向
if (node.parent == null) { //node是度为1的节点并且是根节点
root = replacement;
} else {
if (node == node.parent.left) {
node.parent.left = replacement;
} else { //node == node.parent.right
node.parent.right = replacement;
}
}
} else if (node.parent == null) { //node是叶子节点并且是根节点
root = null;
} else { //node是叶子节点,但不是根节点
if (node == node.parent.left) {
node.parent.left = null;
} else { //node == node.parent.right
node.parent.right = null;
}
}
}

private Node<E> node(E element) {
Node<E> node = root;
while (node != null) {
int cmp = commpare(element, node.element);
if (cmp == 0) {
return node;
} else if (cmp > 0) {
node = node.right;
} else { //cmp < 0
node = node.left;
}
}
return null;
}

/**
* 判断元素是否存在
*
* @param element
* @return
*/
public boolean contains(E element) {
return node(element) != null;
}

/**
* @param e1
* @param e2
* @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于0,代表e1小于e2
*/
private int commpare(E e1, E e2) {
if (comparator != null) {
return comparator.compare(e1, e2);
} else {
return ((Comparable<E>) e1).compareTo(e2);
}
}

/**
* 前序遍历
*/
public void preorderTraversal() {
preorderTraversal(root);
}

private void preorderTraversal(Node<E> node) {
if (node == null) {
return;
}
//先访问根节点
System.out.println(node.element);
//访问左子节点
preorderTraversal(node.left);
//访问右子节点
preorderTraversal(node.right);
}

/**
* 中序遍历
*/
public void inorderTraversal() {
inorderTraversal(root);
}

private void inorderTraversal(Node<E> node) {
if (node == null) {
return;
}
//先中序遍历左子树
inorderTraversal(node.left);
//输出根节点
System.out.println(node.element);
//再中序遍历右子树
inorderTraversal(node.right);
}

/**
* 后序遍历
*/
public void postorderTraversal() {
postorderTraversal(root);
}

private void postorderTraversal(Node<E> node) {
if (node == null) {
return;
}
//先遍历左子树
postorderTraversal(node.left);
//再遍历右子树
postorderTraversal(node.right);
//最后访问根节点
System.out.println(node.element);
}

/**
* 层序遍历
*/
public void levelOrderTranversal() {
if (root == null) {
return;
}
Queue<Node<E>> queue = new LinkedList<>();
//将头节点入队
queue.offer(root);
while (!queue.isEmpty()) {
//将头结点出队
Node<E> node = queue.poll();
System.out.println(node.element);
//如果左子节点不为空,就将左子节点入队
if (node.left != null) {
queue.offer(node.left);
}
//如果右子节点不为空,就将右子节点入队
if (node.right != null) {
queue.offer(node.right);
}
}
}

}


举报

相关推荐

0 条评论