java - 数据结构 - 二叉排序树
二叉排序树(BST)
任何一个节点, 左边的节点值都比他小,右边的都比他大,中序遍历得到的是从小到大的数列。
可以用来排序和快速查找。
1. 添加
从根节点向下寻找,比当前节点小就向左,比当前节点大就向右,直到最低端,然后称为新的叶子节点(没有孩子节点的节点)。
2. 查询
从根节点向下寻找,比当前节点小就向左,比当前节点大就向右,相等时就找到了
3. 删除(主要难点)
3种情况
1. 没有孩子节点
叶子节点可以直接删掉,用null代替其原来位置
2. 有一个孩子
用孩子节点代替自己。
3. 有两个孩子
用右孩子的最小叶子节点,或者左孩子的最大叶子节点代替自己,然后删掉那个叶子节点
本质就是找个跟自己中序遍历时挨着的节点代替自己,这样不会影响整体结构。
平衡二叉树(以后再整理=。=)的继承节点是找自己右孩子的左叶子节点,所以这里就用右孩子的左叶子节点代替了。
package tree; public class BinarySortTree { Node root; public BinarySortTree(){ root = null; } //添加节点 public void add(int num){ if(root == null) { root = new Node(num); } else{ Node n = new Node(num); root.add(n); } } //中序遍历 (按顺序从小到大) public void show(){ if(root == null){ System.out.println("树为空"); } else{ root.show(); } } //发现节点 public Node findNode(int num){ if(root == null){ System.out.println("树为空"); return null; } else{ return root.findNode(num); } } //发现父节点 public Node findParentNode(int num){ if(root == null){ System.out.println("树为空"); return null; } else{ return root.findParentNode(num); } } //发现以某个节点为根节点的子树的最小叶子节点(用来找继承者) public Node findSubTreeMin(Node node){ //因为只是为了找最小,每个节点左边的都比他小,所以只遍历左边 //不过如果有重复值,按照插入的规则会在左边,会继续向左找,保证找到的是叶子节点。 if(node.left == null){ return node; } else{ //左子树存在,说明有更小的值,继续向左找 return findSubTreeMin(node.left); } } //删除节点 public void delete(int num){ if(root == null){ System.out.println("树为空"); return; } else{ Node targetNode = findNode(num); if (targetNode == null) { System.out.println("没有发现目标节点"); return; } Node parentNode = findParentNode(num); if (parentNode == null) { //没有父亲节点,说明是root System.out.println("目标节点是root"); if(root.left != null && root.right == null){ //左节点不为空, 右节点为空 root = root.left; return; } else if(root.left == null && root.right != null){ //左节点不为空, 右节点为空 root = root.right; return; } else if(root.left == null && root.right == null){ //左右都为空 root = null; return; } //3.两个孩子, 用左边最大的叶子节点或者右边最小的叶子节点代替自己 (这里写的是用右边最小的代替) //**********注意,如果这里是用右边最小代替,插入的时候重复值要插在左边,不然会导致找到的不是叶子节点,出现问题。 //上面写的方法是发现子树的最小节点,这里是发现目标右边子树最小的节点,所以参数是root.right if(root.left != null && root.right != null){ Node rightMinNode = findSubTreeMin(targetNode.right); int temp = rightMinNode.value; delete(temp); root.value = temp; } } else{ // target不是根节点 // 1. 叶子节点, 2.一个孩子, 3,两个孩子 //1. 叶子节点, 用null代替自己 if(targetNode.left == null && targetNode.right == null){ //左右都为空 (叶子节点) //判断是父节点的左孩子还是右孩子,然后置空; if(parentNode.left != null && parentNode.left == targetNode){ parentNode.left = null; return; } else if(parentNode.right != null && parentNode.right == targetNode){ parentNode.right = null; return; } } //2.一个孩子, 用孩子代替自己 if(targetNode.left != null && targetNode.right == null){ //有左孩子没有右孩子,把左孩子放在自己当前位置 //判断是父节点的左孩子还是右孩子,然后放置 if(parentNode.left != null && parentNode.left == targetNode){ parentNode.left = targetNode.left; return; } else if(parentNode.right != null && parentNode.right == targetNode){ parentNode.right = targetNode.left; return; } } if(targetNode.left == null && targetNode.right != null){ //有右孩子没有左孩子,把右孩子放在自己当前位置 //判断是父节点的左孩子还是右孩子,然后放置 if(parentNode.left != null && parentNode.left == targetNode){ parentNode.left = targetNode.right; return; } else if(parentNode.right != null && parentNode.right == targetNode){ parentNode.right = targetNode.right; return; } } //3.两个孩子, 用左边最大的叶子节点或者右边最小的叶子节点代替自己 (这里写的是用右边最小的代替) //上面写的方法是发现子树的最小节点,这里是发现目标右边子树最小的节点,所以参数是targetNode.right if(targetNode.left != null && targetNode.right != null){ Node rightMinNode = findSubTreeMin(targetNode.right); //判断是父节点的左孩子还是右孩子,然后放置 if(parentNode.left != null && parentNode.left == targetNode){ int temp = rightMinNode.value; delete(temp); parentNode.left.value = temp; return; } else if(parentNode.right != null && parentNode.right == targetNode){ int temp = rightMinNode.value; delete(temp); parentNode.right.value = temp; return; } } } } } public static void main(String[] args){ BinarySortTree bst = new BinarySortTree(); bst.add(5); bst.add(4); bst.add(6); bst.add(2); bst.add(8); bst.add(3); bst.add(9); bst.add(1); bst.add(7); bst.add(5); bst.add(4); bst.add(6); System.out.println("中序遍历"); bst.show(); System.out.println(); bst.delete(5); //删除一个节点 bst.show(); } public class Node{ public int value; public Node left; public Node right; public Node(int value){ this.value = value; } public void add(Node n){ if(n == null){ return; } else{ if(n.value <= this.value){ //相等的放在左边 if(this.left == null){ this.left = n; } else{ this.left.add(n); } } else{ // >= if(this.right == null){ this.right = n; } else{ this.right.add(n); } } } }//add end public void show(){ if(this.left != null){ this.left.show(); } System.out.print(this.value + " "); if(this.right != null){ this.right.show(); } } public void delete(int num){ Node father = this; Node child = null; } public Node findNode(int num){ Node n = null; if(this.value == num){ return this; } else if(num < this.value && this.left != null){ return this.left.findNode(num); } else if(num >= this.value && this.right != null){ return this.right.findNode(num); } else{ return null; } } public Node findParentNode(int num){ Node n = null; if(this.left != null && this.left.value == num){ //num 是左孩子 return this; } else if(this.right != null && this.right.value == num){ //num 是右孩子 return this; } else if(num < this.value && this.left != null){// num不是孩子节点,且比当前节点小,继续向左寻找 return this.left.findParentNode(num); } else if(num >= this.value && this.right != null){// num不是孩子节点,且比当前节点大,继续向右寻找 return this.right.findParentNode(num); } else{ //进行不下去了,说明无法发现节点,返回null System.out.println("父节点不存在"); return null; } } } }