数据结构--------二叉排序树

二叉排序树

先看一个需求

给你一个数列(7,3, 10, 12,5,1,9),要求能够高效的完成对数据的查询和添加。

方案我们一般首先会想到数组的方式

数组未排序,优点:直接在数组尾添加,速度快。缺点:查找速度慢.
数组排序,优点:可以使用二分查找,查找速度快,缺点:为了保证数组有序在添加新数据时,找到插入位置后,后面的数据需整体移动,速度慢。

链式存储呢?

不管链表是否有序,查找速度都慢,添加数据速度比数组快,不需要数据整体
移动。

我们前面说到树存储可以有效解决,到底是为什么呢?

二叉排序树

介绍

二叉排序树: BST: (Binary Sort(Search) Tree),对于二叉排序树的任何一个非叶子节点,
要求左子节点的值比当前节点的值小右子节点的值比当前节点的值大
特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点

二叉排序树的创建和遍历

package 二叉排序树;

public class BinarySortTree {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		int[] arr= {
    
    7,3,10,12,5,1,9};
		BinarySortTreeDemo binarySortTree = new BinarySortTreeDemo();
		//循环添加节点到二叉树
		for (int i = 0; i < arr.length; i++) {
    
    
			binarySortTree.addNode(new Node(arr[i]));
		}
//		System.out.println("中序遍历二叉树");
		binarySortTree.infixOrder();
	}

}
//创建二叉排序树
class BinarySortTreeDemo{
    
    
	private Node root;
	//添加节点的方法
	public void addNode(Node node){
    
    
		if(root == null){
    
    
			root = node;
		}else{
    
    
			root.addNode(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) {
    
    
		super();
		this.value = value;
	}
	
	@Override
	public String toString() {
    
    
		return "Node [value=" + value + "]";
	}

	//添加节点的方法
	//递归的形式添加节点,需要满足二叉排序树
	public void addNode(Node node){
    
    
		if(node == null){
    
    
			return;
		}
		//判断传入的节点值,跟当前子树根节点值的关系
		if(node.value < this.value){
    
    
			//如果当前节点的左子节点为空
			if(this.left == null){
    
    
				this.left = node;
			}else
			{
    
    
				this.left.addNode(node);//递归添加
			}
		}else{
    
    
			if(this.right == null){
    
    
				this.right = node;
			}else{
    
    
				this.right.addNode(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.删除有两颗子树的节点

思路

情况一:删除叶子节点

1.需要先找到待删除的节点targetNode

2.找到待删除节点的父节点parent(考虑是否有父节点)

3.判断targetNode是parent的左子节点还是右子节点

4.根据前面,对应删除

情况二:删除只有一颗子树的节点

1.需要先找到待删除的节点targetNode

2.找到待删除节点的父节点parent(考虑是否有父节点)

3.确定targetNode的子节点是左子节点还是右子节点

4.确定targetNode是parent的左子节点还是右子节点

5.如果targetNode有左子节点

1)targetNode是parent的左子节点

parent.left = targetNode = left;

2)targetNode是parent的右子节点

parent.left = targetNode .right;

6.如果targetNode有右子节点同理

情况三:删除有两颗子树的节点

1.需要先找到待删除的节点targetNode

2.找到待删除节点的父节点parent(考虑是否有父节点)

3.从targetNode的右子树找到最小的节点

4.用临时变量将最小节点的值保存起来 temp

5.删除最小节点

6.targetNode.value = temp

删除节点代码

package 二叉排序树;

public class BinarySortTree {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		int[] arr= {
    
    7,3,10,12,5,1,9,2};
		BinarySortTreeDemo binarySortTree = new BinarySortTreeDemo();
		//循环添加节点到二叉树
		for (int i = 0; i < arr.length; i++) {
    
    
			binarySortTree.addNode(new Node(arr[i]));
		}
//		System.out.println("中序遍历二叉树");
		binarySortTree.infixOrder();
		
		//测试删除叶子节点
//		binarySortTree.delNode(2);
//		System.out.println("删除2节点后");
//		binarySortTree.infixOrder();
		binarySortTree.delNode(10);
		binarySortTree.infixOrder();

	}

}
//创建二叉排序树
class BinarySortTreeDemo{
    
    
	private Node root;
	//添加节点的方法
	public void addNode(Node node){
    
    
		if(root == null){
    
    
			root = node;
		}else{
    
    
			root.addNode(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);
		}
	}
	//编写方法
	/**
	 * 返回最小节点值,并且删除以node为根节点的二叉排序树的最小节点
	 * @param node	当做一颗二叉排序树的根节点
	 * @return		返回的以node为根节点的二叉排序树的最小节点的值
	 */
	public int delRightTreeMin(Node node){
    
    
		Node target = node;
		//循环查找左子节点,就会找到最小值
		while(target.left != null){
    
    
			target = target.left;
		}
		//这是target就指向了最小节点
		//删除最小节点
		delNode(target.value);
		return target.value;
	}
	
	//删除叶子结点的方法
	public void delNode(int value){
    
    
		if(root == null){
    
    
			return;
		}else{
    
    
			//1.需要先去找到待删除节点
			Node targetNode = search(value);
			//如果没有找到
			if(targetNode == null){
    
    
				return;
			}
			//如果当前这课二叉排序树只有一个节点
			if(root.left == null&& root.right == null){
    
    
				root = null;
				return;
			}
			//去查找targetNode的父节点
			Node parent = searchParent(value);
			//如果待删除的节点是叶子结点
			if(targetNode.left == null && targetNode.right == null){
    
    
				//如果targetNode是parent的左子节点
				if(parent.left != null && parent.left.value == targetNode.value){
    
    
					parent.left = null;
				}else if(parent.right != null && parent.right.value == targetNode.value){
    
    
					parent.right = null;
				}
			}else if(targetNode.left!=null && targetNode.right != null){
    
    
				int minValue = delRightTreeMin(targetNode.right);
				targetNode.value = minValue;
			}else{
    
    
				//删除只有一个子树的节点
				//如果删除的节点有左子节点
				if(targetNode.left != null){
    
    
					if(parent.left.value == targetNode.value){
    
    
						parent.left = targetNode.left;
					}else{
    
    
						parent.right = targetNode.left;
					}
				}else{
    
    
					//要删除的节点有右子节点
					if(parent.left.value == targetNode.value){
    
    
						parent.left = targetNode.right;
					}else{
    
    
						parent.right = targetNode.right;
					}
				}
			}
		}
	}
}
class Node{
    
    
	int value;
	Node left;
	Node right;
	public Node(int value) {
    
    
		super();
		this.value = value;
	}
	
	@Override
	public String toString() {
    
    
		return "Node [value=" + value + "]";
	}

	/**
	 * 查找待删除的节点
	 * @param value	待删除节点的值
	 * @return
	 */
	public Node search(int value){
    
    
		if(value == this.value){
    
    
			return this;
		}else if(value < this.value){
    
    //应该向左子树递归查找
			if(this.left != null){
    
    
				return this.left.search(value);
			}else{
    
    
				return null;
			}
		}else{
    
    
			if(this.right == null){
    
    
				return null;
			}else{
    
    
				return this.right.search(value);
			}
		}
	}
	/**
	 * 查找待删除节点的父节点
	 * @param value		待删除节点的值
	 * @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);
			}else if(value >= this.value && this.right != null){
    
    
				return this.right.searchParent(value);
			}else{
    
    
				return null;//没有找到父节点
			}
		}
	}
	
	
	//添加节点的方法
	//递归的形式添加节点,需要满足二叉排序树
	public void addNode(Node node){
    
    
		if(node == null){
    
    
			return;
		}
		//判断传入的节点值,跟当前子树根节点值的关系
		if(node.value < this.value){
    
    
			//如果当前节点的左子节点为空
			if(this.left == null){
    
    
				this.left = node;
			}else
			{
    
    
				this.left.addNode(node);//递归添加
			}
		}else{
    
    
			if(this.right == null){
    
    
				this.right = node;
			}else{
    
    
				this.right.addNode(node);
			}
		}
	}
	//中序遍历
	public void infixOrder(){
    
    
		if(this.left != null){
    
    
			this.left.infixOrder();
		}
		System.out.println(this);
		if(this.right != null){
    
    
			this.right.infixOrder();
		}
	}
}

注意事项

删除多个节点的时候一定要注意顺序,有的顺序可以正常删除,但是有的顺序会报空指针错误,原因就是删除的顺序有问题,导致我们删除方法中的判断出了问题,就是根节点这个地方,他没有父节点,但是我们判断了,所以在这出错

猜你喜欢

转载自blog.csdn.net/qq_22155255/article/details/113825468