二叉排序树的作用
数组删除和插入的效率很低,链表查找的效率很低,而二叉排序树可以让查找,删除,添加(因为二叉排序树是有序的,所以添加也即是插入)的效率都变的很高。
特点:
- 基于二叉树,但是有一定的规则,每个节点的左子节点总是比它小,右子节点总是比它大
- 中序遍历就是一个升序的序列
- 查找效率近似二分查找
节点类
public class TreeNode {
public int value;
public TreeNode left;
public TreeNode right;
public TreeNode(int value) {
super();
this.value = value;
}
//添加节点
public void add(TreeNode 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 midOrder() {
if(this.left != null) {
this.left.midOrder();
}
System.out.println(this);
if(this.right != null) {
this.right.midOrder();
}
}
//查找目标节点
public TreeNode search(int value) {
if(value == this.value) {
return this;
}else if(value < this.value) {
if(this.left == null) {
return null;
}else {
return this.left.search(value);
}
}else {
if(this.right == null) {
return null;
}else {
return this.right.search(value);
}
}
}
/**
* 查找目标值的父节点
* 实现:判断当前节点的左右子节点是否是要查找的节点,如果是直接返回
* 如果不是,则判断当前节点值与要查找的值的大小关系
* 如果 要查找的值<当前值 判断左子节点是否为空,不为空则往左边查找
* 如果 要查找的值>当前值 判断右子节点是否为空,不为空则往右边查找
*
* @param value 要删除节点的值
* @return 返回目标值的父节点
*/
public TreeNode 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.left != null) {
return this.right.searchParent(value);
}else {
return null;
}
}
}
}
二叉排序树
public class BinarySortTree {
private TreeNode root;
//添加节点
public void add(TreeNode node) {
if(this.root == null) {
this.root = node;
}else {
this.root.add(node);
}
}
//中序遍历
public void midOrder() {
if(this.root == null) {
System.out.println("二叉排序树为空!");
}else {
this.root.midOrder();
}
}
//查找节点
public TreeNode search(int value) {
if(this.root == null) {
System.out.println("二叉排序树为空!");
return null;
}else {
return this.root.search(value);
}
}
//查找目标节点的父节点
public TreeNode searchParent(int value) {
if(this.root == null) {
System.out.println("二叉排序树为空!");
return null;
}else {
return this.root.searchParent(value);
}
}
/**
* 先查找目标节点的父节点,如果找到,则可以同时得到目标节点
* 如果找不到,则判断是否目标节点是否是根节点,如果不是,则没有目标节点,直接返回
* 找到目标节点后:
* 1.判断该节点是否是叶子节点,如果是,则直接通过父节点删除,如果是根节点,则直接把根节点赋值为null
* 2.判断该节点后面是否只有一颗子树,也就是左子节点或者右子节点其中一个为null,则直接把让目标节点的父节点指向目标节点的子节点
* 如果是根节点,则让根节点等于它的左子节点或者右子节点(非空的那个节点)
* 3.如果该节点后面有两颗子树,也就是做左右子节点都不为null,
* 则找到以左子节点为根的子树中最大的数保存起来,删除该节点,然后把值赋值给要目标节点
* 或者找到以右子节点为根的子树中最小的数保存起来,删除该节点,然后把值赋值给要删除的节点 (两者选其一)
*
* @param value 要删除的节点值
*/
public void removeNode(int value) {
if(this.root == null) {
return;
}else {
//找到目标节点的父节点
TreeNode parentNode = searchParent(value);
TreeNode target = null;
if(parentNode == null) {
if(root.value == value) { //查找一下根节点是否是目标节点
target = root;
}else {
return;
}
}else if(parentNode.left != null && parentNode.left.value == value){
target = parentNode.left;
}else if(parentNode.right != null && parentNode.right.value == value){
target = parentNode.right;
}
//如果要删除的节点为叶子节点
if(target.left == null && target.right == null) {
if(parentNode == null) { //根节点
this.root = null;
return;
}
//判断目标节点是父节点的左子节点还是右子节点
if(parentNode.left != null && parentNode.left.value == value) { //左子节点
parentNode.left = null;
return;
}
if(parentNode.right != null && parentNode.right.value == value) { //右子节点
parentNode.right = null;
return;
}
}else if(target.left != null && target.right != null){//如果要删除的节点左右子节点都不为空
//找到左子节点为根的子树中最大的数保存起来,删除该节点,然后把值赋值给要删除的节点
//或者找到右子节点为根的子树中最小的数保存起来,删除该节点,然后把值赋值给要删除的节点
int rightMinValue = removeRightMin(target.right); //我这里删除右子节点为根的子树最小的节点
target.value = rightMinValue;
}else {//如果要删除的节点为左右节点有一个为空
if(target.left != null) { //左子节点不为空,则让它的左子节点代替它的位置
if(parentNode == null) { //根节点
root = target.left;
return ;
}
if(parentNode.left.value == value) {
parentNode.left = target.left;
}else {
parentNode.right = target.left;
}
}else {//右子节点不为空,则让它的右子节点代替它的位置
if(parentNode == null) { //根节点
root = target.right;
return ;
}
if(parentNode.left.value == value) {
parentNode.left = target.right;
}else {
parentNode.right = target.right;
}
}
}
}
}
/**
* 删除二叉排序子树中节点值最小的节点
* @param node 该子树的根节点
* @return 返回删除节点的值
*/
public int removeRightMin(TreeNode node) {
TreeNode temp = node;
while(temp.left != null) {
temp = temp.left;
}
int rightMinValue = temp.value;
removeNode(rightMinValue);
return rightMinValue;
}
}