Data structure-----balanced binary tree

Balanced binary tree

In some cases, our binary sort tree is more like a singly linked list. What is the situation?

If there is a sequence of 1, 2, 3, 4, 5, 6, then his binary sort tree is not so good

Although it does not affect the speed of adding and deleting, it may affect the speed of our query

The problem

The left subtree is all empty, which looks more like a singly linked list in terms of form.

The insertion speed has no effect, the query speed is significantly reduced (because it needs to be compared in order), and the advantages of BST cannot be used, because the left subtree needs to be compared each time, and the query speed is slower than that of a singly linked list.

basic introduction

A balanced binary search tree is also called a self-balancing binary search tree, which is also called an AVL tree, which can ensure high query efficiency .

It has the following characteristics : it is an empty tree or the absolute value of the height difference between its left and right subtrees does not exceed 1 , and the left and right subtrees are both a balanced binary tree. The commonly used implementation methods of balanced binary tree include red-black tree, AVL, scapegoat tree, Treap, stretch tree, etc.

Example: Tell me about the following one is a balanced binary tree

Insert picture description here

The first and the second are balanced binary trees, and the third is not a balanced binary tree

Note : The premise of the binary sort tree is to satisfy the binary sort tree

Create a balanced binary tree

Requirement: Give you a sequence and create a corresponding balanced binary tree sequence {4,3,6,5,7,8}

This example uses the left rotation of video teaching

Ideas:

Insert picture description here

Simple code

package 平衡二叉树;

public class avl {
    
    
	public static void main(String[] args) {
    
    
		int[] arr = {
    
    4,3,6,5,7,8};
		AVLTree avlTree = new AVLTree();
		//添加节点
		for(int i = 0;i<arr.length;i++){
    
    
			avlTree.addNode(new Node(arr[i]));
		}
		//遍历
		System.out.println("中序遍历");
		avlTree.infixOrder();
		System.out.println("在做平衡旋转后");
		System.out.println(avlTree.getRoot().height());//4
		System.out.println("左子树的高度"+avlTree.getRoot().leftHeight());//1
		System.out.println("右子树的高度"+avlTree.getRoot().rightHeight());//3
		//因此需要右子树左旋转
		
	}
	
}

//创建avl树
class AVLTree{
    
    
	private Node root;
	
	public Node getRoot() {
    
    
		return root;
	}
	public void setRoot(Node root) {
    
    
		this.root = 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 + "]";
	}
	//返回当前节点的高度=以根节点为树的高度
	public int height(){
    
    
		return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height())+1;
	}
	//返回左子树的高度
	public int leftHeight(){
    
    
		if(left == null){
    
    
			return 0;
		}
		return left.height();
	}
	//返回右子树的高度
	public int rightHeight(){
    
    
		if(right == null){
    
    
			return 0;
		}
		return right.height();
	}
	//左旋转方法
	private void leftRotate(){
    
    
		//创建新节点,是当前的根节点的值
		Node node = new Node(value);
		//把新的节点的左子树,指向当前节点的左子树
		node.left = left;
		//把新的节点的右子树设置成当前节点的右子树的左子树
		node.right = right.left;
		//把当前节点的值,替换成右子节点的值
		value = right.value;
		//把当前节点的右子树设置成右子树的右子树
		right = right.right;
		//把当前节点的左子树,设置成我们新加的那个节点
		left = node;
	}
	
	/**
	 * 查找待删除的节点
	 * @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);
			}
		}
		//当添加完一个节点后,如果右子树的高度-左子树的高度>1,左旋转
		if(rightHeight() - leftHeight() > 1){
    
    
			leftRotate();//左旋转
		}
	}
	//中序遍历
	public void infixOrder(){
    
    
		if(this.left != null){
    
    
			this.left.infixOrder();
		}
		System.out.println(this);
		if(this.right != null){
    
    
			this.right.infixOrder();
		}
	}
}

Above we thought of the problem as the simplest case, with only the right subtree left-handed,

What if there are other circumstances?

Rotate right

Insert picture description here

In the same way, you only need to write the corresponding right rotation method. I will post the method code here.

	//右旋转
	private void rightRotate(){
    
    
		Node newNode = new Node(value);
		newNode.right = right;
		newNode.left = left.right;
		value = left.value;
		left = left.left;
		right = newNode;
	}

problem

If our array is replaced with 10,11,7,6,8,9, we will find that even if we perform balance processing, it is still not a flat binary tree

Why is this?

Insert picture description here

According to this picture, it is not difficult to see that the right rotation is the rotation of 8,9, but this number is very special, resulting in unbalanced even if it is rotated in the past.

1. When the condition of right rotation is met
2. If the height of the right subtree of its left subtree is greater than the height of its left subtree
3. First rotate the left node of the current node to the left
4. In the current node point right rotation operation can be

Code modification

Mainly when we add nodes, add some logical judgments

	//添加节点的方法
	//递归的形式添加节点,需要满足二叉排序树
	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);
			}
		}
		//当添加完一个节点后,如果右子树的高度-左子树的高度>1,左旋转
		if(rightHeight() - leftHeight() > 1){
    
    
			//如果它的右子树的左子树高度大于它的右子树的高度
			if(right != null && right.leftHeight() > right.rightHeight()){
    
    
				//先对右子树进行右旋转
				right.rightRotate();
				//然后对当前节点左旋转
				leftRotate();//左旋转
			}else{
    
    
				leftRotate();
			}
			return;
		}
		if(leftHeight() - rightHeight() > 1){
    
    
			//如果它的左子树的右子树高度大于它的左子树的高度
			if(left != null && left.rightHeight() > left.leftHeight()){
    
    
				//先对当前节点的左节点(左子树)进行左旋转
				left.leftRotate();
				//在对当前节点进行右旋转
				rightRotate();
			}else{
    
    
				rightRotate();
			}
		}
	}

Guess you like

Origin blog.csdn.net/qq_22155255/article/details/113835918