目录
1. 数组和链表的缺点
- 1. 数组和链表的缺点
- 2. 二叉排序树的介绍
- 3. 二叉排序树删除节点的思路
- 4. 二叉排序树的程序实现
未排序的数组缺点:查找速度慢 排序的数组缺点:为了保证数组有序,在添加新数据时,找到插入位置后,后面的数据需整体移动,速度慢
链表的缺点:不管链表是否有序,查找速度都慢
2. 二叉排序树的介绍二叉排序树【BST: Binary Sort(Search) Tree】的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。如果有相同的值,可以将该节点放在左子节点或右子节点(本示例我们放在右子节点)
比如针对数列{7, 3, 10, 12, 5, 1, 9},对应的二叉排序树如下
添加值为2的节点,结果如下
二叉排序树删除节点的三种情况
-
第一种情况:删除叶子节点,比如2,5,9,12
- 找到要删除的节点targetNode
- 找到targetNode的父节点parentNode 3.1 如果targetNode是parentNode的左子节点,则parentNode.left = null 3.2 如果targetNode是parentNode的右子节点,则parentNode.right = null
-
第二种情况:删除有两颗子树的节点,比如7,3,10
- 找到要删除的节点targetNode
- 找到targetNode的父节点parentNode
- 从targetNode的右子树找到最小的节点
- 用一个临时变量,保存最小节点的值tmpValue
- 然后删除该最小节点(其实是第一种或第三种情况的节点)
- targetNode.value = tmpValue
-
第三种情况:删除只有一颗子树的节点,比如1
- 找到要删除的节点targetNode
- 找到targetNode的父节点parentNode 3.1 如果targetNode的子树是左子节点,且targetNode是parentNode的左子节点,则parentNode.left = targetNode.left 3.2 如果targetNode的子树是左子节点,且targetNode是parentNode的右子节点,则parentNode.right = targetNode.left 3.3 如果targetNode的子树是右子节点,且targetNode是parentNode的左子节点,则parentNode.left = targetNode.right 3.4 如果targetNode的子树是右子节点,且targetNode是parentNode的右子节点,则parentNode.right = targetNode.right
需求:一个数列{7, 3, 10, 12, 5, 1, 9, 2},用二叉排序树高效的完成对数据的查询、添加、删除
程序如下:
public class BinarySortTreeDemo { public static void main(String[] args) { int[] array = {7, 3, 10, 12, 5, 1, 9, 2}; BinarySortTree binarySortTree = new BinarySortTree(); //循环的添加结点到二叉排序树 for (int i = 0; i < array.length; i++) { binarySortTree.add(new Node(array[i])); } // 中序遍历二叉排序树 System.out.println("中序遍历二叉排序树: "); binarySortTree.infixOrder(); // 删除叶子节点 binarySortTree.deleteNode(12); binarySortTree.deleteNode(5); binarySortTree.deleteNode(10); binarySortTree.deleteNode(2); binarySortTree.deleteNode(3); binarySortTree.deleteNode(9); binarySortTree.deleteNode(1); binarySortTree.deleteNode(7); System.out.println("删除结点后,中序遍历二叉排序树:"); binarySortTree.infixOrder(); } } // 创建Node节点 class Node { int value; Node left; Node right; public Node(int value) { this.value = value; } @Override public String toString() { return "Node [value = " + value + "]"; } // 添加节点的方法 public void add(Node node) { if (node == null) { return; } else { // 如果node的值,比当前节点的值小,则向左处理 if (node.value < this.value) { if (this.left == null) { this.left = node; } else { // 向左递归添加节点 this.left.add(node); } // 如果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(); } } // 查找要删除的结点 public Node searchDeleteNode(int value) { if (value == this.value) { return this; // 如果值,比当前节点的值小,则向左处理 } else if (value < this.value) { if (this.left == null) { return null; } else { return this.left.searchDeleteNode(value); } // 如果值,大于等于当前节点的值,则向右处理 } else { if (this.right == null) { return null; } else { return this.right.searchDeleteNode(value); } } } // 查找要删除节点的父节点 public Node searchDeleteParentNode(int value) { // 如果当前结点左子节点或右子节点是要删除的节点,则返回当前节点 if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) { return this; } else { // 如果当前节点不是,则递归向左子节点进行查找 if (value < this.value && this.left != null) { return this.left.searchDeleteParentNode(value); // 再递归向右子节点进行查找 } else if (value >= this.value && this.right != null) { return this.right.searchDeleteParentNode(value); //向右子树递归查找 } else { // 如果不能向左右子节点递归,则返回null return null; } } } } // 创建二叉排序树 class BinarySortTree { private Node root; // 添加节点的方法 public void add(Node node) { if (root == null) { root = node; } else { root.add(node); } } // 中序遍历实现 public void infixOrder() { if (root != null) { root.infixOrder(); } else { System.out.println("二叉排序树为空,不能遍历"); } } // 查找要删除的结点 public Node searchDeleteNode(int value) { if (root == null) { return null; } else { return root.searchDeleteNode(value); } } // 查找要删除节点的父节点 public Node searchDeleteParentNode(int value) { if (root == null) { return null; } else { return root.searchDeleteParentNode(value); } } // 传入右子树,删除该子树的最小值节点,然后返回最小值节点的值 public int delRightTreeMinNode(Node node) { Node target = node; // 循环查找左子节点,就会找到最小值 while (target.left != null) { target = target.left; } // 这时target就是最小值节点 deleteNode(target.value); return target.value; } // 删除节点实现 public void deleteNode(int value) { if (root == null) { return; } else { // 找到要删除的节点targetNode Node targetNode = searchDeleteNode(value); // 如果没有找到要删除的节点 if (targetNode == null) { return; } else { // 如果找到节点,且二叉排序树只有root这一个节点,则直接删除root节点 if (root.left == null && root.right == null) { root = null; return; } else { // 找到targetNode的父节点parentNode Node parentNode = searchDeleteParentNode(value); // 第一种情况:删除叶子节点 if (targetNode.left == null && targetNode.right == null) { // 如果targetNode是parentNode的左子节点,则parentNode.left = null if (parentNode.left != null && parentNode.left.value == value) { parentNode.left = null; // 如果targetNode是parentNode的右子节点,则parentNode.right = null } else if (parentNode.right != null && parentNode.right.value == value) { parentNode.right = null; } // 第二种情况:删除有两颗子树的节点 // 如果删除的是root节点,parentNode为null也不影响 } else if (targetNode.left != null && targetNode.right != null) { int minVal = delRightTreeMinNode(targetNode.right); targetNode.value = minVal; // 第三种情况:删除只有一颗子树的节点 } else { // 如果targetNode的子树是左子节点 if (targetNode.left != null) { if (parentNode != null) { // 如果targetNode是parentNode的左子节点 if (parentNode.left.value == value) { parentNode.left = targetNode.left; } else { // 如果targetNode是parentNode的右子节点 parentNode.right = targetNode.left; } // 如果要删除的是root节点,且root节点只有左子树 } else { root = targetNode.left; } // 如果targetNode的子树是右子节点 } else { if (parentNode != null) { // 如果targetNode是parentNode的左子节点 if (parentNode.left.value == value) { parentNode.left = targetNode.right; } else { // 如果targetNode是parentNode的右子节点 parentNode.right = targetNode.right; } } else { // 如果要删除的是root节点,且root节点只有右子树 root = targetNode.right; } } } } } } } }
运行程序,结果如下:
中序遍历二叉排序树: Node [value = 1] Node [value = 2] Node [value = 3] Node [value = 5] Node [value = 7] Node [value = 9] Node [value = 10] Node [value = 12] 删除结点后,中序遍历二叉排序树: 二叉排序树为空,不能遍历