赞
踩
二叉排序树(Binary Sort Tree)又称二叉查找 / 搜索树(Binary Search Tree),也是树的一种特殊数据结构,这种树结构最大的特点就是查询效率高,很多情况下甚至比链表结构还要高。这种结构不仅查找高效,在动态插入、删除、修改等地方同样高效,因此这种结构在查找、插入算法、删除节点等地方被大量应用。
现有一组数列[7, 3, 10, 12, 5, 1, 9]
,要求能够高效的完成对数据的查询和添加。
解决方案分析:
1、使用数组实现
2、使用链式存储即链表结构
3、使用树存储方式
二叉排序树(Binary Sort Tree)
,即可以保证数据的检索速度,同时也可以保证数据的插入、删除、修改的速度。关于二叉排序树(Binary Sort Tree)
,是如何实现高效查找、插入、删除、修改的??这也是本文探索与介绍的内容。
二叉排序树:BST(Binary Sort(Search) Tree)
,简称BST
。对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。
特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点。
比如针对前面的数据 (7, 3, 10, 12, 5, 1, 9) ,对应的二叉排序树为:
一个数组创建成对应的二叉排序树,并使用中序遍历二叉排序树,比如:数组为 Array(7, 3, 10, 12, 5, 1, 9)
, 创
建成对应的二叉排序树为:
package com.laizhenghua.tree; /** * @description: * @author: laizhenghua * @date: 2021/11/9 20:11 */ public class BinarySortTreeTest { public static void main(String[] args) { Integer[] array = new Integer[]{7, 3, 10, 12, 5, 1, 9}; // 构建二叉排序树 BinarySortTree binarySortTree = new BinarySortTree(); for (Integer value : array) { binarySortTree.add(new Node(value)); } // 中序遍历 binarySortTree.infixOrder(); // 1 3 5 7 9 10 12 } } // 创建二叉排序树 class BinarySortTree { private Node root; // 根节点 // 添加节点方法 public void add(Node node) { if (this.root == null) { this.root = node; } else { root.add(node); } } // 中序遍历 public void infixOrder() { if (root.left != null) { root.infixOrder(); } else { System.out.println("BinarySortTree is null"); } } } // 创建Node节点 class Node { Integer value; Node left; Node right; public Node(Integer value) { this.value = value; } // 递归添加节点,需要满足二叉树排序树要求 public void add(Node node) { if (node == null) { return; } // 和根节点比较value if (node.value < this.value) { if (this.left == null) { this.left = node; } else { this.left.add(node); // 递归向左子树添加 } } else { if (this.right == null) { this.right = node; } else { this.right.add(node); // 递归向右子树添加 } } } // 中序遍历 public void infixOrder() { if (this.left != null) { this.left.infixOrder(); } System.out.println(this); if (this.right != null) { this.right.infixOrder(); } } @Override public String toString() { return "Node{" + "value=" + value + "}"; } }
前面我们已经构建出了一颗二叉排序树,不难发现因为它的特殊排列规律,查找时相当于每次都是使用了二分法查找,所以查找效率比较高。那么二叉排序树的删除又是怎样的呢?
二叉排序树的删除情况比较复杂,有下面三种情况需要考虑:
1、删除叶子节点 (比如:2, 5, 9, 12
)
2、删除只有一颗子树的节点 (比如:1
)
3、删除有两颗子树的节点. (比如:7, 3, 10
)
对删除结点的各种情况的思路分析:
第一种情况:删除叶子节点 (比如:2, 5, 9, 12
) 思路:
targetNode
targetNode
的 父结点 parent
targetNode
是 parent 的左子结点 还是右子结点parent.left = null
②右子结点 parent.right = null
第二种情况:删除只有一颗子树的节点(比如1
)思路:
targetNode
targetNode
的 父结点 parenttargetNode
的子结点是左子结点还是右子结点targetNode
是 parent 的左子结点还是右子结点targetNode
有左子结点
parent.left = targetNode.left;
parent.right = targetNode.left;
targetNode
有右子结点
parent.left = targetNode.right;
parent.right = targetNode.right;
第三种情况:删除有两颗子树的节点(比如:7, 3, 10
)思路:
targetNode.value = temp
完整代码示例:
package com.laizhenghua.tree; /** * @description: * @author: laizhenghua * @date: 2021/11/9 20:11 */ public class BinarySortTreeTest { public static void main(String[] args) { Integer[] array = new Integer[]{7, 3, 10, 12, 5, 1, 9, 2}; // 构建二叉排序树 BinarySortTree binarySortTree = new BinarySortTree(); for (Integer value : array) { binarySortTree.add(new Node(value)); } binarySortTree.infixOrder(); // 1 2 3 5 7 9 10 12 // 删除值为2的节点 binarySortTree.deleteNode(2); System.out.println(); // 中序遍历 binarySortTree.infixOrder(); // 1 3 5 7 9 10 12 } } // 创建二叉排序树 class BinarySortTree { private Node root; // 根节点 // 添加节点方法 public void add(Node node) { if (this.root == null) { this.root = node; } else { root.add(node); } } // 中序遍历 public void infixOrder() { if (root.left != null) { root.infixOrder(); } else { System.out.println("BinarySortTree is null"); } } // 查找节点 public Node searchNode(Integer value) { if (this.root == null) { System.out.println("BinarySortTree is null"); return null; } else { return this.root.searchNode(value); } } // 查找父节点 public Node searchParentNode(Integer value) { if (this.root == null) { System.out.println("BinarySortTree is null"); return null; } else { return this.root.searchParentNode(value); } } // 删除节点 public void deleteNode(Integer value) { if (root == null) { System.out.println("BinarySortTree is null"); } Node targetNode = searchNode(value); if (targetNode == null) { System.out.printf("This node[value = %s] cannot be found", value); } // 如果我们发现当前这可二叉树排序树只有一个节点,并且还是要删除的节点 if (root.left == null && root.right == null) { root = null; return; } Node parentNode = searchParentNode(value); // 情况1删除叶子节点 if (targetNode.left == null && targetNode.right == null) { if (parentNode.left != null && parentNode.left.value == value) { // 删除父节点的左子节点 parentNode.left = null; } else if (parentNode.right != null && parentNode.right.value == value) { // 删除父节点的右子节点 parentNode.right = null; } } else if (targetNode.left != null && targetNode.right != null) { // 删除有两颗子树的节点 Integer minValue = getAndDeleteRightTreeMin(targetNode.right); targetNode.value = minValue; } else { // 删除只有一颗子树的节点 if (targetNode.left != null) { // 如果要删除的节点又左子节点 if (parentNode.left.value == value) { parentNode.left = targetNode.left; } else { parentNode.right = targetNode.left; } } else { // 如果要删除的节点有右子节点 if (parentNode.left.value == value) { parentNode.left = targetNode.right; } else { parentNode.right = targetNode.right; } } } } // 传入node节点(当做二叉排序树的根节点)删除并返回以node为根节点的二叉排序树的最小节点的值 public Integer getAndDeleteRightTreeMin(Node node) { Node minValueNode = node; while (minValueNode.left != null) { minValueNode = minValueNode.left; } // 此时minValueNode就是以node为根节点的最小值 deleteNode(minValueNode.value); return minValueNode.value; } } // 创建Node节点 class Node { Integer value; Node left; Node right; public Node(Integer value) { this.value = value; } // 递归添加节点,需要满足二叉树排序树要求 public void add(Node node) { if (node == null) { return; } // 和根节点比较value if (node.value < this.value) { if (this.left == null) { this.left = node; } else { this.left.add(node); // 递归向左子树添加 } } else { if (this.right == null) { this.right = node; } else { this.right.add(node); // 递归向右子树添加 } } } /** * 查找指定value的节点 * @param value 查找节点的value * @return 如果找到则返回,找不到则返回null */ public Node searchNode(Integer value) { if (this.value == value) { // 找到该节点 return this; } else if (this.value > value) { // 向左递归查找 if (this.left != null) { return this.left.searchNode(value); } } else if (this.value < value) { // 向右递归查找 if (this.right != null) { return this.right.searchNode(value); } } return null; } /** * 查找指定值的父节点 * @param value * @return 返回父节点,如果没有则返回null */ public Node searchParentNode(Integer value) { if (this.right == null && this.left == null) { return null; } if (this.right != null && this.right.value == value) { return this; } else if (this.left != null && this.left.value == value) { return this; } else if (this.value > value) { // 向左递归查找 if (this.left != null) { return this.left.searchParentNode(value); } } else if (this.value < value) { if (this.right != null) { return this.right.searchParentNode(value); } } return null; } // 中序遍历 public void infixOrder() { if (this.left != null) { this.left.infixOrder(); } System.out.println(this); if (this.right != null) { this.right.infixOrder(); } } @Override public String toString() { return "Node{" + "value=" + value + "}"; } }
前面我们已经完成了BST
的构建、添加、查找、删除,看似已经万事大吉了。但是以上代码删除方法写的有漏洞!比如说二叉排序树长这样:
删除value=7
的节点!此时就会报空指针异常。这是为啥?删除情况属于删除只有一颗子树的节点,不难发现当7
为根节点时,已经没有父节点了。当去判断parentNode.left.value
必定抛出空指针!所以我们还要对删除方法进行改造!
// 删除节点 public void deleteNode(Integer value) { if (root == null) { System.out.println("BinarySortTree is null"); } Node targetNode = searchNode(value); if (targetNode == null) { System.out.printf("This node[value = %s] cannot be found", value); } // 如果我们发现当前这可二叉树排序树只有一个节点,并且还是要删除的节点 if (root.left == null && root.right == null) { root = null; return; } Node parentNode = searchParentNode(value); // 情况1删除叶子节点 if (targetNode.left == null && targetNode.right == null) { if (parentNode.left != null && parentNode.left.value == value) { // 删除父节点的左子节点 parentNode.left = null; } else if (parentNode.right != null && parentNode.right.value == value) { // 删除父节点的右子节点 parentNode.right = null; } } else if (targetNode.left != null && targetNode.right != null) { // 删除有两颗子树的节点 Integer minValue = getAndDeleteRightTreeMin(targetNode.right); targetNode.value = minValue; } else { // 删除只有一颗子树的节点 if (targetNode.left != null) { if (parentNode != null) { // 如果要删除的节点又左子节点 if (parentNode.left.value == value) { parentNode.left = targetNode.left; } else { parentNode.right = targetNode.left; } } else { root = targetNode.left; } } else { if (parentNode != null) { // 如果要删除的节点有右子节点 if (parentNode.left.value == value) { parentNode.left = targetNode.right; } else { parentNode.right = targetNode.right; } } else { root = targetNode.right; } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。