二叉排序树
在线性表中,我们可以通过数组和链表对数据进行查询、添加、删除。
在数组中查询的时间复杂度为O(1) , 添加删除的操作时间复杂度为O(n)
在链表中查询的时间复杂度为O(n) ,添加删除的操作时间复杂度为O(1) 【且当前已找到需要删除的数据】
因此我们通过二叉树来存储数据,以及与数据的一些处理。
二叉排序树的定义
二叉排序树(BST),又称二叉查找树、二叉搜索树。
对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点比当前节点的值大。
特别说明,如果有相同的值,可以将该节点放在左子节点或右子节点。
比如对下面的一组数据(7,3,10,8,5,15,1),二叉排序树为:
验证是否为二叉树:
同样我们也可以进行中序遍历来验证
二叉排序树的创建和遍历
创建
之后的节点添加都类似。
最后添加完成
代码:
class Node{
public int val;
public Node leftNode;
public Node rightNode;
public Node(int i) {
this.val = i;
}
//中序遍历
public void infixOrder(){
if(this.leftNode != null){
this.leftNode.infixOrder();
}
System.out.print(this.val + "\t");
if(this.rightNode != null){
this.rightNode.infixOrder();
}
}
//添加节点
public void add(Node node){
if(node == null)
return;
//如果新添加节点小于当前节点
if(node.val < this.val){
if(this.leftNode == null){
this.leftNode = node;
}else{
this.leftNode.add(node);
}
}else{//如果新添加节点大于等于当前节点
if(this.rightNode == null){
this.rightNode = node;
}else{
this.rightNode.add(node);
}
}
}
}
class Tree{
private Node root;
public Tree() {
}
//中序遍历
public void infixOrder(){
if(this.root != null){
this.root.infixOrder();
}else{
System.out.println("二叉排序树为空");
}
}
//二叉排序树添加节点
public void add(Node node){
if(root == null){
root = node;
}else{
root.add(node);
}
}
}
public class BinarySortTree {
public static void main(String[] args) {
Node node1 = new Node(7);
Node node2 = new Node(3);
Node node3 = new Node(10);
Node node4 = new Node(8);
Node node5 = new Node(5);
Node node6 = new Node(15);
Node node7 = new Node(1);
Tree tree = new Tree();
tree.add(node1);
tree.add(node2);
tree.add(node3);
tree.add(node4);
tree.add(node5);
tree.add(node6);
tree.add(node7);
tree.infixOrder(); //1 3 5 7 8 10 15
}
}
二叉排序树的删除
二叉排序树的删除需要考虑三种情况:
1、删除叶子节点
2、删除只有一个子树的节点
3、删除有两颗子树的节点
我们重新创建一个二叉排序树,节点值为:【7,3,10,12,5,1,9,2】
二叉排序树为:
第一种情况:删除节点2、5、9、12
1、找到需要删除的节点
2、找到需要删除节点的父节点
3、判断需要删除节点是父节点的左子节点或右子节点
4、如果为左子节点,parent.leftNode = null,如果为右子节点,parent.rightNode = null
删除节点2之后:
第二种情况:删除节点1
1、找到需要删除的节点targetNode
2、找到targetNode的父节点parent
3、确定targetNode的子结点是左子结点还是右子节点
4、确定targetNode是parent的左子节点还是右子节点。
5、如果targetNode有左子节点
5.1、如果targetNode是parent的左子节点 parent.leftNode = targetNode.left;
5.2、如果targetNode是parent的右子节点 parent.rightNode = targetNode.left;
6、如果targetNode有右子节点
5.1、如果targetNode是parent的左子节点 parent.leftNode = targetNode.right;
5.2、如果targetNode是parent的右子节点 parent.rightNode = targetNode.right;
删除节点1之后:
第三种情况 :删除节点3,10
1、找到需要删除的节点targetNode
2、找到targetNode的父节点parent
3、从targetNode的右子树中找到最小的节点【或者左子树中找到最大节点】 。
4、用一个临时变量,将最小的节点的值保存到temp中。
5、删除右子树中最小的节点【或左子树中最大的节点】
6、targetNode.val = temp;
删除节点3之后:
代码:
class Node{
public int val;
public Node leftNode;
public Node rightNode;
public Node(int i) {
this.val = i;
}
//中序遍历
public void infixOrder(){
if(this.leftNode != null){
this.leftNode.infixOrder();
}
System.out.print(this.val + "\t");
if(this.rightNode != null){
this.rightNode.infixOrder();
}
}
//添加节点
public void add(Node node){
if(node == null)
return;
//如果新添加节点小于当前节点
if(node.val < this.val){
if(this.leftNode == null){
this.leftNode = node;
}else{
this.leftNode.add(node);
}
}else{//如果新添加节点大于等于当前节点
if(this.rightNode == null){
this.rightNode = node;
}else{
this.rightNode.add(node);
}
}
}
//查找需要删除的节点
public Node Search(int val){
if(this.val == val){
return this;
}else if(this.val < val){
if(this.rightNode == null)
return null;
return this.rightNode.Search(val);
}else{
if(this.leftNode == null)
return null;
return this.leftNode.Search(val);
}
}
//查找需要删除节点的父节点
public Node SearchParent(int val){
if((this.leftNode != null && this.leftNode.val == val) || (this.rightNode != null && this.rightNode.val == val)) {
return this;
}else{
//如果查找的值小于当前节点的值,并且当前节点的左子节点不为空
if (val < this.val && this.leftNode != null){
return this.leftNode.SearchParent(val);
}else if (val >= this.val && this.rightNode != null){
return this.rightNode.SearchParent(val);
}else{
return null;
}
}
}
}
class Tree{
private Node root;
public Tree() {
}
//中序遍历
public void infixOrder(){
if(this.root != null){
this.root.infixOrder();
}else{
System.out.println("二叉排序树为空");
}
}
//二叉排序树添加节点
public void add(Node node){
if(root == null){
root = node;
}else{
root.add(node);
}
}
//查找需要删除的节点
public Node Search(int val){
if(root == null){
return null;
}else{
return root.Search(val);
}
}
//查找父节点
public Node SearchParent(int val){
if(this.root == null) {
return null;
}else{
return root.SearchParent(val);
}
}
//找到需要删除节点的右子树的最小值
public int delRightTreeMin(Node node){
Node target = node;
while(target.leftNode != null){
target = target.leftNode;
}
delNode(target.val);
return target.val;
}
//删除节点
public void delNode(int val){
if(root == null) {
return;
} else{
//1、找到需要删除的节点
Node targetNode = Search(val);
if(targetNode == null){
return;
}
//如果当前二叉排序树只有一个节点
if(root.leftNode == null || root.rightNode == null){
root = null;
return;
}
//找到targetNode 的父节点,因为不是根节点,所以一定右父节点
Node parent = SearchParent(val);
//如果要删除的节点是叶子节点
if(targetNode.leftNode == null && targetNode.rightNode == null){
if(parent.leftNode != null && parent.leftNode.val == val){
parent.leftNode = null;
}else if(parent.rightNode != null && parent.rightNode.val == val){
parent.rightNode = null;
}
//如果删除的节点右两个子结点
}else if(targetNode.leftNode != null && targetNode.rightNode != null){
int minVal = delRightTreeMin(targetNode.rightNode);
targetNode.val = minVal;
}else{//如果删除的节点只有一个子结点
//子结点为左节点时
if(targetNode.leftNode != null){
if(parent != null){
if(parent.leftNode.val == val){
parent.leftNode = targetNode.leftNode;
}else{
parent.rightNode = targetNode.leftNode;
}
}else{
root = targetNode.leftNode;
}
}else{
//子结点为右节点时
if(parent != null){
if(parent.leftNode.val == val){
parent.leftNode = targetNode.rightNode;
}else{
parent.rightNode = targetNode.rightNode;
}
}else{
root = targetNode.rightNode;
}
}
}
}
}
}
测试:
public static void main(String[] args) {
Node node1 = new Node(7);
Node node2 = new Node(3);
Node node3 = new Node(10);
Node node4 = new Node(12);
Node node5 = new Node(5);
Node node6 = new Node(1);
Node node7 = new Node(9);
Node node8 = new Node(2);
Tree tree = new Tree();
tree.add(node1);
tree.add(node2);
tree.add(node3);
tree.add(node4);
tree.add(node5);
tree.add(node6);
tree.add(node7);
tree.add(node8);
tree.infixOrder();
System.out.println();
tree.delNode(1); //2 3 5 7 9 10 12
tree.delNode(12); //2 3 5 7 9 10
tree.delNode(10); //2 3 5 7 9
tree.infixOrder();
}