• 树(二叉查找树BST、二叉平衡树AVL、红黑树R-B)


    目录

    一、二叉树

    二、二叉排序树(BST)

    性质:

    插入规则:

    删除规则:

    二叉查找树代码实现:

    三、完全二叉树

    判断一棵树是否是完全二叉树的思路

    层次遍历代码(Java)---leetCode102:

    四、平衡二叉树(AVL)

    性质:

    旋转:

    概念:

    插入步骤:

    删除规则:

    JAVA实现平衡二叉树的插入、删除等等操作以及测试代码

    五、红黑树(R-B Tree)

    性质:

    旋转:

    红黑树的插入步骤:

     红黑树插入实现(Java)


    一、二叉树

    每个节点最多有两个叶子节点的树是二叉树。

    二、二叉排序树(BST)

    也叫二叉查找树二叉搜索树

    性质:

    1. 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
    2. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
    3. 它的左、右子树也分别为二叉排序树

    插入规则:

    比父节点小的值放左边,比父节点大的值,放右边,一个父节点最多只能有两个子节点

    删除规则:

    1. 删除叶子节点,直接删除
    2. 删除只有左子树或者是有右子树的节点,删除节点,左子树或者右子树直接接上
    3. 删除有左子树和右子树的节点P,找到删除节点的直接后继节点H,节点H替换节点P,删除原来的P节点

    (PS:直接后继节点:中序遍历,在删除节点P后一位的节点为直接后继节点)

    二叉查找树代码实现:

    1. /**
    2. * @author Admin
    3. */
    4. public class BSTTree {
    5. /**
    6. * 定义二叉树
    7. */
    8. class Node{
    9. public int iData;
    10. public double dData;
    11. public Node leftNode;
    12. public Node rightNode;
    13. public void showNode(){
    14. System.out.println("{"+iData+","+dData+"}");
    15. }
    16. }
    17. private Node root;
    18. /**
    19. * 插入节点
    20. * 规则:比父节点小的值放左边,比父节点大的值,放右边,一个父节点最多只能有两个子节点
    21. * @param iData
    22. * @param dData
    23. */
    24. public void insert(int iData,double dData){
    25. Node newNode = new Node();
    26. newNode.iData = iData;
    27. newNode.dData = dData;
    28. if(root==null){
    29. root = newNode;
    30. }else{
    31. Node current = root;
    32. Node parent;
    33. while (true){
    34. parent = current;
    35. //插入左节点
    36. if(iData< current.iData){
    37. current=current.leftNode;
    38. if(current==null){
    39. parent.leftNode=newNode;
    40. return;
    41. }
    42. }else{//插入右节点
    43. current=current.rightNode;
    44. if(current==null){
    45. parent.rightNode = newNode;
    46. return;
    47. }
    48. }
    49. }
    50. }
    51. }
    52. /**
    53. * 查找节点
    54. * @param key
    55. * @return
    56. */
    57. public Node find(int key){
    58. Node current = root;
    59. if(current==null){
    60. return new Node();
    61. }
    62. while(current.iData!=key){
    63. if(current.iData>key){
    64. current=current.leftNode;
    65. }else{
    66. current=current.rightNode;
    67. }
    68. if(current==null){
    69. return new Node();
    70. }
    71. }
    72. return current;
    73. }
    74. /**
    75. * 规则:
    76. * 1、删除叶子节点,直接删除
    77. * 2、删除只有左子树或者是有右子树的节点,删除节点,左子树或者右子树直接接上
    78. * 3、删除有左子树和右子树的节点P,找到删除节点的直接后继节点H,节点H替换节点P,删除原来的P节点
    79. * 直接后继节点:中序遍历,在节点P后一位的节点为直接后继节点
    80. * @param key
    81. */
    82. public void delete(int key){
    83. //找到需要删除的节点
    84. Node current = find(key);
    85. Node parent = current==null?null:current.parentNode;
    86. if(current!=null){
    87. //删除节点的左子树不为空
    88. if(current.leftNode != null && current.rightNode != null){
    89. //查询待删除节点的直接后继节点
    90. Node successorNode = getSuccessorNode(current);
    91. //保存节点数据,便于后面的替换
    92. int iData = successorNode.iData;
    93. double dData = successorNode.dData;
    94. //删除该后继节点
    95. delete(successorNode.iData);
    96. //替换节点
    97. current.iData = iData;
    98. current.dData = dData;
    99. }else if(current.leftNode != null || current.rightNode != null){
    100. //删除含有左子树或者右子树的节点,将删除节点的父节点直接连接删除节点的左子树或者右子树
    101. if(current.leftNode!=null){
    102. current.leftNode.parentNode = parent;
    103. }else{
    104. current.rightNode.parentNode = parent;
    105. }
    106. if(parent!=null){
    107. if(parent.iData>current.iData){
    108. parent.leftNode = current.leftNode != null?current.leftNode:current.rightNode;
    109. }else{
    110. parent.rightNode = current.leftNode != null?current.leftNode:current.rightNode;
    111. }
    112. }else{
    113. //删除节点为根节点,直接将根节点替换为下一个节点
    114. if(current.leftNode!=null){
    115. root = current.leftNode;
    116. }else{
    117. root = current.rightNode;
    118. }
    119. }
    120. }else{
    121. //删除叶子节点
    122. if(parent==null){
    123. //根节点,直接将树置空
    124. root = null;
    125. }else{
    126. //删除叶子节点,直接置空
    127. if(parent.iData>current.iData){
    128. parent.leftNode = null;
    129. }else{
    130. parent.rightNode = null;
    131. }
    132. }
    133. }
    134. }
    135. }
    136. /**
    137. * 获取节点的直接后继节点
    138. * @param node
    139. * @return
    140. */
    141. public Node getSuccessorNode(Node node){
    142. Node current = node==null?null:node.rightNode;
    143. if(current==null){
    144. return null;
    145. }
    146. while(current.leftNode!=null){
    147. current = current.leftNode;
    148. }
    149. return current;
    150. }
    151. /**
    152. * 查找树最小值和最大值
    153. * @return
    154. */
    155. public Node[] mVal(){
    156. Node current = root;
    157. Node maxCurrent = current;
    158. Node minCurrent = current;
    159. Node[] minAndMaxVal = new Node[2];
    160. while(minCurrent.leftNode!=null){
    161. minCurrent = minCurrent.leftNode;
    162. }
    163. minAndMaxVal[0]=minCurrent;
    164. while(maxCurrent.rightNode!=null){
    165. maxCurrent = maxCurrent.rightNode;
    166. }
    167. minAndMaxVal[1]=maxCurrent;
    168. return minAndMaxVal;
    169. }
    170. }

    测试二叉查找树代码

    1. public static void main(String[] args) {
    2. // write your code here
    3. BSTTree treeTest = new TreeTest();
    4. treeTest.insert(3,3.03);
    5. treeTest.insert(5,5.05);
    6. treeTest.insert(1,1.01);
    7. treeTest.insert(2,2.02);
    8. treeTest.insert(4,4.04);
    9. treeTest.insert(6,6.06);
    10. //查找节点3
    11. BSTTree.Node node = treeTest.find(5);
    12. if(node == null){
    13. System.out.println("can not find it");
    14. }else{
    15. node.showNode();
    16. }
    17. BSTTree.Node[] temp = treeTest.mVal();
    18. temp[0].showNode();
    19. temp[1].showNode();
    20. //删除节点3
    21. treeTest.delete(5);
    22. //再次查找
    23. BSTTree.Node node1 = treeTest.find(5);
    24. if(node1 == null){
    25. System.out.println("can not find it");
    26. }else{
    27. node1.showNode();
    28. }
    29. }

    结果:

    三、完全二叉树

    叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。

    性质:

    1、具有n个结点的完全二叉树的深度[log2n]+1

    2、如果对一棵有n个结点的完全二叉树的结点按层序编号, 则对任一结点i (1≤i≤n) 有:

    • 如果i=1, 则结点i是二叉树的根, 无双亲;如果i>1, 则其双亲parent (i) 是结点[i/2].
    • 如果2i>n, 则结点i无左孩子, 否则其左孩子lchild (i) 是结点2i;
    • 如果2i+1>n, 则结点i无右孩子, 否则其右孩子rchild (i) 是结点2i+1.

    判断一棵树是否是完全二叉树的思路

    1、如果树为空,则直接返回错

    2、如果树不为空:层序遍历二叉树

    2.1、如果一个结点左右孩子都不为空,则pop该节点,将其左右孩子入队列;

    2.1、如果遇到一个结点,左孩子为空,右孩子不为空,则该树一定不是完全二叉树;

    2.2、如果遇到一个结点,左孩子不为空,右孩子为空;或者左右孩子都为空,且则该节点之后的队列中的结点都为叶子节点,该树才是完全二叉树,否则就不是完全二叉树;

    层次遍历代码(Java)---leetCode102:

    1. static class TreeNode{
    2. int val;
    3. TreeNode left;
    4. TreeNode right;
    5. TreeNode(){}
    6. TreeNode(int val){
    7. this.val = val;
    8. }
    9. TreeNode(int val,TreeNode left,TreeNode right){
    10. this.val = val;
    11. this.left = left;
    12. this.right = right;
    13. }
    14. public void showNode(){
    15. System.out.println("{"+val+":"+((left==null)?null:left.val)+":"+((right==null)?null:right.val)+"}");
    16. }
    17. }
    18. public static void main(String[] args) {
    19. TreeNode tree1 = new TreeNode(9);
    20. TreeNode tree2 = new TreeNode(15);
    21. TreeNode tree3 = new TreeNode(7);
    22. TreeNode tree4 = new TreeNode(20,tree2,tree3);
    23. TreeNode tree5 = new TreeNode(3,tree1,tree4);
    24. List> res = new ArrayList<>();
    25. Queue queue = new LinkedList<>();
    26. queue.add(tree5);
    27. while (!queue.isEmpty()){
    28. int count = queue.size();
    29. List list = new ArrayList<>();
    30. while (count > 0) {
    31. TreeNode node = queue.poll();
    32. list.add(node==null?null:node.val);
    33. if(node!=null && node.left!=null){
    34. queue.add(node.left);
    35. }
    36. if(node!=null && node.right!=null){
    37. queue.add(node.right);
    38. }
    39. count--;
    40. }
    41. res.add(list);
    42. }
    43. for (List re : res) {
    44. System.out.println(re);
    45. }
    46. }

    四、平衡二叉树(AVL)

    性质:

    1、是二叉排序树

    2、每个节点的左子树和右子树的高度之差至多等于1,大于1则失衡,需要旋转纠正

    旋转:

    1、左旋

    • 根节点为根节点的左子树
    • 根节点的左子树根节点的右子树

    2、右旋

    • 根节点为根节点的右子树
    • 根节点的右子树根节点的左子树

    概念:

    平衡因子(BF):节点的左子树高度减右子树高度

    最小不平衡子树:往平衡二叉树中插入新的节点,从插入点由下往上,依次遍历插入点的各个祖先节点,记录第一个遍历到的平衡因子的绝对值 |BF| >1的祖先节点,以该节点为根节点的子树,即为这棵树的最小不平衡子树。

    4 种「旋转」纠正类型(只需纠正最小不平衡子树即可,最小不平衡子树是距离插入节点最近的,并且BF的绝对值大于1的节点为根节点的子树):

    1. LL 型(关注BF=2,BF=1):插入左孩子的左子树,右旋(BF为平衡因子,即左子树高度减右子树高度
    2. RR 型(关注BF=-2,BF=-1):插入右孩子的右子树,左旋
    3. LR 型(关注BF=2,BF=-1):插入左孩子的右子树,先左旋,再右旋
    4. RL 型(关注BF=-2,BF=1):插入右孩子的左子树,先右旋,再左旋

    下图中,单个节点第一个值为节点数据,第二个是平衡因子BF

    第一种:右旋,2为新根节点,3为2的右子树

    第二种:先左旋,变为LL型(如下图),再右旋,2为新根节点,3为2的右子树

    第三种:左旋,2为新根节点,1为2的左子树

    第四种:先右旋,变成RR型(如下图),再左旋,2为新根节点,1为2的左子树

    插入步骤:

    1. 生成二叉查找树
    2. 计算平衡因子,判断是否需要调整,以及如何调整(需要调整的情况就是上面列的四种类型)

    删除规则:

    1. 删除节点为叶子节点时,更新该节点的父节点高度,以及往上所有父节点高度;
    2. 删除节点只有左子树或者右子树时,该删除节点下的左子树或右子树接上,并且更新原删除节点的父节点高度;
    3. 删除节点有左子树和右子树时,将该删除节点替换为删除节点的直接后继节点,并且回调本方法删除该后继节点;
    4. 删除后,需要从根节点往下,依次计算平衡因子,判断是否失衡,并调整

    例题:{3,2,1,4,5,6,7,10,9,8}构造平衡二叉树

    (以下只列了最难的一步转换,图中的节点,第一个数为保存的数据,第二个数为平衡因子):

    节点8插入过程

     失衡,找到最小不平衡子树{6,5,9,7,10,8},RL型(先右旋,再左旋),节点{6,9,7}右旋,7的右子树变为9的左子树如下图

    节点{6,7,9}左旋,节点6变为节点7的左子树,得到结果

     d43154d33ee3348ad51ea8ca72eda399.png

    JAVA实现平衡二叉树的插入、删除等等操作以及测试代码

    1. package com.examply;
    2. import java.util.*;
    3. /**
    4. * @author :叙
    5. */
    6. public class AVLtree {
    7. static class Tree {
    8. int height;
    9. Tree leftTree;
    10. Tree rightTree;
    11. Tree parent;
    12. int data;
    13. //无参构造方法
    14. Tree() {
    15. }
    16. Tree(int data) {
    17. this.data = data;
    18. //默认高度为1
    19. this.height = 1;
    20. }
    21. }
    22. /**
    23. * 计算平衡因子
    24. *
    25. * @param tree
    26. * @return
    27. */
    28. public int countBF(Tree tree) {
    29. if (tree == null || tree.leftTree == null && tree.rightTree == null) {
    30. return 0;
    31. }
    32. if (tree.leftTree == null) {
    33. return -tree.rightTree.height;
    34. } else if (tree.rightTree == null) {
    35. return tree.leftTree.height;
    36. } else {
    37. return tree.leftTree.height - tree.rightTree.height;
    38. }
    39. }
    40. /**
    41. * 计算高度
    42. *
    43. * @param tree
    44. */
    45. public void countHeight(Tree tree) {
    46. if (tree != null) {
    47. if (tree.leftTree != null || tree.rightTree != null) {
    48. //左右子树最高的高度,加一为当前节点高度
    49. tree.height = Math.max(tree.rightTree == null ? 0 : tree.rightTree.height, tree.leftTree == null ? 0 : tree.leftTree.height) + 1;
    50. } else {
    51. tree.height = 1;
    52. }
    53. }
    54. }
    55. /**
    56. * 右旋
    57. * ● 旧根节点为新根节点的右子树
    58. * ● 新根节点的右子树为旧根节点的左子树
    59. *
    60. * @param tree
    61. */
    62. public void rightRotate(Tree tree) {
    63. Tree oldTree = tree;
    64. Tree newTree = tree.leftTree;
    65. Tree parent = tree.parent;
    66. if (parent != null) {
    67. //确定旧根节点在父类的位置,放入新根节点
    68. if (oldTree.parent.data > oldTree.data) {
    69. parent.leftTree = newTree;
    70. } else {
    71. parent.rightTree = newTree;
    72. }
    73. }
    74. //修改新根节点的父节点为旧根节点的父节点
    75. newTree.parent = parent;
    76. //新根节点的右子树为旧根节点的左子树
    77. oldTree.leftTree = newTree.rightTree;
    78. if (newTree.rightTree!=null){
    79. //新根节点的右子树的父节点为旧根节点
    80. newTree.rightTree.parent = oldTree;
    81. }
    82. //新根节点的右子树为旧根节点
    83. newTree.rightTree = oldTree;
    84. //修改旧根节点的父节点为新根节点
    85. oldTree.parent = newTree;
    86. //修改高度
    87. countHeight(oldTree);
    88. countHeight(newTree);
    89. }
    90. /**
    91. * 左旋
    92. * ● 旧根节点为新根节点的左子树
    93. * ● 新根节点的左子树为旧根节点的右子树
    94. *
    95. * @param tree
    96. */
    97. public void leftRotate(Tree tree) {
    98. Tree oldTree = tree;
    99. Tree newTree = tree.rightTree;
    100. Tree parent = tree.parent;
    101. if (parent != null) {
    102. //确定旧根节点在父类的位置,放入新根节点
    103. if (oldTree.parent.data > oldTree.data) {
    104. parent.leftTree = newTree;
    105. } else {
    106. parent.rightTree = newTree;
    107. }
    108. }
    109. //修改新根节点的父节点为旧根节点的父节点
    110. newTree.parent = parent;
    111. //新根节点的左子树为旧根节点的右子树
    112. oldTree.rightTree = newTree.leftTree;
    113. if(newTree.leftTree!=null){
    114. //新根节点的左子树的父节点为旧根节点
    115. newTree.leftTree.parent = oldTree;
    116. }
    117. //修改新根节点的右子树的父节点为旧根节点
    118. newTree.leftTree = oldTree;
    119. //修改旧根节点的父节点为新根节点
    120. oldTree.parent = newTree;
    121. //修改高度
    122. countHeight(oldTree);
    123. countHeight(newTree);
    124. }
    125. private Tree root;
    126. /**
    127. * 插入节点(递归)
    128. * @param root
    129. * @param data
    130. */
    131. public void insert(Tree root, int data) {
    132. //小于根节点,则插入到左边
    133. if (data < root.data) {
    134. if (root.leftTree != null) {
    135. insert(root.leftTree, data);
    136. } else {
    137. root.leftTree = new Tree(data);
    138. root.leftTree.parent = root;
    139. }
    140. } else {
    141. //大于根节点,则插入到右边
    142. if (root.rightTree != null) {
    143. insert(root.rightTree, data);
    144. } else {
    145. root.rightTree = new Tree(data);
    146. root.rightTree.parent = root;
    147. }
    148. }
    149. //左子树高则右旋
    150. if(countBF(root) == 2){
    151. //左孩子节点的右子树高则先左旋
    152. if(countBF(root.leftTree) == -1){
    153. leftRotate(root.leftTree);
    154. }
    155. rightRotate(root);
    156. }
    157. //右子树高则左旋
    158. if(countBF(root) == -2){
    159. //右孩子节点的左子树高则先右旋
    160. if(countBF(root.rightTree) == 1){
    161. rightRotate(root.rightTree);
    162. }
    163. leftRotate(root);
    164. }
    165. //调整之后重新计算节点的高度
    166. countHeight(root);
    167. }
    168. /**
    169. * 插入
    170. * @param data
    171. */
    172. public Tree insert(int data){
    173. //初始数据
    174. if(root ==null){
    175. root = new Tree(data);
    176. }else{
    177. //刷新root,使root始终为根节点
    178. while(root.parent!=null){
    179. root = root.parent;
    180. }
    181. insert(root,data);
    182. }
    183. return root;
    184. }
    185. /**
    186. * 层次遍历树
    187. * @param tree
    188. */
    189. public void showTree(Tree tree){
    190. List> res = new ArrayList<>();
    191. Queue queue = new LinkedList<>();
    192. queue.add(tree);
    193. while (!queue.isEmpty()){
    194. int count = queue.size();
    195. List list = new ArrayList<>();
    196. while (count > 0) {
    197. Tree node = queue.poll();
    198. list.add("{当前节点:"+node.data+",父节点:"+(node.parent==null?null:node.parent.data)+"}");
    199. if(node!=null && node.leftTree!=null){
    200. queue.add(node.leftTree);
    201. }
    202. if(node!=null && node.rightTree!=null){
    203. queue.add(node.rightTree);
    204. }
    205. count--;
    206. }
    207. res.add(list);
    208. }
    209. for (List re : res) {
    210. System.out.println(re);
    211. }
    212. }
    213. /**
    214. * 删除
    215. * 1、删除节点为叶子节点时,更新该节点的父节点高度,以及往上所有父节点高度;
    216. * 2、删除节点只有左子树或者右子树时,该删除节点下的左子树或右子树接上,并且更新原删除节点的父节点高度;
    217. * 3、删除节点有左子树和右子树时,将该删除节点替换为删除节点的直接后继节点,并且回调本方法删除该后继节点;
    218. * 4、删除后,需要从根节点往下,依次计算平衡因子,判断是否失衡,并调整
    219. */
    220. public Tree delete(Tree root,int key){
    221. //找到需要删除的节点
    222. Tree current = find(root,key);
    223. Tree parent = current==null?null:current.parent;
    224. if(current!=null){
    225. //删除节点的左子树不为空
    226. if(current.leftTree != null && current.rightTree != null){
    227. //查询待删除节点的直接后继节点
    228. Tree successorNode = getSuccessorNode(current);
    229. //保存节点数据,便于后面的替换
    230. int data = successorNode.data;
    231. //删除该后继节点
    232. delete(root,successorNode.data);
    233. //替换节点
    234. current.data = data;
    235. }else if(current.leftTree != null || current.rightTree != null){
    236. //删除含有左子树或者右子树的节点,将删除节点的父节点直接连接删除节点的左子树或者右子树
    237. if(current.leftTree!=null){
    238. current.leftTree.parent = parent;
    239. }else{
    240. current.rightTree.parent = parent;
    241. }
    242. if(parent!=null){
    243. if(parent.data>current.data){
    244. parent.leftTree = current.leftTree != null?current.leftTree:current.rightTree;
    245. }else{
    246. parent.rightTree = current.leftTree != null?current.leftTree:current.rightTree;
    247. }
    248. //更新高度
    249. updateHeight(parent);
    250. }else{
    251. //删除节点为根节点,直接将根节点替换为下一个节点
    252. if(current.leftTree!=null){
    253. root = current.leftTree;
    254. }else{
    255. root = current.rightTree;
    256. }
    257. }
    258. }else{
    259. //删除叶子节点
    260. if(parent==null){
    261. //根节点,直接将树置空
    262. root = null;
    263. }else{
    264. //删除叶子节点,直接置空
    265. if(parent.data>current.data){
    266. parent.leftTree = null;
    267. }else{
    268. parent.rightTree = null;
    269. }
    270. //更新高度
    271. updateHeight(parent);
    272. }
    273. }
    274. return update(root,key);
    275. }
    276. return null;
    277. }
    278. /**
    279. * 从tree节点开始,一直往上更新节点高度
    280. * @param tree
    281. */
    282. public void updateHeight(Tree tree){
    283. Tree parent = tree;
    284. while(parent!=null){
    285. //修改父节点高度
    286. countHeight(parent);
    287. parent = parent.parent;
    288. }
    289. }
    290. /**
    291. * 查找节点
    292. * @param root
    293. * @param key
    294. * @return
    295. */
    296. public Tree find(Tree root,int key){
    297. if(root == null){
    298. return null;
    299. }
    300. Tree current = root;
    301. while(current.data!=key){
    302. if(current.data>key){
    303. current = current.leftTree;
    304. }else{
    305. current = current.rightTree;
    306. }
    307. if(current==null){
    308. break;
    309. }
    310. }
    311. return current;
    312. }
    313. /**
    314. * 获取tree的后继节点
    315. * @param tree
    316. * @return
    317. */
    318. public Tree getSuccessorNode(Tree tree){
    319. Tree current = tree==null?null:tree.rightTree;
    320. if(current==null){
    321. return null;
    322. }
    323. while(current.leftTree != null){
    324. current = current.leftTree;
    325. }
    326. return current;
    327. }
    328. /**
    329. * 递归调整二叉树
    330. * @param root
    331. * @param key
    332. */
    333. public void updateTree(Tree root,int key){
    334. Tree current = root;
    335. //左子树高则右旋
    336. if(countBF(current) == 2){
    337. //左孩子节点的右子树高则先左旋
    338. if(countBF(current.leftTree) == -1){
    339. leftRotate(current.leftTree);
    340. }
    341. rightRotate(current);
    342. }
    343. //右子树高则左旋
    344. if(countBF(current) == -2){
    345. //右孩子节点的左子树高则先右旋
    346. if(countBF(current.rightTree) == 1){
    347. rightRotate(current.rightTree);
    348. }
    349. leftRotate(current);
    350. }
    351. //调整之后重新计算节点的高度
    352. countHeight(root);
    353. if(key
    354. if(current.leftTree == null){
    355. }else{
    356. updateTree(current.leftTree,key);
    357. }
    358. }else{
    359. if(current.rightTree == null){
    360. }else{
    361. updateTree(current.rightTree,key);
    362. }
    363. }
    364. }
    365. /**
    366. * 调整二叉树为平衡二叉树
    367. * @param tree
    368. * @param data 删除节点
    369. * @return
    370. */
    371. public Tree update(Tree tree,int data){
    372. //初始数据
    373. if(tree ==null){
    374. tree = new Tree(data);
    375. }else{
    376. //刷新tree,使tree始终为根节点
    377. while(tree.parent!=null){
    378. tree = tree.parent;
    379. }
    380. updateTree(tree,data);
    381. }
    382. //刷新tree,使tree为根节点
    383. while(tree.parent!=null){
    384. tree = tree.parent;
    385. }
    386. return tree;
    387. }
    388. /**
    389. * 测试
    390. * @param args
    391. */
    392. public static void main(String[] args) {
    393. AVLtree avLtree = new AVLtree();
    394. avLtree.insert(3);
    395. avLtree.insert(2);
    396. avLtree.insert(1);
    397. avLtree.insert(4);
    398. avLtree.insert(5);
    399. avLtree.insert(6);
    400. avLtree.insert(7);
    401. avLtree.insert(10);
    402. avLtree.insert(9);
    403. Tree tree = avLtree.insert(8);
    404. avLtree.showTree(tree);
    405. System.out.println("-----------------");
    406. Tree tree5 = avLtree.delete(tree,5);
    407. avLtree.showTree(tree5);
    408. System.out.println("-----------------");
    409. Tree tree6 = avLtree.delete(tree,6);
    410. avLtree.showTree(tree6);
    411. }
    412. }

    结果:

    五、红黑树(R-B Tree)

    介绍

    红黑树,其节点分为两类,一类被标记为红色,另一类被标记为黑色;红黑树是一棵不完整的平衡二叉查找树树,

    性质

    • 根节点是黑色
    • 叶子节点不存储数据,并且是黑色
    • 任何相邻的节点不能同时为红色,必须使用黑色节点隔开
    • 对于单个节点,该节点到叶子节点的所有路径中都包含相同数目的黑色节点

    旋转

    • 左旋
      • 旧根节点的右子树为新根节点的左子树
      • 新根节点的左子树为旧根节点
      • 交换新根节点和旧根节点的颜色,新黑旧红
    • 右旋
      • 旧根节点的左子树为新根节点的右子树
      • 新根节点的右子树为旧根节点
      • 交换新根节点和旧根节点的颜色,新黑旧红

    红黑树的插入步骤

    1. 生成二叉查找树
    2. 令插入点为红色
    3. 判断是否需要旋转调整,以及如何调整

    判断情况如下

    情况一:插入点为根节点,违反了性质1,直接令其为黑色

    情况二:插入点的父节点为红色,违反了性质3,需要进行调整

    父节点为红色的情况需要再细分为以下几种:

    父节点为红色,父节点的兄弟节点为黑色,插入点位于父节点的左子树,如下图,需要先右旋,然后左旋,并交换祖父节点和插入节点的颜色

     父节点为红色,父节点的兄弟节点为黑色,插入点位于父节点的右子树,如下图,需要先左旋,然后右旋,并交换祖父节点和插入节点的颜色

     父节点为红色,父节点的兄弟节点也为红色,如下图,需要把父节点和父节点的兄弟节点全变为黑色,并将祖父节点变为红色,再将祖父节点当作插入点,递归重复判断是否需要调整(PS:若最后把根节点变成了红色,直接将根节点改成黑色即可,如下图第三张)

     例子:假设需要顺序插入{2,4,3,5,6,8,7,9},生成一棵红黑树,以下列出了推导过程图。








     红黑树插入实现(Java)

    1. package com.examply;
    2. import java.util.*;
    3. /**
    4. * 红黑树
    5. */
    6. public class RBTree {
    7. static class RBNode{
    8. int data;
    9. RBNode leftNode;
    10. RBNode rightNode;
    11. RBNode parent;
    12. int color;//颜色,0红色1黑色
    13. //无参构造方法
    14. RBNode() {
    15. }
    16. RBNode(int data) {
    17. this.data = data;
    18. //默认颜色为红色
    19. this.color = 0;
    20. }
    21. }
    22. //红色
    23. private static final int RED_NODE = 0;
    24. //黑色
    25. private static final int BLACK_NODE = 1;
    26. /**
    27. * 左旋
    28. * @param node
    29. */
    30. public void leftRotate(RBNode node){
    31. if(node==null){
    32. return;
    33. }
    34. RBNode oldNode = node.parent;
    35. RBNode parent = oldNode.parent;
    36. RBNode newNode = node;
    37. //新根节点的父节点为旧根节点的父节点
    38. newNode.parent = parent;
    39. if(parent!=null){
    40. if(parent.data
    41. parent.rightNode = newNode;
    42. }else{
    43. parent.leftNode = newNode;
    44. }
    45. }
    46. if(newNode.leftNode!=null){
    47. //新根节点的左子树的父节点为旧根节点
    48. newNode.leftNode.parent = oldNode;
    49. }
    50. //旧根节点的右子树为新根节点的左子树
    51. oldNode.rightNode = newNode.leftNode;
    52. //新根节点的左子树为旧根节点
    53. newNode.leftNode = oldNode;
    54. //旧根节点的父节点为新根节点
    55. oldNode.parent = newNode;
    56. }
    57. /**
    58. * 右旋
    59. * @param node
    60. */
    61. public void rightRotate(RBNode node){
    62. if(node==null){
    63. return;
    64. }
    65. RBNode oldNode = node.parent;
    66. RBNode parent = oldNode.parent;
    67. RBNode newNode = node;
    68. //新根节点的父节点为旧根节点的父节点
    69. newNode.parent = parent;
    70. if(parent!=null){
    71. if(parent.data
    72. parent.rightNode = newNode;
    73. }else{
    74. parent.leftNode = newNode;
    75. }
    76. }
    77. if(newNode.rightNode!=null){
    78. //新根节点的右子树的父节点为旧根节点
    79. newNode.rightNode.parent = oldNode;
    80. }
    81. //旧根节点的左子树为新根节点的右子树
    82. oldNode.leftNode = newNode.rightNode;
    83. //新根节点的右子树为旧根节点
    84. newNode.rightNode = oldNode;
    85. //旧根节点的父节点为新根节点
    86. oldNode.parent = newNode;
    87. }
    88. /**
    89. * 插入数据
    90. * @param root
    91. * @param data
    92. */
    93. public void insert(RBNode root,int data){
    94. if(root==null){
    95. return;
    96. }
    97. RBNode current = root;
    98. //插入数据
    99. if(root.data>data){
    100. if(root.leftNode!=null){
    101. current = root.leftNode;
    102. insert(current,data);
    103. return;
    104. }else{
    105. root.leftNode = new RBNode(data);
    106. root.leftNode.parent = root;
    107. current = root.leftNode;
    108. }
    109. }else{
    110. if(root.rightNode!=null){
    111. current = root.rightNode;
    112. insert(current,data);
    113. return;
    114. }else{
    115. root.rightNode = new RBNode(data);
    116. root.rightNode.parent = root;
    117. current = root.rightNode;
    118. }
    119. }
    120. fixRBTree(current);
    121. }
    122. /**
    123. * 调整红黑树
    124. * @param current
    125. */
    126. public void fixRBTree(RBNode current){
    127. //根节点,直接变黑色
    128. if(current.parent==null){
    129. current.color = BLACK_NODE;
    130. }else{
    131. RBNode parent = current.parent;
    132. //判断父节点为红色
    133. if(parent.color == RED_NODE){
    134. //判断插入点位于左子树还是右子树
    135. if(current.data>parent.data){
    136. //判断父节点的兄弟节点位置
    137. if(parent.parent.data
    138. //RR型
    139. //判断父节点的兄弟节点是否为红色
    140. if(parent.parent.leftNode!=null && parent.parent.leftNode.color==RED_NODE){
    141. //令父节点的兄弟节点为黑色
    142. parent.parent.leftNode.color = BLACK_NODE;
    143. //令父节点为黑色
    144. parent.color = BLACK_NODE;
    145. //令祖父节点为红色
    146. parent.parent.color = RED_NODE;
    147. //递归祖父节点
    148. fixRBTree(parent.parent);
    149. }else{
    150. //左旋
    151. leftRotate(current.parent);
    152. //新根节点为黑色
    153. current.parent.color = BLACK_NODE;
    154. //旧根节点为红色
    155. if(current.parent.leftNode!=null){
    156. current.parent.leftNode.color = RED_NODE;
    157. }
    158. }
    159. }else{
    160. //LR型
    161. if(parent.parent.rightNode!=null && parent.parent.rightNode.color==RED_NODE){
    162. //令父节点的兄弟节点为黑色
    163. parent.parent.rightNode.color = BLACK_NODE;
    164. //令父节点为黑色
    165. parent.color = BLACK_NODE;
    166. //令祖父节点为红色
    167. parent.parent.color = RED_NODE;
    168. //递归祖父节点
    169. fixRBTree(parent.parent);
    170. }else{
    171. //左旋
    172. leftRotate(current);
    173. //右旋
    174. rightRotate(current);
    175. //新根节点为黑色
    176. current.color = BLACK_NODE;
    177. //旧根节点为红色
    178. current.rightNode.color = RED_NODE;
    179. }
    180. }
    181. }else{
    182. //RL型
    183. if(parent.parent.data
    184. if(parent.parent.leftNode!=null && parent.parent.leftNode.color==RED_NODE){
    185. //令父节点的兄弟节点为黑色
    186. parent.parent.leftNode.color = BLACK_NODE;
    187. //令父节点为黑色
    188. parent.color = BLACK_NODE;
    189. //令祖父节点为红色
    190. parent.parent.color = RED_NODE;
    191. //递归祖父节点
    192. fixRBTree(parent.parent);
    193. }else{
    194. //右旋
    195. rightRotate(current);
    196. //左旋
    197. leftRotate(current);
    198. //新根节点为黑色
    199. current.color = BLACK_NODE;
    200. //旧根节点为红色
    201. current.leftNode.color = RED_NODE;
    202. }
    203. }else{
    204. //LL型
    205. if(parent.parent.rightNode!=null && parent.parent.rightNode.color==RED_NODE){
    206. //令父节点的兄弟节点为黑色
    207. parent.parent.rightNode.color = BLACK_NODE;
    208. //令父节点为黑色
    209. parent.color = BLACK_NODE;
    210. //令祖父节点为红色
    211. parent.parent.color = RED_NODE;
    212. //递归祖父节点
    213. fixRBTree(parent.parent);
    214. }else{
    215. //右旋
    216. rightRotate(current.parent);
    217. //新根节点为黑色
    218. current.parent.color = BLACK_NODE;
    219. //旧根节点为红色
    220. current.parent.rightNode.color = RED_NODE;
    221. }
    222. }
    223. }
    224. }
    225. }
    226. }
    227. private RBNode root;
    228. public RBNode insert(int data){
    229. //初始数据
    230. if(root ==null){
    231. root = new RBNode(data);
    232. root.color = BLACK_NODE;
    233. }else{
    234. //刷新root,使root始终为根节点
    235. while(root.parent!=null){
    236. root = root.parent;
    237. }
    238. insert(root,data);
    239. }
    240. return root;
    241. }
    242. /**
    243. * 层次遍历树
    244. * @param tree
    245. */
    246. public void showTree(RBNode tree){
    247. //使tree指向根节点
    248. while(tree.parent!=null){
    249. tree = tree.parent;
    250. }
    251. List> res = new ArrayList<>();
    252. Queue queue = new LinkedList<>();
    253. queue.add(tree);
    254. while (!queue.isEmpty()){
    255. int count = queue.size();
    256. List list = new ArrayList<>();
    257. while (count > 0) {
    258. RBNode node = queue.poll();
    259. list.add("{当前节点:"+node.data+"颜色为:"+(node.color==0?"红色":"黑色")+",父节点:"+(node.parent==null?null:node.parent.data)+"}");
    260. if(node!=null && node.leftNode!=null){
    261. queue.add(node.leftNode);
    262. }
    263. if(node!=null && node.rightNode!=null){
    264. queue.add(node.rightNode);
    265. }
    266. count--;
    267. }
    268. res.add(list);
    269. }
    270. for (List re : res) {
    271. System.out.println(re);
    272. }
    273. }
    274. public static void main(String[] args) {
    275. RBTree tree = new RBTree();
    276. tree.insert(2);
    277. tree.insert(4);
    278. tree.insert(3);
    279. tree.insert(5);
    280. tree.insert(6);
    281. tree.insert(8);
    282. tree.insert(7);
    283. RBNode root = tree.insert(9);
    284. tree.showTree(root);
    285. }
    286. }

     结果:

     以上就是全部内容,不足之处,请多指教

  • 相关阅读:
    【Harmony OS】【ArkUI】ets开发 创建视图与构建布局
    【SpringCloud】Nacos的安装、Nacos注册、Nacos服务多级存储模型
    vr展厅开发流程
    Android Startup启动优化
    为什么国外程序员的创造力比中国程序员强?
    【JavaEE进阶系列 | 从小白到工程师】正则表达式的语法&使用
    【无标题】H5页面解决点击页面处关闭键盘,触发了页面的事件的问题
    CentOS Linux 系统镜像
    stable-diffusion 电商领域prompt测评集合
    pip install paramiko出错的解决方案
  • 原文地址:https://blog.csdn.net/qq_49721447/article/details/127403465