• 平衡二叉树(AVL)【java实现+图解】


    目录

    一、平衡二叉树(AVL)

    二、平衡二叉树的四种旋转

    1.右旋转

    2.左旋转

    3. 左右旋转

    4. 右左旋转

     三、基于二叉搜索树之平衡二叉树的代码实现

    1.具体方法思路

    2.java代码实现 


    一、平衡二叉树(AVL)

    一种自平衡二叉搜索树,它是在每个节点上增加一个平衡因子,然后通过调整树中节点的高度来保持树的平衡。平衡因子是左子树的高度减去右子树的高度,用它可以表示出当前节点的平衡程度。对于任意一个结点左子树和右子树的高度差不能超过1,当一个节点的平衡因子绝对值大于1时,这个节点就被称为不平衡节点。

    二、平衡二叉树的四种旋转

    1.右旋转

     

    1. //左旋转
    2. public Node leftSpin(Node x){
    3. Node y = x.right;
    4. Node t2 = y.left;
    5. x.right = t2;
    6. y.left = x;
    7. //更新节点的高度
    8. x.height = Math.max(getHight(x.left),getHight(x.right))+1;
    9. y.height = Math.max(getHight(y.left),getHight(y.right))+1;
    10. return y;
    11. }

     

    2.左旋转

     

    1. //右旋转
    2. public Node rightSpin(Node x){
    3. Node y = x.left;
    4. Node t2 = y.right;
    5. x.left = t2;
    6. y.right = x;
    7. //更新节点的高度
    8. x.height = Math.max(getHight(x.left),getHight(x.right))+1;
    9. y.height = Math.max(getHight(y.left),getHight(y.right))+1;
    10. return y;
    11. }

    3. 左右旋转

    先旋转成上面只需要右旋转的情况,再经过右旋转成平衡二叉树

    4. 右左旋转

    先旋转成上面只需要左旋转的情况,再经过左旋转成平衡二叉树

     三、基于二叉搜索树之平衡二叉树的代码实现

    1.具体方法思路

    主要方法:

    添加:每次添加一个节点后更新输的高度,平衡因子可能改变,依据平衡因子对二叉树进行调整;

    删除:每删除一个节点后,也需要更新输的高度,平衡因子可能改变,依据平衡因子对二叉树进行调整。

    辅助方法:

    判断是否是二叉搜索树:通过中序遍历,将节点的前一个值与后一个值比较;

    判断是否是平衡二叉树:通过判断每个子树的平衡因子的是否在[-1,1]区间内。

    2.java代码实现 

    1. import java.util.*;
    2. import java.util.stream.Collectors;
    3. public class AVLextends Comparable> {
    4. public class Node {
    5. T val;
    6. Node left;
    7. Node right;
    8. //以该节点为根的树的高度
    9. int height;
    10. //统计单词出现的次数
    11. int count;
    12. public Node(T val) {
    13. this.val = val;
    14. this.height = 1;//默认高度为1
    15. this.count = 1;
    16. }
    17. @Override
    18. public String toString() {
    19. return String.format("val:%s,heiget:%d,count:%d",this.val,this.height,this.count);
    20. }
    21. }
    22. private Node root;
    23. private Integer size;
    24. public AVL() {
    25. this.root = null;
    26. this.size = 0;
    27. }
    28. public boolean isEmpt() {
    29. return this.size == 0;
    30. }
    31. public Integer getSize() {
    32. return this.size;
    33. }
    34. //验证是否是二叉搜索树
    35. public boolean isBinaryTree(){
    36. List list = new ArrayList<>();
    37. midTraversal(list,this.root);
    38. for (int i = 1; i < list.size(); i++) {
    39. if (list.get(i-1).compareTo(list.get(i))>0){
    40. return false;
    41. }
    42. }
    43. return true;
    44. }
    45. //获取当前节点的高度
    46. public int getHight(Node node){
    47. if (node==null){
    48. return 0;
    49. }
    50. return node.height;
    51. }
    52. //获取当前节点的平衡因子
    53. public int getBalanceFactor(Node node){
    54. if (node==null){
    55. return 0;
    56. }
    57. return getHight(node.left)-getHight(node.right);
    58. }
    59. public boolean isBalanceTree(){
    60. return isBalanceTree(this.root);
    61. }
    62. //判断以node为节点的树是否是平衡二叉树
    63. private boolean isBalanceTree(Node node){
    64. if (node==null){
    65. return true;
    66. }
    67. int balanceFactor = Math.abs(getBalanceFactor(node));
    68. if (balanceFactor>1){
    69. return false;
    70. }else {
    71. return isBalanceTree(node.left)&&isBalanceTree(node.right);
    72. }
    73. }
    74. //左旋转
    75. public Node leftSpin(Node x){
    76. Node y = x.right;
    77. Node t2 = y.left;
    78. x.right = t2;
    79. y.left = x;
    80. //更新节点的高度
    81. x.height = Math.max(getHight(x.left),getHight(x.right))+1;
    82. y.height = Math.max(getHight(y.left),getHight(y.right))+1;
    83. return y;
    84. }
    85. //右旋转
    86. public Node rightSpin(Node x){
    87. Node y = x.left;
    88. Node t2 = y.right;
    89. x.left = t2;
    90. y.right = x;
    91. //更新节点的高度
    92. x.height = Math.max(getHight(x.left),getHight(x.right))+1;
    93. y.height = Math.max(getHight(y.left),getHight(y.right))+1;
    94. return y;
    95. }
    96. //添加
    97. public void add(T val) {
    98. this.root = add(this.root, val);
    99. }
    100. private Node add(Node node, T val) {
    101. if (node == null) {
    102. this.size++;
    103. Node leafNode = new Node(val);
    104. return leafNode;
    105. }
    106. //当前节点的值小于添加的val,因此val做右孩子
    107. if (node.val.compareTo(val) < 0) {
    108. node.right = add(node.right, val);
    109. //更新高度
    110. node.height = Math.max(getHight(node.left),getHight(node.right))+1;
    111. } else if (node.val.compareTo(val) > 0){
    112. node.left = add(node.left, val);
    113. //更新高度
    114. node.height = Math.max(getHight(node.left),getHight(node.right))+1;
    115. }else {
    116. node.count++;
    117. }
    118. //添加后还要判断是否是平衡二叉树
    119. Node resNode = node;
    120. int balanceFactor = getBalanceFactor(node);
    121. if (balanceFactor>1&&getBalanceFactor(node.left)>=0){
    122. //右
    123. resNode = rightSpin(node);
    124. }else if (balanceFactor>1&&getBalanceFactor(node.left)<0){
    125. //左右
    126. node.left = leftSpin(node.left);
    127. resNode = rightSpin(node);
    128. }else if (balanceFactor<-1&&getBalanceFactor(node.right)<=0){
    129. //左
    130. resNode = leftSpin(node);
    131. }else if (balanceFactor<-1&&getBalanceFactor(node.right)>0){
    132. //右左
    133. node.right = rightSpin(node.right);
    134. resNode = leftSpin(node);
    135. }
    136. return resNode;
    137. }
    138. //删除操作
    139. //删除树中的val
    140. public void remove(T val){
    141. if (!contain(val)){
    142. return;
    143. }
    144. this.root = remove(this.root,val);
    145. }
    146. /**
    147. * 删除val
    148. * @param node
    149. * @param val
    150. * @return
    151. */
    152. public Node remove(Node node, T val){
    153. // 递归终止条件
    154. if (node == null) {
    155. return null;
    156. }
    157. Node resNode = null;
    158. if (node.val.compareTo(val) == 0) {
    159. this.size--;
    160. if (node.right==null){
    161. //右子树为空
    162. resNode = node.left;
    163. }else if (node.left==null){
    164. //左子树为空
    165. resNode = node.right;
    166. }else {
    167. // 左右子树都不为空
    168. // 1.找到删除节点的后继
    169. Node suffixNode = getMinDG(node.right);
    170. // 2.删除后继
    171. suffixNode.right = remove(node.right,getMinDG(node.right).val);
    172. // 3.连接
    173. suffixNode.left = node.left;
    174. this.size++;
    175. // 返回删除后的根
    176. resNode = suffixNode;
    177. }
    178. }else if (node.val.compareTo(val)<0){
    179. node.right = remove(node.right,val);
    180. resNode = node;
    181. }else {
    182. node.left = remove(node.left,val);
    183. resNode = node;
    184. }
    185. //删除节点可能为叶子结点
    186. if (resNode==null){
    187. return null;
    188. }
    189. //更新高度
    190. resNode.height = Math.max(getHight(resNode.left),getHight(resNode.right))+1;
    191. int balanceFactor = getBalanceFactor(resNode);
    192. if (balanceFactor>1&&getBalanceFactor(resNode.left)>=0){
    193. //右
    194. resNode = rightSpin(resNode);
    195. }else if (balanceFactor>1&&getBalanceFactor(resNode.left)<0){
    196. //左右
    197. resNode.left = leftSpin(resNode.left);
    198. resNode = rightSpin(resNode);
    199. }else if (balanceFactor<-1&&getBalanceFactor(resNode.right)<=0){
    200. //左
    201. resNode = leftSpin(resNode);
    202. }else if (balanceFactor<-1&&getBalanceFactor(resNode.right)>0){
    203. //右左
    204. resNode.right = rightSpin(resNode.right);
    205. resNode = leftSpin(resNode);
    206. }
    207. return resNode;
    208. }
    209. private Node getMinDG(Node node) {
    210. // 递归终止条件
    211. if (node.left == null) {
    212. return node;
    213. }
    214. // 递归操作
    215. return getMinDG(node.left);
    216. }
    217. public String midTraversal() {
    218. List res = new ArrayList<>();
    219. midTraversal(res, this.root);
    220. return res.stream().map(item -> item.toString()).collect(Collectors.joining(","));
    221. }
    222. /**
    223. * 中序遍历
    224. *
    225. * @param result
    226. * @param node 当前节点
    227. * @return
    228. */
    229. private void midTraversal(List result, Node node) {
    230. if (node == null) {
    231. return;
    232. }
    233. midTraversal(result, node.left);
    234. result.add(node.val);
    235. midTraversal(result, node.right);
    236. }
    237. public void showTree(){
    238. showTree(this.root);
    239. }
    240. private void showTree(Node node){
    241. if (node == null) {
    242. return;
    243. }
    244. showTree(node.left);
    245. System.out.print(node);
    246. System.out.println("BalanceFactor:"+getBalanceFactor(node));
    247. showTree(node.right);
    248. }
    249. //查询是否存在val
    250. public boolean contain(T val){
    251. return contain(this.root,val);
    252. }
    253. private boolean contain(Node node,T val){
    254. // 递归的终止条件
    255. // 查询到低也没有找到
    256. if (node==null){
    257. return false;
    258. }
    259. // 递归操作
    260. if (node.val.compareTo(val)==0){
    261. return true;
    262. }else if (node.val.compareTo(val)<0){
    263. return contain(node.right,val);
    264. }else {
    265. return contain(node.left,val);
    266. }
    267. }
    268. //测试
    269. public static void main(String[] args) {
    270. AVL bst = new AVL<>();
    271. List list = ReadBookUtil.readBook("pride-and-prejudice.txt");
    272. list.stream().forEach(item->{
    273. bst.add(item);
    274. });
    275. System.out.println("是否是二分搜索树"+bst.isBinaryTree());
    276. System.out.println("是否是平衡二叉树"+bst.isBalanceTree());
    277. bst.showTree();
    278. }
    279. }
  • 相关阅读:
    Dockerfile文件详解
    vue中使用vant的几种引入方式-(全局引入,按需引入,页面中引入)
    【JAVA】String类
    【爬虫】实验项目三:验证码处理与识别
    信奥中的数学基础:分解质因数 因式分解
    Educational Codeforces Round 156 (Rated for Div. 2)
    SS928开发板 开发记录三: nfs 挂载
    D52【python 接口自动化学习】- python基础之模块与标准库
    前端学习案例-viewer.js实现预览效果
    Python办公自动化Excel
  • 原文地址:https://blog.csdn.net/weixin_63541561/article/details/134001147