【tree】二叉查找树
作者:互联网
目录
- 一、基本概念
- 二叉查找树的定义
- 查找节点
- 插入节点
- 删除节点
- 二、递归实现
- 实现代码
- 测试案例
- 三、非递归实现
- 实现代码
- 测试案例
系列目录
一、基本概念
1.二叉查找树的定义
二叉查找树:首先是一颗二叉树,其次对于树中的任意一个节点都有该节点的值大于左孩子小于右孩子。
二叉树的定义见第一篇博客《树》。
需要实现的基本操作:查找结点、插入节点和删除节点。
2.查找节点
从根结点出发:
- 如果查找key小于节点key,则向左走;
- 如果查找key大于节点key,则向右走;
- 如果查找key等于节点key,则返回该节点;
- 如果查找到叶子节点还没有找到相等key,那么返回null;
- 查找节点是否为null,是递归实现中递归的终止条件,也是非递归实现中while循环的终止条件;
3.插入节点
从根结点出发:
- 如果查找key小于节点key,则向左走;
- 如果查找key大于节点key,则向右走;
- 如果查找key等于节点key,则说明树种已经存在相同key的节点,修改该节点的value并返回;
- 如果查找到叶子节点还没有找到相同的key,则将新节点插入为节点的左孩子或右孩子;
- 查找节点是否为null,是递归实现中递归的终止条件,也是非递归实现中while循环的终止条件;
- 新插入的节点一定会挂在某一个叶子节点上;
4.删除节点
从根结点出发:
- 如果查找key小于节点key,则向左走;
- 如果查找key大于节点key,则向右走;
- 如果查找key等于节点key,则说明该节点即为待删除结点,此时有两种情况:
- 待删除结点有两个孩子:这种情形,为了保证二叉排序树的顺序性(即大于左孩子小于右孩子),需要从左子树或右子树中找一个最接近待删除节点的节点来替换它,即左子树的最大节点或右子树的最小节点
- 左子树的最大节点,即左子树的最右节点
- 右子树的最小节点,即右子树的最左节点
- 注意:本文后续递归和非递归的实现中,选择右子树的最大节点(即右子树的最左节点)来代替删除节点位置,从而保证二叉查找树的有序性
- 待删除结点最多只有一个孩子:直接让子节点代替删除节点的位置
- 待删除结点有两个孩子:这种情形,为了保证二叉排序树的顺序性(即大于左孩子小于右孩子),需要从左子树或右子树中找一个最接近待删除节点的节点来替换它,即左子树的最大节点或右子树的最小节点
如下图所示:
- 58节点只有一个左孩子47节点,删除58节点只需要让47节点顶替其位置,这样既维护了二叉树的逻辑结构,也保证了二叉查找树的顺序性;
- 47节点既有左孩子也有右孩子:
- 对于47节点的左子树而言,其左子树的最右节点37的值最接近47节点,用37节点来替换47节点,不会破坏二叉查找树的顺序性,37节点是左子树的最大值,但又小于47节点的右子树的任意一个节点
- 对于47节点的右子树而言,因为只有51节点,所以可以直接替换,如果有多个节点,那就找右子树的最左节点(即右子树的最小节点)来替换47节点,同样也不会破坏二叉查找树的顺序性;
二、递归实现
1.实现代码
package cn.wxy.blog;
import java.util.Objects;
import cn.wxy.blog.TraversalTreeTool.TreeNode;
/**
* 递归实现二叉查找树
* @author 王大锤
* @date 2021年6月11日
*/
public class BinarySearchTree<K extends Comparable<K>, V> {
static class Node<K extends Comparable<K>, V> implements TreeNode<K, Node<K, V>> {
private K key;
private V value;
private Node<K, V> parent;
private Node<K, V> leftChild;
private Node<K, V> rightChild;
public Node(K key, V value, Node<K, V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public Node<K, V> getParent() {
return parent;
}
public void setParent(Node<K, V> parent) {
this.parent = parent;
}
public Node<K, V> getLeftChild() {
return leftChild;
}
public void setLeftChild(Node<K, V> leftChild) {
this.leftChild = leftChild;
}
public Node<K, V> getRightChild() {
return rightChild;
}
public void setRightChild(Node<K, V> rightChild) {
this.rightChild = rightChild;
}
@Override
public String toString() {
return this.key + ":" + value + " ";
}
}
private Node<K, V> root;
public BinarySearchTree() {
}
public Node<K, V> getRoot() {
return this.root;
}
public void insert(K key, V value) {
if (Objects.nonNull(key)) {
// 通过this.root = insert来维护二叉树的根节点
this.root = insert(key, value, this.root, null);
}
}
/**
* 递归实现二叉查找树的节点插入:
* 1.如果插入key小于当前查找的节点,则往左走
* 2.如果插入key大于当前查找的节点,则往右走
* 3.如果插入key等于当前查找的节点,修改当前节点的value
* @param key 插入的key
* @param value 插入的value
* @param node 当前节点
* @param parent 当前节点的父节点
* @return
*/
private Node<K, V> insert(K key, V value, Node<K, V> node, Node<K, V> parent) {
if (Objects.isNull(node))
return new Node<K, V>(key, value, parent);
int compare = key.compareTo(node.key);
if (compare < 0)
// 通过node.leftChild = insert在递归中来维护树的逻辑结构
node.leftChild = insert(key, value, node.leftChild, node);
else if (compare > 0)
// 通过node.rightChild = insert在递归中来维护树的逻辑结构
node.rightChild = insert(key, value, node.rightChild, node);
else
node.value = value;
return node;
}
public V select(K key) {
if (Objects.nonNull(key)) {
Node<K, V> node = select(key, this.root);
return Objects.nonNull(node) ? node.value : null;
}
return null;
}
/**
* 递归实现二叉查找树的节点查找:
* 1.如果查找key小于当前查找的节点,则往左走
* 2.如果查找key大于当前查找的节点,则往右走
* 3.如果查找key等于当前查找的节点,则返回该节点
* @param key 查找的key
* @param node 当前节点
* @return
*/
private Node<K, V> select(K key, Node<K, V> node) {
if (Objects.isNull(node))
return null;
int compare = key.compareTo(node.key);
if (compare < 0)
return select(key, node.leftChild);
else if (compare > 0)
return select(key, node.rightChild);
else
return node;
}
public void remove(K key) {
if (Objects.nonNull(key))
// 通过this.root = remove来维护二叉查找树的根节点
this.root = remove(key, this.root);
}
/**
* 递归实现二叉查找树的节点删除:
* 1.如果删除key小于当前查找的节点,则往左走
* 2.如果删除key大于当前查找的节点,则往右走
* 3.如果删除key等于当前查找的节点,则删除该节点
* 此时,删除节点分为两种情况:
* a.删除节点有两个孩子:这种情形,为了保证二叉排序树的顺序性(大于左孩子小于右孩子),需要从左子树或右子树中找一个最接近删除节点节点
* 即左子树的最大节点或右子树的最小节点
* 左子树的最大节点即左子树的最右节点
* 右子树的最小节点即右子树的最左节点
* 本例中选择右子树的最大节点(即右子树的最左节点)来代替删除节点位置,从而保证二叉查找树的有序性
* b.删除节点最多只有一个孩子:直接让子节点代替删除节点的位置
* @param key
* @param node
* @return
*/
private Node<K, V> remove(K key, Node<K, V> node) {
if (Objects.isNull(node))
return null;
int compare = key.compareTo(node.key);
if (compare < 0)
// 通过node.leftChild = remove在递归中来维护树的逻辑结构
node.leftChild = remove(key, node.leftChild);
else if (compare > 0)
// 通过node.rightChild = remove在递归中来维护树的逻辑结构
node.rightChild = remove(key, node.rightChild);
else {
// 找到待删除结点,此时有两种情况:
// a.待删除结点有两个孩子:本例中用待删除节点右子树的最小节点(即右子树的最左节点)来替换其位置
// b.待删除结点最多有一个孩子:直接用其孩子替换待删除结点的位置
if (Objects.nonNull(node.leftChild) && Objects.nonNull(node.rightChild)) {
// 找到右子树最左节点,该节点最多有一个孩子
// 摘除右子树最左节点
// 用右子树最左节点替换待删除结点的位置
// 返回右子树最左节点,用以在递归中保证树的逻辑结构
Node<K, V> leftmostNodeOfRightSubtree = findMin(node.rightChild);
// 通过node.rightChild = remove在递归中来维护树的逻辑结构
node.rightChild = remove(leftmostNodeOfRightSubtree.key, node.rightChild);
// 递归返回的时候,node.parent会完成与leftMostOfSubtree的左孩子或有孩子关系
// 三个节点六条边,所以只需要处理五条边
leftmostNodeOfRightSubtree.parent = node.parent;
leftmostNodeOfRightSubtree.leftChild = node.leftChild;
leftmostNodeOfRightSubtree.rightChild = node.rightChild;
if (Objects.nonNull(node.leftChild))
node.leftChild.parent = leftmostNodeOfRightSubtree;
node.leftChild = null;
if (Objects.nonNull(node.rightChild))
node.rightChild.parent = leftmostNodeOfRightSubtree;
node.rightChild = null;
node = leftmostNodeOfRightSubtree;
} else {
// 用待删除结点的孩子替换其位置之后,为了在递归中维护树的逻辑结构,递归返回的是当前该位置的节点,即代替其位置的子节点
// 所以在这个case中,用returnNode变量来记录替换待删除结点孩子,以便稍后递归返回该节点
Node<K, V> returnNode = null;
if (Objects.nonNull(node.leftChild)) {
returnNode = node.leftChild;
node.leftChild.parent = node.parent;
node.leftChild = null;
} else if (Objects.nonNull(node.rightChild)) {
returnNode = node.rightChild;
node.rightChild.parent = node.parent;
node.rightChild = null;
}
node.parent = null;
node = returnNode;
}
}
return node;
}
private Node<K, V> findMin(Node<K, V> node) {
if (Objects.isNull(node))
throw new NullPointerException();
Node<K, V> find = node;
while (Objects.nonNull(find)) {
node = find;
find = find.leftChild;
}
return node;
}
}
2.测试用例
- 通过insert构建一个二叉查找树;
- 对于查找操作,需要测试根节点、叶节点、普通节点和不存在的节点;
- 对于删除操作,需要测试删除根节点、叶节点、两个孩子的节点、一个孩子的节点和不存在的节点;
insert构建的二叉查找树如下图所示:
插入测试:
BinarySearchTree<Integer, String> binaryTree = new BinarySearchTree<Integer, String>();
binaryTree.insert(62, "62");
binaryTree.insert(58, "58");
binaryTree.insert(88, "88");
binaryTree.insert(47, "47");
binaryTree.insert(73, "73");
binaryTree.insert(99, "99");
binaryTree.insert(35, "35");
binaryTree.insert(51, "51");
binaryTree.insert(93, "93");
binaryTree.insert(37, "37");
System.out.print("先序遍历:");
TraversalTreeTool.preorderTraversalByRecursion(binaryTree.getRoot());
System.out.println();
System.out.print("中序遍历:");
TraversalTreeTool.inorderTraversalByRecursion(binaryTree.getRoot());
System.out.println();
System.out.print("后序遍历:");
TraversalTreeTool.postorderTraversalByRecursion(binaryTree.getRoot());
System.out.println();
System.out.print("层序遍历:");
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
先序遍历:62:62 58:58 47:47 35:35 37:37 51:51 88:88 73:73 99:99 93:93
中序遍历:35:35 37:37 47:47 51:51 58:58 62:62 73:73 88:88 93:93 99:99
后序遍历:37:37 35:35 51:51 47:47 58:58 73:73 93:93 99:99 88:88 62:62
层序遍历:62:62 58:58 88:88 47:47 73:73 99:99 35:35 51:51 93:93 37:37
查找测试:
BinarySearchTree<Integer, String> binaryTree = new BinarySearchTree<Integer, String>();
binaryTree.insert(62, "62");
binaryTree.insert(58, "58");
binaryTree.insert(88, "88");
binaryTree.insert(47, "47");
binaryTree.insert(73, "73");
binaryTree.insert(99, "99");
binaryTree.insert(35, "35");
binaryTree.insert(51, "51");
binaryTree.insert(93, "93");
binaryTree.insert(37, "37");
System.out.println(binaryTree.getRoot());
System.out.println(binaryTree.select(62));
System.out.println(binaryTree.select(58));
System.out.println(binaryTree.select(88));
System.out.println(binaryTree.select(93));
System.out.println("查询不存在的节点:" + binaryTree.select(-1));
62:62
62
58
88
93
查询不存在的节点:null
删除测试:
BinarySearchTree<Integer, String> binaryTree = new BinarySearchTree<Integer, String>();
binaryTree.insert(62, "62");
binaryTree.insert(58, "58");
binaryTree.insert(88, "88");
binaryTree.insert(47, "47");
binaryTree.insert(73, "73");
binaryTree.insert(99, "99");
binaryTree.insert(35, "35");
binaryTree.insert(51, "51");
binaryTree.insert(93, "93");
binaryTree.insert(37, "37");
System.out.print("删除不存在的节点-1: ");
binaryTree.remove(-1);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
System.out.println();
System.out.print("删除叶节点37: ");
binaryTree.remove(37);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
System.out.println();
System.out.print("删除一个孩子节点58: ");
binaryTree.remove(58);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
System.out.println();
System.out.print("删除两个孩子节点88: ");
binaryTree.remove(88);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
System.out.println();
System.out.print("删除根节点62: ");
binaryTree.remove(62);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
删除不存在的节点-1: 62:62 58:58 88:88 47:47 73:73 99:99 35:35 51:51 93:93 37:37
删除叶节点37: 62:62 58:58 88:88 47:47 73:73 99:99 35:35 51:51 93:93
删除一个孩子节点58: 62:62 47:47 88:88 35:35 51:51 73:73 99:99 93:93
删除两个孩子节点88: 62:62 47:47 93:93 35:35 51:51 73:73 99:99
删除根节点62: 73:73 47:47 93:93 35:35 51:51 99:99
三、非递归实现
1.实现代码
package cn.wxy.blog;
import java.util.Objects;
import cn.wxy.blog.TraversalTreeTool.TreeNode;
/**
* 二叉查找树的非递归实现
* 和递归实现的最大区别是逻辑结构的维护:
* a.递归实现是在每次递归的代码部分来维护树的逻辑结构;
* b.非递归实现需要在增、删操作中来维护树的逻辑结构和根节点;
* @author 王大锤
* @date 2021年6月12日
*/
public class BinarySearchTreeNonRecursive<K extends Comparable<K>, V> {
static class Node<K extends Comparable<K>, V> implements TreeNode<K, Node<K, V>> {
private K key;
private V value;
private Node<K, V> parent;
private Node<K, V> leftChild;
private Node<K, V> rightChild;
public Node(K key, V value, Node<K, V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public Node<K, V> getParent() {
return parent;
}
public void setParent(Node<K, V> parent) {
this.parent = parent;
}
public Node<K, V> getLeftChild() {
return leftChild;
}
public void setLeftChild(Node<K, V> leftChild) {
this.leftChild = leftChild;
}
public Node<K, V> getRightChild() {
return rightChild;
}
public void setRightChild(Node<K, V> rightChild) {
this.rightChild = rightChild;
}
@Override
public String toString() {
return this.key + ":" + value + " ";
}
}
private Node<K, V> root;
public BinarySearchTreeNonRecursive() {
}
public Node<K, V> getRoot() {
return this.root;
}
public Node<K, V> select(K key) {
Node<K, V> node = selectNode(key);
if (Objects.nonNull(node) && key.compareTo(node.key) == 0)
return node;
return null;
}
/**
* select和insert中都需要查找结点,但是又有一些差异,所以抽取相同部分到selectNode函数中
* select: 从根节点开始往下找,找到返回节点,否则返回null
* insert: 从根节点开始往下找,找到节点更新其value,否则找到最后一个叶节点,新节点挂到叶节点的左孩子或右孩子
* 所以selectNode函数的逻辑:
* 1.从根节点开始往下找,找到则返回节点;
* 2.否则返回查找时最后一个叶节点;
* 3.如果key为null或者查找的是一颗空树,则返回null;
* @param key
* @return
*/
private Node<K, V> selectNode(K key) {
if (Objects.isNull(key))
return null;
Node<K, V> current = null;
Node<K, V> find = this.root;
while (Objects.nonNull(find)) {
// 当循环到叶子节点的时候,current等于叶子节点
// find在接下来的判断中变为叶子节点的左孩子或右孩子,此时find一定为null,从而结束循环
current = find;
int compare = key.compareTo(find.key);
if (compare < 0)
find = find.leftChild;
else if (compare > 0)
find = find.rightChild;
else
return current;
}
return current;
}
/**
* 1.如果是空树,则插入的KV作为根节点;
* 2.如果树非空,分为两种情况
* a.树中已经存在key的节点,那么更新该节点的value并返回
* b.树中不存在key的节点,将新节点插入最后一次查找时的叶节点,作为其左孩子或右孩子
* @param key
* @param value
* @return
*/
public Node<K, V> insert(K key, V value) {
if (Objects.isNull(key))
throw new NullPointerException();
// 根节点null,说明是空树
if (Objects.isNull(this.root)) {
this.root = new Node<K, V>(key, value, null);
return this.root;
}
// 如果树非空,那selectNode的返回值要么是最后一次查找的叶节点,要么是树中已经存在同key的节点
Node<K, V> current = selectNode(key);
int compare = key.compareTo(current.key);
// 如果树中已经存在同key的节点,那么修改该节点的value并返回
if (compare == 0) {
current.value = value;
return current;
}
// 否则,current就是最后一次查找的叶节点,新节点作为该叶节点的左孩子或右孩子
Node<K, V> node = new Node<K, V>(key, value, current);
if (compare < 0)
current.leftChild = node;
else
current.rightChild = node;
return node;
}
/**
* 分成两种情况:
* 1.待删除节点有两个孩子,选取右子树最左节点替换该节点
* 2.待删除节点最多只有一个孩子,直接将子节点替换该节点
* 最后还需要判断待删除结点是不是根节点,如果是,则要将this.root指向替换节点
* @param key
* @return
*/
public Node<K, V> remove(K key) {
if (Objects.isNull(key))
return null;
Node<K, V> deleteNode = selectNode(key);
// 如果树是一棵空树或树中不存在要删除的key,返回null
if (Objects.isNull(deleteNode) || key.compareTo(deleteNode.key) != 0)
return null;
// 到这里,deleteNode确定就是待删除的节点,接下来判断其孩子情况做进一步的处理
if (Objects.nonNull(deleteNode.leftChild) && Objects.nonNull(deleteNode.rightChild)) {
// deleteNode有两个孩子
twoChildren(deleteNode);
} else {
// deleteNode最多有一个孩子
oneChildAtMost(deleteNode);
}
return deleteNode;
}
private Node<K, V> findMin(Node<K, V> node) {
if (Objects.isNull(node))
throw new NullPointerException();
Node<K, V> find = node;
while (Objects.nonNull(find)) {
node = find;
find = find.leftChild;
}
return node;
}
/**
* 删除节点时,待删除结点最多只有一个孩子的情况
* Note:当待删除节点有两个孩子的时候,需要找到其右子树的最左节点来替代它,此时要摘除其右子树最左节点,而右子树最左节点要么是一个叶节点,要么只有一个右孩子
* @param deleteNode
*/
private void oneChildAtMost(Node<K, V> deleteNode) {
// 需要关注deleteNode是否是根节点,如果是,那么需要修改this.root的引用指向替换节点
Node<K, V> replaceNode = null;
if (Objects.nonNull(deleteNode.leftChild)) {
deleteNode.leftChild.parent = deleteNode.parent;
if (Objects.nonNull(deleteNode.parent)) {
if (deleteNode.equals(deleteNode.parent.leftChild))
deleteNode.parent.leftChild = deleteNode.leftChild;
else
deleteNode.parent.rightChild = deleteNode.leftChild;
}
replaceNode = deleteNode.leftChild;
deleteNode.leftChild = null;
} else if (Objects.nonNull(deleteNode.rightChild)) {
deleteNode.rightChild.parent = deleteNode.parent;
if (Objects.nonNull(deleteNode.parent)) {
if (deleteNode.equals(deleteNode.parent.leftChild))
deleteNode.parent.leftChild = deleteNode.rightChild;
else
deleteNode.parent.rightChild = deleteNode.rightChild;
}
replaceNode = deleteNode.rightChild;
deleteNode.rightChild = null;
} else {
if (Objects.nonNull(deleteNode.parent)) {
if (deleteNode.equals(deleteNode.parent.leftChild))
deleteNode.parent.leftChild = null;
else
deleteNode.parent.rightChild = null;
}
}
deleteNode.parent = null;
// 如果删除的是根节点,那么this.root要指向替换节点
if (this.root.equals(deleteNode))
this.root = replaceNode;
}
private void twoChildren(Node<K, V> deleteNode) {
// deleteNode有两个孩子,找到其右子树最左节点进行替换,此时leftmostNodeOfRightSubtree要么是叶节点要么只有右孩子
Node<K, V> leftmostNodeOfRightSubtree = findMin(deleteNode.rightChild);
// 摘除leftmostNodeOfRightSubtree
oneChildAtMost(leftmostNodeOfRightSubtree);
// 将leftmostNodeOfRightSubtree替换deleteNode的位置:
// 1.处理父节点关系
leftmostNodeOfRightSubtree.parent = deleteNode.parent;
if (Objects.nonNull(deleteNode.parent)) {
if (deleteNode.equals(deleteNode.parent.leftChild))
deleteNode.parent.leftChild = leftmostNodeOfRightSubtree;
else
deleteNode.parent.rightChild = leftmostNodeOfRightSubtree;
}
deleteNode.parent = null;
// 虽然node的左右子树非空,但是如果左右子树都只有一个节点,那么oneChildAtMost方法调用完成之后,其左孩子或右孩子变成了null
// 2.处理左孩子
leftmostNodeOfRightSubtree.leftChild = deleteNode.leftChild;
if (Objects.nonNull(deleteNode.leftChild))
deleteNode.leftChild.parent = leftmostNodeOfRightSubtree;
deleteNode.leftChild = null;
// 3.处理右孩子
leftmostNodeOfRightSubtree.rightChild = deleteNode.rightChild;
if (Objects.nonNull(deleteNode.rightChild))
deleteNode.rightChild.parent = leftmostNodeOfRightSubtree;
deleteNode.rightChild = null;
// 如果删除的是根节点,那么this.root要指向替换节点
if (this.root.equals(deleteNode))
this.root = leftmostNodeOfRightSubtree;
}
}
2.测试用例
- 通过insert构建一个二叉查找树;
- 对于查找操作,需要测试根节点、叶节点、普通节点和不存在的节点;
- 对于删除操作,需要测试删除根节点、叶节点、两个孩子的节点、一个孩子的节点和不存在的节点;
insert构建的二叉查找树如下图所示:
插入测试:
BinarySearchTreeNonRecursive<Integer, String> binaryTree = new BinarySearchTreeNonRecursive<Integer, String>();
binaryTree.insert(62, "62");
binaryTree.insert(58, "58");
binaryTree.insert(88, "88");
binaryTree.insert(47, "47");
binaryTree.insert(73, "73");
binaryTree.insert(99, "99");
binaryTree.insert(35, "35");
binaryTree.insert(51, "51");
binaryTree.insert(93, "93");
binaryTree.insert(37, "37");
System.out.print("先序遍历:");
TraversalTreeTool.preorderTraversalByRecursion(binaryTree.getRoot());
System.out.println();
System.out.print("中序遍历:");
TraversalTreeTool.inorderTraversalByRecursion(binaryTree.getRoot());
System.out.println();
System.out.print("后序遍历:");
TraversalTreeTool.postorderTraversalByRecursion(binaryTree.getRoot());
System.out.println();
System.out.print("层序遍历:");
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
先序遍历:62:62 58:58 47:47 35:35 37:37 51:51 88:88 73:73 99:99 93:93
中序遍历:35:35 37:37 47:47 51:51 58:58 62:62 73:73 88:88 93:93 99:99
后序遍历:37:37 35:35 51:51 47:47 58:58 73:73 93:93 99:99 88:88 62:62
层序遍历:62:62 58:58 88:88 47:47 73:73 99:99 35:35 51:51 93:93 37:37
查询测试:
BinarySearchTreeNonRecursive<Integer, String> binaryTree = new BinarySearchTreeNonRecursive<Integer, String>();
binaryTree.insert(62, "62");
binaryTree.insert(58, "58");
binaryTree.insert(88, "88");
binaryTree.insert(47, "47");
binaryTree.insert(73, "73");
binaryTree.insert(99, "99");
binaryTree.insert(35, "35");
binaryTree.insert(51, "51");
binaryTree.insert(93, "93");
binaryTree.insert(37, "37");
System.out.println(binaryTree.getRoot());
System.out.println(binaryTree.select(62));
System.out.println(binaryTree.select(58));
System.out.println(binaryTree.select(88));
System.out.println(binaryTree.select(93));
System.out.println("查询不存在的节点:" + binaryTree.select(-1));
62:62
62:62
58:58
88:88
93:93
查询不存在的节点:null
删除测试:
BinarySearchTreeNonRecursive<Integer, String> binaryTree = new BinarySearchTreeNonRecursive<Integer, String>();
binaryTree.insert(62, "62");
binaryTree.insert(58, "58");
binaryTree.insert(88, "88");
binaryTree.insert(47, "47");
binaryTree.insert(73, "73");
binaryTree.insert(99, "99");
binaryTree.insert(35, "35");
binaryTree.insert(51, "51");
binaryTree.insert(93, "93");
binaryTree.insert(37, "37");
System.out.print("删除不存在的节点-1: ");
binaryTree.remove(-1);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
System.out.println();
System.out.print("删除叶节点37: ");
binaryTree.remove(37);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
System.out.println();
System.out.print("删除一个孩子节点58: ");
binaryTree.remove(58);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
System.out.println();
System.out.print("删除两个孩子节点88: ");
binaryTree.remove(88);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
System.out.println();
System.out.print("删除根节点62: ");
binaryTree.remove(62);
TraversalTreeTool.levelTraversal(binaryTree.getRoot());
删除不存在的节点-1: 62:62 58:58 88:88 47:47 73:73 99:99 35:35 51:51 93:93 37:37
删除叶节点37: 62:62 58:58 88:88 47:47 73:73 99:99 35:35 51:51 93:93
删除一个孩子节点58: 62:62 47:47 88:88 35:35 51:51 73:73 99:99 93:93
删除两个孩子节点88: 62:62 47:47 93:93 35:35 51:51 73:73 99:99
删除根节点62: 73:73 47:47 93:93 35:35 51:51 99:99
参考资料:
- 《大话数据结构》
- 《数据结构与算法分析:Java语言描述》
- 《算法导论》
标签:node,insert,deleteNode,tree,binaryTree,二叉,查找,key,节点 来源: https://blog.csdn.net/reliveIT/article/details/117710589