二叉排序树(BST)介绍
二叉排序树例子
需求
给你一个数列{7,3,10,12,5,1,9},要求能够高效的完成对数据的查询和添加。
解决方案分析
- 使用数组
- 数组未排序,优点:直接再数组末尾添加,速度快;缺点:查找速度慢。
- 数组排序,优点:可以使用二分查找,查找速度快;缺点:为了保证数组有序,在添加新数据时,找到插入位置后,数据需要整体移动,速度慢。
- 使用链式存储(链表)
- 不管链表是否有序,查找速度都慢,添加数据不需要整体移动,添加速度比数组快。
- 使用二叉排序树
二叉排序树介绍
二叉排序树:BST(Binary Sort(Search) Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。
特别说明:如果有相同值,可以将该节点放在左子节点或右子节点。
例如上方例子中的数组的二叉排序树应该为:
二叉排序树创建和遍历
创建数列{7,3,10,12,5,1,9}对应的二叉排序树并使用中序遍历;
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| package com.jokerdig.binarySortTree;
public class BinarySortTreeDemo { public static void main(String[] args) { int [] arr = {7,3,10,12,5,1,9}; BinarySortTree binarySortTree = new BinarySortTree(); for(int value:arr){ binarySortTree.add(new Node(value)); } binarySortTree.infixOrder(); } }
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("没有数据"); } } }
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; } 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(); } } }
|
运行结果
1 2 3 4 5 6 7 8 9
| Node{value=1} Node{value=3} Node{value=5} Node{value=7} Node{value=9} Node{value=10} Node{value=12}
Process finished with exit code 0
|
二叉排序树删除节点思路图解
思路分析
二叉排序树的删除情况比较复杂,有以下三种情况考虑:
-
删除叶子节点(例如:2,5,9,12)
-
删除只有一棵子树的节点(例如:1)
-
删除有两棵子树的节点(例如:7,3,10)
示意图:
步骤分析
-
删除叶子节点
-
需要先去定位要删除的节点targetNode;
-
找到targetNode的父节点parent;
-
判断targetNode是parent的左子节点还是右子节点;
-
根据上方的情况来对应删除;
左子节点:parent.left = null;
右子节点:parent.right = null;
-
删除只有一棵子树的节点
- 需要先去定位要删除的节点targetNode;
- 找到targetNode的父节点parent;
- 确定targetNode的子节点是左子节点还是右子节点;
- targetNode是parent的左子节点还是右子节点;
- 如果targetNode有左子节点:
- 如果targetNode是parent的左子节点:parent.left = targetNode.left;
- 如果targetNode是parent的右子节点:parent.right = targetNode.left;
- 如果targetNode有右子节点:
- 如果targetNode是parent的左子节点:parent.left = targetNode.right;
- 如果targetNode是parent的右子节点:parent.right = targetNode.right;
-
删除有两棵子树的节点
- 需要先去定位要删除的节点targetNode;
- 找到targetNode的父节点parent;
- 从targetNode的右子树找到最小的节点(或左子树找到最大节点);
- 用一个临时变量temp,将最小节点值保存;
- 删除该最小节点;
- targetNode.value = temp;
二叉排序树删除叶子节点
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
| package com.jokerdig.binarySortTree;
public class BinarySortTreeDemo { public static void main(String[] args) { int [] arr = {7,3,10,12,5,1,9,2}; BinarySortTree binarySortTree = new BinarySortTree(); for(int value:arr){ binarySortTree.add(new Node(value)); } binarySortTree.infixOrder();
binarySortTree.delNode(2); System.out.println("删除叶子节点2后的二叉排序树"); binarySortTree.infixOrder(); } }
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 search(int value){ if(root == null){ return null; }else{ return root.search(value); } }
public Node searchParent(int value){ if(root == null){ return null; }else{ return root.searchParent(value); } }
public void delNode(int value){ if(root==null){ return; }else{ Node targetNode = search(value); if(targetNode == null){ return; } if(root.left == null && root.right == null){ root = null; return; } Node parent = searchParent(value); if(targetNode.left == null && targetNode.right == null){ if(parent.left != null && parent.left.value ==value){ parent.left = null; }else if(parent.right !=null && parent.right.value == value){ parent.right = null; } } } } }
class Node{ int value; Node left; Node right;
public Node(int value) { this.value = value; }
@Override public String toString() { return "Node{" + "value=" + value + '}'; }
public Node search(int value){ if(value == this.value){ return this; }else if(value < this.value){ if(this.left == null){ return null; } return this.left.search(value); }else{ if(this.right == null){ return null; } return this.right.search(value); } }
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); } else if(value >= this.value && this.right!=null){ return this.right.searchParent(value); } else{ return null; } } } public void add(Node node){ if(node==null) { return; } 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(); } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Node{value=1} Node{value=2} Node{value=3} Node{value=5} Node{value=7} Node{value=9} Node{value=10} Node{value=12} 删除叶子节点2后的二叉排序树 Node{value=1} Node{value=3} Node{value=5} Node{value=7} Node{value=9} Node{value=10} Node{value=12}
Process finished with exit code 0
|
二叉排序树删除有一棵子树的节点
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
| package com.jokerdig.binarySortTree;
public class BinarySortTreeDemo { public static void main(String[] args) { int [] arr = {7,3,10,12,5,1,9,2}; BinarySortTree binarySortTree = new BinarySortTree(); for(int value:arr){ binarySortTree.add(new Node(value)); } binarySortTree.infixOrder();
binarySortTree.delNode(1); System.out.println("删除有一棵子树的节点的二叉排序树"); binarySortTree.infixOrder(); } }
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 search(int value){ if(root == null){ return null; }else{ return root.search(value); } }
public Node searchParent(int value){ if(root == null){ return null; }else{ return root.searchParent(value); } } public void delNode(int value){ if(root==null){ return; }else{ Node targetNode = search(value); if(targetNode == null){ return; } if(root.left == null && root.right == null){ root = null; return; } Node parent = searchParent(value); if(targetNode.left == null && targetNode.right == null){ if(parent.left != null && parent.left.value ==value){ parent.left = null; }else if(parent.right !=null && parent.right.value == value){ parent.right = null; } }else if(targetNode.left!=null && targetNode.right!=null){
}else{ if(targetNode.left!=null){ if(parent.left.value == value){ parent.left = targetNode.left; }else{ parent.right = targetNode.left; } } else{ if(parent.left.value == value){ parent.left = targetNode.right; }else{ parent.right = targetNode.right; } } } } } }
class Node{ int value; Node left; Node right;
public Node(int value) { this.value = value; }
@Override public String toString() { return "Node{" + "value=" + value + '}'; }
public Node search(int value){ if(value == this.value){ return this; }else if(value < this.value){ if(this.left == null){ return null; } return this.left.search(value); }else{ if(this.right == null){ return null; } return this.right.search(value); } }
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); } else if(value >= this.value && this.right!=null){ return this.right.searchParent(value); } else{ return null; } } } public void add(Node node){ if(node==null) { return; } 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(); } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Node{value=1} Node{value=2} Node{value=3} Node{value=5} Node{value=7} Node{value=9} Node{value=10} Node{value=12} 删除有一棵子树的节点的二叉排序树 Node{value=2} Node{value=3} Node{value=5} Node{value=7} Node{value=9} Node{value=10} Node{value=12}
Process finished with exit code 0
|
二叉排序树删除有两棵子树的节点
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
| package com.jokerdig.binarySortTree;
public class BinarySortTreeDemo { public static void main(String[] args) { int [] arr = {7,3,10,12,5,1,9,2}; BinarySortTree binarySortTree = new BinarySortTree(); for(int value:arr){ binarySortTree.add(new Node(value)); } binarySortTree.infixOrder();
binarySortTree.delNode(3); System.out.println("删除有两棵子树的节点的二叉排序树"); binarySortTree.infixOrder(); } }
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 search(int value){ if(root == null){ return null; }else{ return root.search(value); } }
public Node searchParent(int value){ if(root == null){ return null; }else{ return root.searchParent(value); } } public void delNode(int value){ if(root==null){ return; }else{ Node targetNode = search(value); if(targetNode == null){ return; } if(root.left == null && root.right == null){ root = null; return; } Node parent = searchParent(value); if(targetNode.left == null && targetNode.right == null){ if(parent.left != null && parent.left.value ==value){ parent.left = null; }else if(parent.right !=null && parent.right.value == value){ parent.right = null; } }else if(targetNode.left!=null && targetNode.right!=null){ int min = delRightTreeMin(targetNode.right); targetNode.value = min; }else{ if(targetNode.left!=null){ if(parent.left.value == value){ parent.left = targetNode.left; }else{ parent.right = targetNode.left; } } else{ if(parent.left.value == value){ parent.left = targetNode.right; }else{ parent.right = targetNode.right; } } } } }
public int delRightTreeMin(Node node){ Node target = node; while(target.left!=null){ target = target.left; } delNode(target.value); return target.value; } }
class Node{ int value; Node left; Node right;
public Node(int value) { this.value = value; }
@Override public String toString() { return "Node{" + "value=" + value + '}'; }
public Node search(int value){ if(value == this.value){ return this; }else if(value < this.value){ if(this.left == null){ return null; } return this.left.search(value); }else{ if(this.right == null){ return null; } return this.right.search(value); } }
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); } else if(value >= this.value && this.right!=null){ return this.right.searchParent(value); } else{ return null; } } } public void add(Node node){ if(node==null) { return; } 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(); } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Node{value=1} Node{value=2} Node{value=3} Node{value=5} Node{value=7} Node{value=9} Node{value=10} Node{value=12} 删除有两棵子树的节点的二叉排序树 Node{value=1} Node{value=2} Node{value=5} Node{value=7} Node{value=9} Node{value=10} Node{value=12}
Process finished with exit code 0
|
二叉排序树删除节点注意事项
问题
在删除节点的时候,如果我们删除带有一棵子树的根节点,就会出现空指针异常,因为这时根节点的父节点为null。
完善代码
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
| package com.jokerdig.binarySortTree;
public class BinarySortTreeDemo { public static void main(String[] args) { int [] arr = {7,3,10,12,5,1,9,2}; BinarySortTree binarySortTree = new BinarySortTree(); for(int value:arr){ binarySortTree.add(new Node(value)); } binarySortTree.infixOrder();
binarySortTree.delNode(7); System.out.println("删除根节点后的二叉排序树"); binarySortTree.infixOrder(); } }
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 search(int value){ if(root == null){ return null; }else{ return root.search(value); } }
public Node searchParent(int value){ if(root == null){ return null; }else{ return root.searchParent(value); } } public void delNode(int value){ if(root==null){ return; }else{ Node targetNode = search(value); if(targetNode == null){ return; } if(root.left == null && root.right == null){ root = null; return; } Node parent = searchParent(value); if(targetNode.left == null && targetNode.right == null){ if(parent.left != null && parent.left.value ==value){ parent.left = null; }else if(parent.right !=null && parent.right.value == value){ parent.right = null; } }else if(targetNode.left!=null && targetNode.right!=null){ int min = delRightTreeMin(targetNode.right); targetNode.value = min; }else{ if(targetNode.left!=null){ if(parent!=null){ if(parent.left.value == value){ parent.left = targetNode.left; }else{ parent.right = targetNode.left; } }else{ root = targetNode.left; } } else{ if(parent!=null) { if(parent.left.value == value){ parent.left = targetNode.right; }else{ parent.right = targetNode.right; } }else{ root = targetNode.right; }
} } } }
public int delRightTreeMin(Node node){ Node target = node; while(target.left!=null){ target = target.left; } delNode(target.value); return target.value; } }
class Node{ int value; Node left; Node right;
public Node(int value) { this.value = value; }
@Override public String toString() { return "Node{" + "value=" + value + '}'; }
public Node search(int value){ if(value == this.value){ return this; }else if(value < this.value){ if(this.left == null){ return null; } return this.left.search(value); }else{ if(this.right == null){ return null; } return this.right.search(value); } }
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); } else if(value >= this.value && this.right!=null){ return this.right.searchParent(value); } else{ return null; } } } public void add(Node node){ if(node==null) { return; } 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(); } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Node{value=1} Node{value=2} Node{value=3} Node{value=5} Node{value=7} Node{value=9} Node{value=10} Node{value=12} 删除根节点后的二叉排序树 Node{value=1} Node{value=2} Node{value=3} Node{value=5} Node{value=9} Node{value=10} Node{value=12}
Process finished with exit code 0
|