数据结构022_二叉排序树(创建、遍历、删除)
二叉排序树(二叉搜索树)(Binary Sort(Search) Tree),快速查找、添加、删除。
一、性质:
1)若左子树不为空,则左子树上所有节点的值都小于根结点的值。
2)若右子树不为空,右子树所有节点的值大于根结点的值。
3)左右子树也分别是二叉排序树。
创建遍历没有什么问题,删除需要考虑你要删除的节点是叶子节点还是有一颗子树的节点还是有两棵子树的节点。
二、删除思路:
1)删除叶子节点:
- 找到需要删除的节点targetNode;
- 找到父节点parentNode;
- 确定是父节点的左子节点还是右子节点;
- 将父节点的对应子节点置空即可。
2)删除有一颗子树的节点:(问题不大就是将子节点代替删除节点)
- 找到需要删除的节点targetNode;
- 找到父节点parentNode;
- 确定targetNode的子节点是左子节点还是右子节点;
- 确定targetNode是parentNode的左子节点还是右子节点;
- 如果targetNode有左子节点:
- 如果targetNode是parentNode的左子节点,将parentNode的左子节点指向targetNode的左子节点
- 如果targetNode是parentNode的右子节点,将parentNode的左子节点指向teargetNode右子节点
6.如果targetNode有右子节点:
- 如果targetNode是parentNode的左子节点,parentNode.left = targetNode.right;
- 如果targetNode是parentNode的右子节点,parentNode.right= targetNode.right;
3)删除有两棵子树的节点 :(问题不大就是提溜出一个左边最大或者右边最小放在待删除节点,也就是为了保持排序树的特点)
- 找到需要删除的节点targetNode;
- 找到父节点parentNode;
- 从targetNode右子树找最小节点(或者从左子树找最大节点)
- 用一个临时变量temp保存这个最小节点的值
- 删除最小节点(这一步的删除就简单了,最小节点只会是没有子树或者有一颗子树,就是上面前两种情况)
- 将要删除节点的值换成temp
三、创建遍历删除代码实现
package com.njcx.AVLTree; public class AVLtreeDemo { public static void main(String[] args) { int[] arr = { 8, 10, 6, 7, 9, 5, 3, 20 }; AVLTree avlTree = new AVLTree(); for (int item : arr) { avlTree.addNode(new Node(item)); } System.out.println("添加的二叉排序树中序遍历:"); avlTree.infixOrder(); avlTree.delNode(10); System.out.println("删除节点后的二叉排序树:"); avlTree.infixOrder(); } } class AVLTree { private Node root; public Node getRoot() { return root; } /** * 添加节点 * * @param node */ public void addNode(Node node) { if (root == null) root = node; else root.addNode(node); } /** * 中序遍历 */ public void infixOrder() { if (root == null) System.out.println("空树"); else root.infixOrder(); } /** * * 前序遍历 */ public void preOrder() { if (root == null) System.out.println("空树"); else root.preOrder(); } /** * 查找要删除的节点 * * @param value * @return */ public Node searchNode(int value) { if (root == null) return null; else return root.searchNode(value); } /** * 查找要删除节点的父节点 * * @param value * @return */ public Node searchParent(int value) { if (root == null) return null; else return root.searchParent(value); } /** * 删除节点 * * @param node */ public void delNode(int value) { if (root == null) return; Node targetNode = searchNode(value); // 如果没有找到要删除的节点,返回 if (targetNode == null) return; // 如果当前二叉树只有一个节点,就不需要找父节点了 if (root.left == null && root.right == null) { root = null; return; } Node parentNode = searchParent(value); // 目标节点是叶子节点 if (targetNode.left == null && targetNode.right == null) { if (parentNode.left != null && parentNode.left == targetNode) parentNode.left = null; else if (parentNode.right != null && parentNode.right == targetNode) parentNode.right = null; } // 目标节点有两个子节点 else if (targetNode.left != null && targetNode.right != null) { int val = delMinNode(targetNode.right); // 找右子树最小的节点,删除最小的节点 targetNode.value = val;// 当前节点的值替换成最小的节点 } // 目标节点有一个子节点 else { if (targetNode.left != null) { if (parentNode != null) { // 这个判断我忘记加了就会报空指针异常 if (parentNode.left == targetNode) parentNode.left = targetNode.left; else parentNode.right = targetNode.left; } else{ root = targetNode.left; } } else { if (parentNode != null) { if (parentNode.left == targetNode) parentNode.left = targetNode.right; else parentNode.right = targetNode.right; } else { root = targetNode.right; } } } } /** * 删除以node为根结点的树的最小节点并将最小节点的值返回 * * @param targetNode */ public int delMinNode(Node node) { Node target = node; while (target.left != null) target = target.left; int value = target.value; delNode(value); return value; } } class Node { int value; Node left; Node right; public Node(int value) { this.value = value; } @Override public String toString() { return "Node [value=" + value + "]"; } /** * 二叉排序树添加节点的方法 * * @param node */ public void addNode(Node node) { if (node == null) return; if (node.value > this.value) { // 递归向右子树添加 if (this.right == null) this.right = node; else this.right.addNode(node); } else { // 递归向左子树添加 if (this.left == null) this.left = node; else this.left.addNode(node); } } /** * 中序遍历 */ public void infixOrder() { if (this.left != null) this.left.infixOrder(); System.out.println(this); if (this.right != null) this.right.infixOrder(); } /** * 前序遍历 没必要其实,纯粹是为辅助中序遍历验证我创建的树是我想象中的样子 */ public void preOrder() { System.out.println(this); if (this.left != null) this.left.preOrder(); if (this.right != null) this.right.preOrder(); } /** * 查找要删除的节点 * * @param node * @return */ public Node searchNode(int value) { if (this.left != null && this.value > value) return this.left.searchNode(value); if (this.right != null && this.value < value) return this.right.searchNode(value); if (this.value == value) return this; return null; } /** * 查找要删除节点的父节点 * * @param targetNode * @return */ public Node searchParent(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.searchParent(value); if (value > this.value && this.right != null) return this.right.searchParent(value); return null; } } }