赞
踩
目录
排序二叉树:是一颗二叉树,且所有节点的右孩子比自身大,自身比左孩子大
平衡二叉树:在排序二叉树的定义之下,再加一个条件,即所有节点左子树和右子树高度之差的绝对值不大于1
1)失衡节点作为旋转节点
2)失衡节点的子节点作为中心节点
3)旋转节点绕中心节点左旋,如果中心节点存在左子树,那么该左子树变成旋转节点的右子树
4)再把中心节点挂到原来的父节点上
5)总结:左旋
同RR型相反,右旋,略过
1)先以失衡节点的子节点作为旋转节点,失衡节点的孙子节点作为中心节点,右旋
2)如果中心节点存在右子树,则该右子树变成旋转节点的左子树
3)将中心节点连接到失衡节点的右孩子处
4)再已失衡节点作为旋转节点,以失衡节点的孙子节点作为中心节点,左旋
5)如果中心节点存在左子树,则该左子树变成旋转节点的右子树
6)将中心节点挂到原来的父节点上
7)总结:先右旋,后左旋
同RL型相反,先左旋,后右旋,略过
从根节点向下遍历,找到插入节点的父节点,然后插入
1)插入新节点可能会导致多个节点失衡
2)插入造成的失衡,失衡节点只可能是插入节点的父节点或更上层的祖先节点(爷爷、祖父...)
3)插入操作,只需调整最小失衡子树,调整后整颗树会恢复平衡,证明见6.1
1)从插入节点向上遍历,直到找到第一个失衡节点(最小失衡子树根节点)
2)从失衡节点向插入节点遍历,前两个路径即失衡类型
3)举例
1)待删除节点无子节点——直接删除(节点替换,用null替换删除节点)
2)待删除节点仅有左子树或右子树——直接用左子树或右子树替换删除的节点(节点替换,用直接子节点替换删除节点)
3)待删除节点既有左子树,又有右子树——找右子树中最小的节点minNode,用minNode的值替换删除节点的值(值替换),再用minNode的右子树替换minNode(节点替换)
1)删除节点无子节点
2)删除节点有且仅有左子树(右子树)
3)删除节点既有左子树又有右子树
从失衡节点向左右子树中,较高的子树遍历,得到前两个路径即为失衡类型,举例
话不多说,show you my code
- package com.yqc.tree;
-
- import lombok.Data;
- import java.util.Objects;
-
- /**
- * @author Rickon
- * @date 2023/11/16 18:21
- * @description
- * 1、一个根节点属性root
- * 2、主要方法:
- * 插入、删除、调整、左旋、右旋、节点替换、查找指定值节点、判断节点是否失衡、判断右子树是否高于左子树
- * 前序遍历、中序遍历、后续遍历、层序遍历
- * 树的高度、叶子节点数、所有节点数、第i层节点数
- */
-
- @Data
- public class BSTree {
-
- // 操作类型
- private static final int OPERATION_INSERT = 1;
- private static final int OPERATION_DELETE = 2;
-
- // 根节点属性
- private Node root;
-
- /**
- * 插入
- */
- public void insert(Integer val) {
- Node newNode = new Node(val, null);
-
- // 插入第一个节点
- if (null == this.root) {
- this.root = newNode;
- return;
- }
-
- // 找到待插入节点的父节点
- Node temp = root;
- Node parent = null;
- while (null != temp) {
- parent = temp;
- if (val > temp.val)
- temp = temp.rChild;
- else if (val < temp.val)
- temp = temp.lChild;
- else
- // 待插入节点已存在
- return;
- }
-
- // 插入
- newNode.parent = parent;
- if (val > parent.val)
- parent.rChild = newNode;
- else
- parent.lChild = newNode;
-
- // 调整至平衡
- adjust(newNode, OPERATION_INSERT);
- }
-
- /**
- * 删除
- */
- public void remove(Integer val) {
- // 找到待删除节点
- Node removedNode = get(val);
- if (null == removedNode)
- return;
-
- // 保存节点,用来寻找失衡节点的起始节点
- Node beginNode = null;
-
- // 分情况处理
- if (null == removedNode.lChild && null == removedNode.rChild) {
- // 失衡节点只能是删除节点上层节点(父亲,爷爷,祖父...)
- beginNode = removedNode.parent;
- // 直接删除
- replace(removedNode, null);
- } else if (null != removedNode.lChild && null != removedNode.rChild) {
- // 找右子树中最小的节点minNode(也可找左子树中最大的节点)
- Node minNode = null;
- Node temp = removedNode.rChild;
- while (null != temp) {
- minNode = temp;
- temp = temp.lChild;
- }
- // 失衡节点只能是minNode上层节点(父亲,爷爷,祖父...)
- beginNode = minNode.parent;
- // minNode的右子树替换minNode(右子树提升,相对位置变化,右子树最多只有一个节点,否则minNode就失衡了)
- replace(minNode, minNode.rChild);
- // 待删除节点val替换成minNode的val(节点值互换,相对位置不变)
- removedNode.val = minNode.val;
- } else {
- // 失衡节点只能是删除节点上层节点(父亲,爷爷,祖父...)
- beginNode = removedNode.parent;
- // 保存待删除节点左子树或右子树
- Node child;
- if (null != removedNode.lChild)
- child = removedNode.lChild;
- else
- child = removedNode.rChild;
- // 左子树或右子树替换待删除节点
- replace(removedNode, child);
- }
-
- // 调整
- adjust(beginNode, OPERATION_DELETE);
- }
-
- /**
- * 用target替换source的位置(位置替换,注意区别值替换)
- */
- private void replace(Node source, Node target) {
- if (source == null)
- throw new RuntimeException("被替换节点不能为空");
- Node parent = source.parent;
-
- // 用null替换
- if (target == null) {
- if (source.val > parent.val)
- parent.rChild = null;
- else
- parent.lChild = null;
- source.parent = null;
- source.lChild = null;
- source.rChild = null;
- return;
- }
-
- if (Objects.equals(source.val, target.val))
- throw new RuntimeException("被替换节点与目标几点值相同");
-
- // 用非空节点替换
- if (source.val > parent.val)
- parent.rChild = target;
- else
- parent.lChild = target;
- target.parent = parent;
- source.parent = null;
- source.lChild = null;
- source.rChild = null;
- }
-
- /**
- * 查询指定节点
- */
- private Node get(Integer val) {
- Node temp = root;
- Node removedNode = null;
- while (null != temp) {
- if (val > temp.val)
- temp = temp.rChild;
- else if (val < temp.val)
- temp = temp.lChild;
- else {
- removedNode = temp;
- break;
- }
- }
-
- return removedNode;
- }
-
- /**
- * 插入或删除节点后,重新调整为平衡二叉树
- * @param node 插入操作——插入节点,删除操作——找失衡节点的起始节点
- * @param operationType 操作类型
- */
- private void adjust(Node node, int operationType) {
- // 从插入或起始节点向上遍历,找失衡节点(最小失衡子树根节点)
- Node temp = node;
- Node unbalancedNode = null;
- while (temp != null) {
- if (isUnbalanced(temp)) {
- unbalancedNode = temp;
- break;
- } else {
- temp = temp.parent;
- }
- }
- if (null == unbalancedNode)
- return;
-
- // 判断失衡类型
- StringBuilder sb = new StringBuilder();
- temp = unbalancedNode;
- Node unbalancedChildNode = null;
- Node unbalancedGrandChildNode = null;
- if (OPERATION_INSERT == operationType) {
- // 从失衡节点向插入节点搜索两次,判断失衡类型
- for (int i=0; i<2; ++i) {
- if (node.val > temp.val) {
- sb.append("R");
- temp = temp.rChild;
- } else {
- sb.append("L");
- temp = temp.lChild;
- }
- // 记录【失衡节点儿子节点】和【失衡节点孙子节点】
- if (unbalancedChildNode == null)
- unbalancedChildNode = temp;
- else
- unbalancedGrandChildNode = temp;
- }
- } else {
- // 从失衡节点向更高子树遍历搜索两次,判断失衡类型
- for (int i=0; i<2; ++i) {
- if (isRChildHigher(temp)) {
- sb.append("R");
- temp = temp.rChild;
- } else {
- sb.append("L");
- temp = temp.lChild;
- }
- // 记录【失衡节点儿子节点】和【失衡节点孙子节点】
- if (unbalancedChildNode == null)
- unbalancedChildNode = temp;
- else
- unbalancedGrandChildNode = temp;
- }
- }
- String unbalancedType = sb.toString();
-
- // 根据失衡类型调整
- // 记录失衡节点的父节点,最小失衡子树调整后需要挂在该父节点上,如果为空,说明根节点就是最小失衡子树的根节点
- Node parent = unbalancedNode.parent;
- boolean isRChild = false;
- if (null != parent) {
- isRChild = unbalancedNode.val > parent.val;
- }
- switch (unbalancedType) {
- case "RR":
- // 左旋
- lRotate(unbalancedNode, unbalancedChildNode, parent, isRChild);
- break;
- case "RL":
- // 先右旋,后左旋
- rRotate(unbalancedChildNode, unbalancedGrandChildNode, unbalancedNode, true);
- lRotate(unbalancedNode, unbalancedGrandChildNode, parent, isRChild);
- break;
- case "LL":
- // 右旋
- rRotate(unbalancedNode, unbalancedChildNode, parent, isRChild);
- break;
- case "LR":
- // 先左旋,后右旋
- lRotate(unbalancedChildNode, unbalancedGrandChildNode, unbalancedNode, false);
- rRotate(unbalancedNode, unbalancedGrandChildNode, parent, isRChild);
- break;
- default:
- throw new RuntimeException("失衡类型异常");
- }
- }
-
- /**
- * 判断节点右子树高度是否高于左子树
- */
- private boolean isRChildHigher(Node root) {
- if (root == null) {
- return false;
- }
-
- return height(root.getRChild()) > height(root.getLChild());
- }
-
- /**
- * 左旋
- * @param rotatedNode 旋转节点
- * @param centerNode 中心节点
- * @param parent 旋转后,中心节点的父节点
- * @param isRChild 旋转后,中心节点是否作为parent的右孩子
- */
- private void lRotate(Node rotatedNode, Node centerNode, Node parent, boolean isRChild) {
- // 保存【中心节点】的左孩子
- Node centerNodeLChild = centerNode.lChild;
- // 【旋转节点】变成【中心节点】的左孩子
- centerNode.lChild = rotatedNode;
- rotatedNode.parent = centerNode;
- rotatedNode.rChild = null;
- // 【中心节点】的左孩子变成【旋转节点】的右孩子
- if (centerNodeLChild != null) {
- rotatedNode.rChild = centerNodeLChild;
- centerNodeLChild.parent = rotatedNode;
- }
- // 旋转后重新连接到parent,parent为null,表示调整了整棵树
- if (null == parent)
- root = centerNode;
- else if (isRChild)
- parent.rChild = centerNode;
- else
- parent.lChild = centerNode;
- centerNode.parent = parent;
- }
-
- /**
- * 右旋
- * @param rotatedNode 旋转节点
- * @param centerNode 中心节点
- * @param parent 旋转后,中心节点的父节点
- * @param isRChild 旋转后,中心节点是否作为parent的右孩子
- */
- private void rRotate(Node rotatedNode, Node centerNode, Node parent, boolean isRChild) {
- // 保存【中心节点】的左孩子
- Node centerNodeRChild = centerNode.rChild;
- // 【旋转节点】变成【中心节点】的右孩子
- centerNode.rChild = rotatedNode;
- rotatedNode.parent = centerNode;
- rotatedNode.lChild = null;
- // 【中心节点】的左孩子变成【旋转节点】的右孩子
- if (centerNodeRChild != null) {
- rotatedNode.lChild = centerNodeRChild;
- centerNodeRChild.parent = rotatedNode;
- }
- // 旋转后重新连接到parent,parent为null,表示调整了整棵树
- if (null == parent)
- root = centerNode;
- else if (isRChild)
- parent.rChild = centerNode;
- else
- parent.lChild = centerNode;
- centerNode.parent = parent;
- }
-
- /**
- * 判断节点是否是失衡
- */
- private boolean isUnbalanced(Node root) {
- if (null == root) {
- return false;
- }
-
- return Math.abs(height(root.lChild) - height(root.rChild)) > 1;
- }
-
- /**
- * 前序遍历
- */
- public void preOrder() {
- preOrder(this.root);
- System.out.println();
- }
- private void preOrder(Node root) {
- if (null == root) {
- return;
- }
- System.out.print(root + " ");
- preOrder(root.lChild);
- preOrder(root.rChild);
- }
-
- /**
- * 中序遍历
- */
- public void midOrder() {
- midOrder(this.root);
- System.out.println();
- }
- private void midOrder(Node root) {
- if (null == root) {
- return;
- }
- midOrder(root.lChild);
- System.out.print(root + " ");
- midOrder(root.rChild);
- }
-
- /**
- * 后序遍历
- */
- public void postOrder() {
- postOrder(this.root);
- System.out.println();
- }
- private void postOrder(Node root) {
- if (null == root) {
- return;
- }
- postOrder(root.lChild);
- postOrder(root.rChild);
- System.out.print(root + " ");
- }
-
- /**
- * 层序遍历
- */
- public void levelOrder() {
- if (null == root) {
- return;
- }
-
- // 根节点入队
- Queue queue = new Queue(10);
- queue.push(root);
- // 队列不空
- while (!queue.isEmpty()) {
- // 出队并打印
- Node node = queue.pop();
- System.out.print(node + " ");
- // 左子树入队
- if (null != node.lChild) {
- queue.push(node.lChild);
- }
- // 右子树入队
- if (null != node.rChild) {
- queue.push(node.rChild);
- }
- }
- System.out.println();
- }
-
- /**
- * 第i层的节点数
- */
- public int countOfLevel(int i) {
- return countOfLevel(root, i);
- }
- private int countOfLevel(Node root, int i) {
- if (null == root) {
- return 0;
- }
-
- if (1 == i) {
- return 1;
- }
-
- return countOfLevel(root.lChild, i - 1) + countOfLevel(root.rChild, i - 1);
- }
-
- /**
- * 树的高度
- */
- public int height() {
- return height(root);
- }
- private int height(Node root) {
- if (null == root) {
- return 0;
- }
-
- return Math.max(1 + height(root.lChild), 1 + height(root.rChild));
- }
-
- /**
- * 叶子节点个数
- */
- public int leafCount() {
- return leafCount(root);
- }
- private static int leafCount(Node root) {
- if (null == root) {
- return 0;
- }
-
- if (null == root.lChild && null == root.rChild) {
- return 1;
- }
-
- return leafCount(root.lChild) + leafCount(root.rChild);
- }
-
- /**
- * 所有节点个数
- */
- public int size() {
- return size(root);
- }
- private static int size(Node root) {
- if (null == root) {
- return 0;
- }
-
- return 1 + size(root.lChild) + size(root.rChild);
- }
-
- @Data
- static class Node{
- Integer val;
- Node parent;
- Node lChild;
- Node rChild;
-
- public Node(Integer val, Node parent) {
- this.val = val;
- this.parent = parent;
- }
-
- // 一定要重写toString(),因为父子节点相互持有对方的引用,不重写会导致循环打印,stackOverFlow
- @Override
- public String toString() {
- return val + "";
- }
- }
-
- @Data
- static class Queue {
- Node[] queue;
- int size;
- int head;
- int tail;
- int capacity;
-
- public Queue(int capacity) {
- this.queue = new Node[capacity];
- this.size = 0;
- this.head = 0;
- this.tail = 0;
- this.capacity = capacity;
- }
-
- /**
- * 判断队列是否已满
- */
- public boolean isFull() {
- return this.getSize() >= this.getCapacity();
- }
-
- /**
- * 判断队列是否为空
- */
- public boolean isEmpty() {
- return this.getSize() <= 0;
- }
-
- /**
- * 入队
- */
- public boolean push(Node element) {
- if (isFull()) {
- throw new RuntimeException("队列已满");
- }
-
- queue[tail] = element;
- tail = (tail + 1) % capacity;
- size++;
- return true;
- }
-
- /**
- * 出队
- */
- public Node pop() {
- if (isEmpty()) {
- throw new RuntimeException("队列为空");
- }
-
- Node element = queue[head];
- head = (head + 1) % capacity;
- size--;
- return element;
- }
- }
- }
- package com.yqc.tree;
-
-
- /**
- * @author Rickon
- * @date 2023/11/14 20:34
- * @description
- */
- public class Test {
-
- public static void main(String[] args) {
- BSTree tree = new BSTree();
- tree.insert(10);
- tree.insert(20);
- tree.insert(5);
- tree.insert(37);
- tree.insert(39);
- tree.insert(15);
- Test.print(tree);
- tree.remove(10);
- tree.remove(39);
- tree.remove(37);
- Test.print(tree);
- System.out.println("节点总数:" + tree.size());
- System.out.println("叶子节点数:" + tree.leafCount());
- System.out.println("第2层节点数:" + tree.countOfLevel(2));
- System.out.println("树的高度:" + tree.height());
- }
-
- private static void print(BSTree tree) {
- System.out.print("先序:");tree.preOrder();
- System.out.print("中序:");tree.midOrder();
- System.out.print("后序:");tree.postOrder();
- System.out.print("层序:");tree.levelOrder();
- }
- }
1)在节点插入前,节点20的左子树高度为H+1;插入后,节点20的左子树高度为H+2,导致节点20衡
2)在节点插入前,节点10的右子树高度为H;插入后,节点10的右子树高度由H增高为H+1,导致节点10失衡
3)节点插入后,10和20两个节点失衡,最小失衡子树为虚线绿框所示,根节点即失衡节点10
4)以10为根节点的子树,是20节点的左子树,在新节点插入前,其高度为H+1,插入后,高度为H+2(变为最小失衡子树),在调整平衡后(调整最小失衡子树),高度又变为H+1,所以节点20从平衡变为失衡,又因为最小失衡子树的调整,20节点重新达到平衡
1)删除节点18,如果要使得节点10失衡,那么在删除操作前,节点10的左子树【子树1】的高度必须比右子树高1
2)删除节点18后,节点10的右子树高度减1,导致节点10失衡
3)但是从节点20看,它的左子树高度不变不会因为节点18的删除而改变,仍然保持平衡
4)同理,更高层祖先节点也不会失衡
3、删除既有左子树又有右子树的节点,失衡节点只可能是minNode的父节点
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。