【数据结构(十·树结构的实际应用)】平衡二叉树(5)

news2024/9/28 20:15:00

文章目录

  • 前言
  • 1. 基本概念
  • 2. 应用案例
    • 2.1. 左旋转(单旋转)
      • 2.1.1. 思路分析
      • 2.1.2. 代码实现
    • 2.2. 右旋转(单旋转)
      • 2.2.1. 思路分析
      • 2.2.2. 代码实现
    • 2.3. 双旋转
      • 2.3.1. 问题情景
      • 2.3.2. 思路分析
      • 2.3.3. 代码实现


前言

二叉排序树可能存在的问题:

看一个案例(说明二叉排序树可能的问题)
    给一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在

在这里插入图片描述

上图中,BST 存在的问题分析:
(1)左子树全部为空,从形式上看,更像一个单链表
(2)插入速度没有影响
(3)查询速度明显降低(因为需要依次比较),不能发挥 BST的优势,因为每次还需要比较左子树,其查询速度比单链表还慢

解决方案:平衡二叉树(AVL)

1. 基本概念

    平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为 AVL 树, 可以保证查询效率较高
具有以下特点:
    ①它是一棵空树或它的左右两个子树的高度差的绝对值不超过 1
    ②并且左右两个子树都是一棵平衡二叉树。
    ③平衡二叉树的常用实现方法有红黑树AVL替罪羊树Treap伸展树等。

举例说明, 看看下面哪些是 AVL 树, 为什么?

在这里插入图片描述

2. 应用案例

2.1. 左旋转(单旋转)

要求:
    给一个数列,创建出对应的平衡二叉树.数列 {4,3,6,5,7,8}

2.1.1. 思路分析

在这里插入图片描述

2.1.2. 代码实现

package avl;

public class AVLTreeDemo {

	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.add(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

	}

}

//创建AVLTree
class AVLTree {
	private Node root;

	public Node getRoot() {
		return root;
	}

	// 查找要删除的节点
	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);
		}
	}

	// 编写方法:
	// 1. 返回的以node为根节点的二叉排序树的最小节点的值
	// 2. 删除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.需要先去找到要删除的结点 targetNode
			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是父节点的左子节点还是右子节点
				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 minVal = delRightTreeMin(targetNode.right);
				targetNode.value = minVal;
			} else {// 删除只有一颗子树的节点
					// 如果要删除的节点有左子节点
				if (targetNode.left != null) {
					if (parent != null) {
						// 如果targetNode是parent的左子节点
						if (parent.left != null && parent.left.value == value) {
							parent.left = targetNode.left;
						} else {// targetNode是parent的右子节点
							parent.right = targetNode.left;
						}
					} else {
						root = targetNode.left;
					}
				} else {// 如果要删除的节点是右子节点
					if (parent != null) {
						// 如果targetNode是parent的左子节点
						if (parent.left.value == value) {
							parent.left = targetNode.right;
						} else {// 如果targetNode是parent的右子节点
							parent.right = targetNode.right;
						}
					} else {
						root = targetNode.left;
					}
				}
			}

		}
	}

	// 添加节点的方法
	public void add(Node node) {
		if (root == null) {
			root = node;// 如果root为空,则直接让root指向node
		} else {
			root.add(node);
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (root != null) {
			root.infixOrder();
		} else {
			System.out.println("二叉排序树为空,不能遍历");
		}
	}
}

//创建Node节点
class Node {
	int value;
	Node left;
	Node right;

	public Node(int value) {
		this.value = value;
	}

	// 返回左子树的高度
	public int leftHeight() {
		if (left == null) {
			return 0;
		}
		return left.height();
	}

	// 返回右子树的高度
	public int rightHeight() {
		if (right == null) {
			return 0;
		}
		return right.height();
	}

	// 返回以该节点为根节点的树的高度
	public int height() {
		return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
	}

	// 左旋转方法
	private void leftRotate() {

		// 创建新的节点,以当前根节点的值
		Node newNode = new Node(value);
		// 把新的节点的左子树设置成当前节点的左子树
		newNode.left = left;
		// 把新的节点右子树设置为当前节点的右子树的左子树
		newNode.right = right.left;
		// 把当前节点的值替换成右子节点的值
		value = right.value;
		// 把当前节点的右子树设置当前节点的右子树的右子树
		right = right.right;
		// 把当前节点的左子树(左子结点)设置成新的节点
		left = newNode;
	}

	// 查找要删除的节点
	/**
	 * 
	 * @param value 希望删除的节点的值
	 * @return 如果找到返回该节点,否则返回null
	 */
	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);
		}

	}

	// 查找要删除节点的父节点
	/**
	 * 
	 * @param value 要找到的节点的值
	 * @return 返回的是要删除的节点的父节点,如果没有就返回null
	 */
	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;// 没有找到父节点
			}
		}
	}

	@Override
	public String toString() {
		return "Node [value=" + value + "]";
	}

	// 添加节点的方法
	// 递归的形式添加节点,注意需要满足二叉树的要求
	public void add(Node node) {
		if (node == null) {
			return;
		}

		// 判断传入的结点的值,和当前子树的根节点的值的关系
		if (node.value < this.value) {
			// 如果当前节点的左节点为null
			if (this.left == null) {
				this.left = node;
			} else {
				// 递归向左子树添加
				this.left.add(node);
			}
		} else {// 添加的节点的值大于当前节点的值
			if (right == null) {
				this.right = node;
			} else {
				// 递归向右子树添加
				this.right.add(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();
		}
	}
}

运行结果:

在这里插入图片描述

2.2. 右旋转(单旋转)

要求:
    给一个数列,创建出对应的平衡二叉树.数列 {10,12, 8, 9, 7, 6}

2.2.1. 思路分析

在这里插入图片描述

2.2.2. 代码实现

package avl;

public class AVLTreeDemo {

	public static void main(String[] args) {
//		int[] arr = { 4, 3, 6, 5, 7, 8 };
		int[] arr = { 10, 12, 8, 9, 7, 6 };
		AVLTree avlTree = new AVLTree();

		// 添加节点
		for (int i = 0; i < arr.length; i++) {
			avlTree.add(new Node(arr[i]));
		}

		// 遍历
		System.out.println("中序遍历");
		avlTree.infixOrder();

		System.out.println("在平衡处理后~~");
		System.out.println("树的高度=" + avlTree.getRoot().height());// 3

		System.out.println("树的左子树高度=" + avlTree.getRoot().leftHeight());// 2
		System.out.println("树的右子树高度=" + avlTree.getRoot().rightHeight());// 2
		System.out.println("当前的根节点=" + avlTree.getRoot());// 8
	}

}

//创建AVLTree
class AVLTree {
	private Node root;

	public Node getRoot() {
		return root;
	}

	// 查找要删除的节点
	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);
		}
	}

	// 编写方法:
	// 1. 返回的以node为根节点的二叉排序树的最小节点的值
	// 2. 删除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.需要先去找到要删除的结点 targetNode
			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是父节点的左子节点还是右子节点
				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 minVal = delRightTreeMin(targetNode.right);
				targetNode.value = minVal;
			} else {// 删除只有一颗子树的节点
					// 如果要删除的节点有左子节点
				if (targetNode.left != null) {
					if (parent != null) {
						// 如果targetNode是parent的左子节点
						if (parent.left != null && parent.left.value == value) {
							parent.left = targetNode.left;
						} else {// targetNode是parent的右子节点
							parent.right = targetNode.left;
						}
					} else {
						root = targetNode.left;
					}
				} else {// 如果要删除的节点是右子节点
					if (parent != null) {
						// 如果targetNode是parent的左子节点
						if (parent.left.value == value) {
							parent.left = targetNode.right;
						} else {// 如果targetNode是parent的右子节点
							parent.right = targetNode.right;
						}
					} else {
						root = targetNode.left;
					}
				}
			}

		}
	}

	// 添加节点的方法
	public void add(Node node) {
		if (root == null) {
			root = node;// 如果root为空,则直接让root指向node
		} else {
			root.add(node);
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (root != null) {
			root.infixOrder();
		} else {
			System.out.println("二叉排序树为空,不能遍历");
		}
	}
}

//创建Node节点
class Node {
	int value;
	Node left;
	Node right;

	public Node(int value) {
		this.value = value;
	}

	// 返回左子树的高度
	public int leftHeight() {
		if (left == null) {
			return 0;
		}
		return left.height();
	}

	// 返回右子树的高度
	public int rightHeight() {
		if (right == null) {
			return 0;
		}
		return right.height();
	}

	// 返回以该节点为根节点的树的高度
	public int height() {
		return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
	}

	// 左旋转方法
	private void leftRotate() {

		// 创建新的节点,以当前根节点的值
		Node newNode = new Node(value);
		// 把新的节点的左子树设置成当前节点的左子树
		newNode.left = left;
		// 把新的节点右子树设置为当前节点的右子树的左子树
		newNode.right = right.left;
		// 把当前节点的值替换成右子节点的值
		value = right.value;
		// 把当前节点的右子树设置当前节点的右子树的右子树
		right = right.right;
		// 把当前节点的左子树(左子结点)设置成新的节点
		left = newNode;
	}

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

		newNode.right = right;

		newNode.left = left.right;
		value = left.value;
		left = left.left;
		right = newNode;
	}

	// 查找要删除的节点
	/**
	 * 
	 * @param value 希望删除的节点的值
	 * @return 如果找到返回该节点,否则返回null
	 */
	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);
		}

	}

	// 查找要删除节点的父节点
	/**
	 * 
	 * @param value 要找到的节点的值
	 * @return 返回的是要删除的节点的父节点,如果没有就返回null
	 */
	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;// 没有找到父节点
			}
		}
	}

	@Override
	public String toString() {
		return "Node [value=" + value + "]";
	}

	// 添加节点的方法
	// 递归的形式添加节点,注意需要满足二叉树的要求
	public void add(Node node) {
		if (node == null) {
			return;
		}

		// 判断传入的结点的值,和当前子树的根节点的值的关系
		if (node.value < this.value) {
			// 如果当前节点的左节点为null
			if (this.left == null) {
				this.left = node;
			} else {
				// 递归向左子树添加
				this.left.add(node);
			}
		} else {// 添加的节点的值大于当前节点的值
			if (right == null) {
				this.right = node;
			} else {
				// 递归向右子树添加
				this.right.add(node);
			}
		}

		// 当添加完一个结点后,如果 右子树的高度 - 左子树的高度 > 1,左旋转
		if (rightHeight() - leftHeight() > 1) {

			leftRotate();// 左旋转
		}

		// 当添加完一个节点后,如果(左子树的高度-右子树的高度) > 1,右旋转
		if (leftHeight() - rightHeight() > 1) {
			rightRotate();
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (this.left != null) {
			this.left.infixOrder();
		}
		System.out.println(this);
		if (this.right != null) {
			this.right.infixOrder();
		}
	}
}

运行结果:

在这里插入图片描述

2.3. 双旋转

2.3.1. 问题情景

某些情况下,只进行双旋转可能会出现问题,像下面的这个例子:只进行右旋转无法得到一个平衡二叉树。
在这里插入图片描述

2.3.2. 思路分析

解决办法:(情况一)
1.当符合右旋转的条件时
2.如果它的左子树根节点的右子树高度大于它的左子树根节点的左子树的高度
3.先对 当前结点(根节点) 的 左子节点(左子树的根节点) 进行左旋转
4.在对当前结点进行右旋转的操作即可
    
图解如下:
    在这里插入图片描述
    
解决办法:(情况二)
1.当符合左旋转的条件时
2.如果它的右子树根节点的左子树高度大于它的右子树根节点的右子树的高度
3.先对 当前结点(根节点) 的 右子节点(右子树的根节点) 进行右旋转
4.在对当前结点进行左旋转的操作即可

2.3.3. 代码实现

package avl;

public class AVLTreeDemo {

	public static void main(String[] args) {
//		int[] arr = { 4, 3, 6, 5, 7, 8 };
//		int[] arr = { 10, 12, 8, 9, 7, 6 };
		int[] arr = { 10, 11, 7, 6, 8, 9 };

		AVLTree avlTree = new AVLTree();

		// 添加节点
		for (int i = 0; i < arr.length; i++) {
			avlTree.add(new Node(arr[i]));
		}

		// 遍历
		System.out.println("中序遍历");
		avlTree.infixOrder();

		System.out.println("在平衡处理后~~");
		System.out.println("树的高度=" + avlTree.getRoot().height());// 3

		System.out.println("树的左子树高度=" + avlTree.getRoot().leftHeight());// 2
		System.out.println("树的右子树高度=" + avlTree.getRoot().rightHeight());// 2
		System.out.println("当前的根节点=" + avlTree.getRoot());// 8

		System.out.println("根节点的左子结点=" + avlTree.getRoot().left);// 7
		System.out.println("根节点的左子结点.左子结点=" + avlTree.getRoot().left.left);// 6
		System.out.println("根节点的右子结点=" + avlTree.getRoot().right);// 10
		System.out.println("根节点的右子结点.左子结点=" + avlTree.getRoot().right.left);// 9
		System.out.println("根节点的右子结点.右子结点=" + avlTree.getRoot().right.right);// 11
	}

}

//创建AVLTree
class AVLTree {
	private Node root;

	public Node getRoot() {
		return root;
	}

	// 查找要删除的节点
	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);
		}
	}

	// 编写方法:
	// 1. 返回的以node为根节点的二叉排序树的最小节点的值
	// 2. 删除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.需要先去找到要删除的结点 targetNode
			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是父节点的左子节点还是右子节点
				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 minVal = delRightTreeMin(targetNode.right);
				targetNode.value = minVal;
			} else {// 删除只有一颗子树的节点
					// 如果要删除的节点有左子节点
				if (targetNode.left != null) {
					if (parent != null) {
						// 如果targetNode是parent的左子节点
						if (parent.left != null && parent.left.value == value) {
							parent.left = targetNode.left;
						} else {// targetNode是parent的右子节点
							parent.right = targetNode.left;
						}
					} else {
						root = targetNode.left;
					}
				} else {// 如果要删除的节点是右子节点
					if (parent != null) {
						// 如果targetNode是parent的左子节点
						if (parent.left.value == value) {
							parent.left = targetNode.right;
						} else {// 如果targetNode是parent的右子节点
							parent.right = targetNode.right;
						}
					} else {
						root = targetNode.left;
					}
				}
			}

		}
	}

	// 添加节点的方法
	public void add(Node node) {
		if (root == null) {
			root = node;// 如果root为空,则直接让root指向node
		} else {
			root.add(node);
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (root != null) {
			root.infixOrder();
		} else {
			System.out.println("二叉排序树为空,不能遍历");
		}
	}
}

//创建Node节点
class Node {
	int value;
	Node left;
	Node right;

	public Node(int value) {
		this.value = value;
	}

	// 返回左子树的高度
	public int leftHeight() {
		if (left == null) {
			return 0;
		}
		return left.height();
	}

	// 返回右子树的高度
	public int rightHeight() {
		if (right == null) {
			return 0;
		}
		return right.height();
	}

	// 返回以该节点为根节点的树的高度
	public int height() {
		return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
	}

	// 左旋转方法
	private void leftRotate() {

		// 创建新的节点,以当前根节点的值
		Node newNode = new Node(value);
		// 把新的节点的左子树设置成当前节点的左子树
		newNode.left = left;
		// 把新的节点右子树设置为当前节点的右子树的左子树
		newNode.right = right.left;
		// 把当前节点的值替换成右子节点的值
		value = right.value;
		// 把当前节点的右子树设置当前节点的右子树的右子树
		right = right.right;
		// 把当前节点的左子树(左子结点)设置成新的节点
		left = newNode;
	}

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

		newNode.right = right;

		newNode.left = left.right;
		value = left.value;
		left = left.left;
		right = newNode;
	}

	// 查找要删除的节点
	/**
	 * 
	 * @param value 希望删除的节点的值
	 * @return 如果找到返回该节点,否则返回null
	 */
	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);
		}

	}

	// 查找要删除节点的父节点
	/**
	 * 
	 * @param value 要找到的节点的值
	 * @return 返回的是要删除的节点的父节点,如果没有就返回null
	 */
	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;// 没有找到父节点
			}
		}
	}

	@Override
	public String toString() {
		return "Node [value=" + value + "]";
	}

	// 添加节点的方法
	// 递归的形式添加节点,注意需要满足二叉树的要求
	public void add(Node node) {
		if (node == null) {
			return;
		}

		// 判断传入的结点的值,和当前子树的根节点的值的关系
		if (node.value < this.value) {
			// 如果当前节点的左节点为null
			if (this.left == null) {
				this.left = node;
			} else {
				// 递归向左子树添加
				this.left.add(node);
			}
		} else {// 添加的节点的值大于当前节点的值
			if (right == null) {
				this.right = node;
			} else {
				// 递归向右子树添加
				this.right.add(node);
			}
		}

		// 当添加完一个结点后,如果 右子树的高度 - 左子树的高度 > 1,左旋转
		if (rightHeight() - leftHeight() > 1) {
			// 如果它的右子树根节点的左子树高度大于它的右子树根节点的右子树的高度
			if (right != null && right.leftHeight() > right.rightHeight()) {
				// 先对 当前结点(根节点) 的 右子节点(右子树的根节点) 进行右旋转
				right.rightRotate();
				// 在对当前结点进行左旋转的操作即可
				leftRotate();// 左旋转
			} else {
				// 直接进行左旋转即可
				leftRotate();
			}
			return;// 必须要!!!
		}

		// 当添加完一个节点后,如果(左子树的高度-右子树的高度) > 1,右旋转
		if (leftHeight() - rightHeight() > 1) {
			// 如果它的左子树根节点的右子树高度大于它的左子树根节点的左子树的高度
			if (left != null && left.rightHeight() > left.leftHeight()) {
				// 先对 当前结点(根节点) 的 左节点(左子树的根节点) 进行左旋转
				left.leftRotate();
				// 在对当前结点进行右旋转的操作
				rightRotate();
			} else {
				// 直接进行右旋转即可
				rightRotate();
			}
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (this.left != null) {
			this.left.infixOrder();
		}
		System.out.println(this);
		if (this.right != null) {
			this.right.infixOrder();
		}
	}
}

运行结果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1309650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Amazon Connect ,全渠道云联络中心

Amazon Connect &#xff0c;全渠道云联络中心 前言一. Amazon Connect 介绍 &#x1f5fa;️二. Amazon Connect 使用教程 &#x1f5fa;️1.我们打开URl链接找到对应服务2.输入Amazon Connect选中第一个点击进入即可&#xff1b;3.在进入之后我们就开始创建实例&#xff0c;点…

【方法】如何给Word文档添加水印?

想要保护文档内容的原创性&#xff0c;我们可以给文档添加上水印来做个防伪标记&#xff0c;防范文件内容被盗用。 有些小伙伴还不知道Word在哪里设置添加水印&#xff0c;接下来分享一个添加水印的教程&#xff0c;一起学习一下吧。 1. 打开需要添加水印的Word文档&#xff…

【源码复现】《Towards Deeper Graph Neural Networks》

目录 1、论文简介2、论文核心介绍2.1、基本概述2.2、模型介绍 3、源码复现3.1、torch复现3.2、DGL复现 1、论文简介 论文题目——《Towards Deeper Graph Neural Networks》论文作者——Meng Liu, Hongyang Gao & Shuiwang Ji论文地址——Towards Deeper Graph Neural Net…

如何给pycharm配置解释器

目录 给pycharm配置解释器 给pycharm配置解释器 选择show all 选择已经存在的环境 其中的py38是我通过conda创建的虚拟环境。

BearPi Std 板从入门到放弃 - 筑基(1)(RT-Thread 智慧路灯)

简介 使用BearPi IOT Std开发板及其扩展板E53_SC1&#xff0c; SC1上有I2C1 的光照强度传感器BH1750 和 EEPROM AT24C02&#xff0c; 智慧路灯功能, 实现亮度达到对应阈值则开启点灯 主板: 主芯片: STM32L431RCT6LED : PC13 \ 推挽输出\ 高电平点亮串口: Usart1I2C使用 : I2C…

排序算法(二)-冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序

排序算法(二) 前面介绍了排序算法的时间复杂度和空间复杂数据结构与算法—排序算法&#xff08;一&#xff09;时间复杂度和空间复杂度介绍-CSDN博客&#xff0c;这次介绍各种排序算法——冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序。 文章目录 排序算法(二)…

transformer文章翻译【Attention is all you need】

参考博客&#xff1a;https://blog.csdn.net/nocml/article/details/103082600 论文目录 摘要1. Introduction2. Background3. Model Architecture3.1 Encoder and Decoder Stacks3.2 Attention3.2.1 Scaled Dot-Product Attention3.2.2 Multi-Head Attention3.2.3 Application…

实验室移液器配套PFA吸头性能稳定特氟龙吸头特点

PFA吸头是一种高性能移液器配件&#xff0c;由聚全氟丙烯&#xff08;Perfluoroalkoxy&#xff09;材料制成。这种材料具有优异的耐化学品、耐热和电绝缘性能&#xff0c;使得PFA吸头在应用中表现出色。 首先&#xff0c;PFA吸头具有卓越的耐化学腐蚀性能。无论是酸性溶液、碱性…

环境保护:人类生存的最后机会

随着科技的进步和人类文明的不断发展&#xff0c;地球上的自然资源也在以惊人的速度消耗殆尽。人类对于环境的无止境的掠夺&#xff0c;使得我们的地球正面临着前所未有的环境危机。环境污染、全球变暖、大规模灭绝等问题不断困扰着我们&#xff0c;似乎指向了人类生存的最后机…

世微AP5414 锂电池升降压 恒流恒压 LED电源驱动IC

产品简介 AP5414 是一种输入电压范围宽&#xff08;0.8~5.5V&#xff09;&#xff0c;可调恒定电流和限定电流两种模式来 驱动白光 LED 而设计的升压型 DC/DC 变换器。该器件能利用单节或双节干电池驱动单 颗大功率白光 LED&#xff0c;同样可以利用一节锂电池驱动两颗、三颗或…

docker gpu 详细部署 video-retalking(跟着步骤打指令就完事了)

本地操作系统&#xff1a;centos 搞个小塔 1、首先在centos下安装宝塔&#xff08;后面会用到&#xff09; 建个容器 2、根据自己要的python版本修改一下语句&#xff0c;这里拉取的是python3.8.8的镜像 docker run -itd --name video-retalking --gpus all --shm-size&qu…

医院污水处理设备远程监控超标报警解决方案

行业背景 近年来&#xff0c;我国医疗机构建设得到了巨大的发展。根据《2022年我国卫生健康事业发展统计公报》&#xff0c;2022年末&#xff0c;全国医疗卫生机构总数达1032918个。截至2022年10月&#xff0c;根据全国排污许可证管理信息平台&#xff0c;共有 13316家医院核发…

ElasticSearch - networking配置global

版本8.11 单机部署了一个节点 在elasticsearch.yml中 配置了network.host: 8.8.8.8(之前为127.0.0.1) 但启动服务失败 报错信息为: BindTransportException: Failed to bind to 8.8.8.8:[9300-9399] 为啥要配置8.8.8.8 是因为参考的官方说明 Networking | Elasticsearch Gu…

RHEL8_Linux下载ansible

本章内容主要介绍RHEL8中如何安装ansible ansible时如何工作的在RHEL8中安装ansible 1.ansible工作原理 如果管理的服务器很多&#xff0c;如几十台甚至几百台&#xff0c;那么就需要一个自动化管理工具了&#xff0c;ansible就是这样的一种自动化管理工具。 1&…

AWS Ubuntu设置DNS解析(解决resolve.conf被覆盖问题)

众所周知&#xff1a; Ubuntu在域名解析时&#xff0c;最直接使用的是/etc/resolve.conf文件&#xff0c;它是/run/systemd/resolve/resolve.conf的软链接&#xff0c;而对于刚装完的ubuntu系统&#xff0c;该文件的内容如下 ubuntuip-172-31-36-184:/etc$ cat resolv.conf #…

Mysql workbench

下载地址: https://download.csdn.net/download/a876106354/88616595

EasyExcel实现⭐️本地excel数据解析并保存到数据库的脚本编写,附案例实现

目录 前言 一、 EasyExcel 简介 二、实战分析 1.Controller控制层 2. service方法和方法实现 3.EasyExcel相关类 3.1 excel表实体类 3.2 自定义监听器类 4.测试 4.1 准备工作 4.2 断点调试 5.生成脚本文件 三、分析总结 章末 小伙伴们大家好&#xff0c;最近开发的时…

spring 笔记三 Spring与Web环境集成

文章目录 Spring与Web环境集成ApplicationContext应用上下文获取方式导入Spring集成web的坐标置ContextLoaderListener监听器通过工具获得应用上下文对象SpringMVC概述SpringMVC快速入门 Spring与Web环境集成 ApplicationContext应用上下文获取方式 应用上下文对象是通过new …

QT-CAD-3D显示操作工具

QT-CAD-3D显示操作工具 一、效果展示二、核心程序三、程序链接 一、效果展示 二、核心程序 TDF_LabelSequence DxfReader::transfer(DocumentPtr doc, TaskProgress* progress) {TDF_LabelSequence seqLabel;Handle_XCAFDoc_ShapeTool shapeTool doc->xcaf().shapeTool();…

ProcessOn在线绘制部分项目流程图

目录 一、ProcessOn 1.1 简介 1.2 官方网站 二、Axure自定义元件库 2.1 新建元件库 2.2 自定义元件 2.3 添加元件库 三、HIS系统门诊流程图 四、HIS系统住院流程图 五、HIS系统药品采购入库流程图 六、OA会议流程图 一、ProcessOn 1.1 简介 ProcessOn是一款在线的流…