文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java编程内功-数据结构与算法「平衡二叉树」

2024-12-03 07:50

关注

二叉排序树可能的问题

给定一个数列{1,2,3,4,5,6},要求创建一个二叉排序树(BST),分析问题所在

问题分析:

  1. 左子树全部为空,从形式上看,更像一个单链表;
  2. 插入速度没有影响;
  3. 查询速度明显降低(因为需要一次比较),不能发挥BST的优势。因为每次还要比较左子树,其查询速度,比单链表还慢。
  4. 解决方案-平衡二叉树(ALV)

基本介绍

  1. 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree),又称为AVL树,可以保证查询效率较高。
  2. 具有以下特点:它是一颗空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树。平衡二叉树的常用实现方法有 红黑树、AVL、替罪羊树、Treap、伸展树等;
  3. 举例说明,下图前两个都是平衡二叉树,第一个左右子树的高度差绝对值是1,第二个左右子树高度差的绝对值是0,而第三个的左右子树高度差的绝对值是2,这样第三个就不是平衡二叉树;

平衡二叉树之左旋转

步骤

  1. 创建一个新的节点newNode,值等于当前根节点的值。
  2. 把新节点的左子树设置成当前节点的左子树。
  3. 把新节点的右子树设置成当前节点的右子树的左子树。
  4. 把当前节点的值换为当前右子节点的值。
  5. 把当前节点的右子树设置成右子树的右子树。
  6. 把当前节点的左子树设置为新的节点。

平衡二叉树之右旋转

步骤:

  1. 创建一个新的节点,值等于当前根的节点的值。
  2. 把新节点的右子树设置成当前节点的右子树。
  3. 把新节点的左子树设置成当前节点的左子树的右子树。
  4. 把当前节点的值换位左子节点的值。
  5. 把当前节点的左子树设置成左子树的左子树。
  6. 把当前节点的右子树设置为新的节点。

平衡二叉树之双旋转

在某些情况下,单旋转不能完成完成平衡二叉树的转换,需要进行两次旋转。

  1. 如果它的右子树的左子树的高度大于它的右子树的右子树的高度,需要先对右子树进行右旋转,再对当前节点进行左旋转。
  2. 如果它的左子树的右子树高度大于它的左子树的左子树高度,
  3. 需要对左子树先进行左旋转,再对当前节点进行右旋转。

代码案例

  1. package com.xie.avl; 
  2.  
  3. public class AVLTreeDemo { 
  4.     public static void main(String[] args) { 
  5.         int[] arr = {4, 3, 6, 5, 7, 8}; 
  6.         AVLTree avlTree = new AVLTree(); 
  7.         for (int i = 0; i < arr.length; i++) { 
  8.             avlTree.add(new Node(arr[i])); 
  9.         } 
  10.         System.out.println("中序遍历"); 
  11.         avlTree.infixOrder(); 
  12.         System.out.println("在没有平衡处理前~~"); 
  13.         System.out.println("树的高度=" + avlTree.getRoot().height()); 
  14.         System.out.println("树的左子树的高度=" + avlTree.getRoot().leftHeight()); 
  15.         System.out.println("树的右子树的高度=" + avlTree.getRoot().rightHeight()); 
  16.     } 
  17.  
  18. class AVLTree { 
  19.     private Node root; 
  20.  
  21.     public Node getRoot() { 
  22.         return root; 
  23.     } 
  24.  
  25.     public void setRoot(Node root) { 
  26.         this.root = root; 
  27.     } 
  28.  
  29.     //查找要删除的节点的父节点 
  30.     public Node searchParent(Node node) { 
  31.         if (root != null) { 
  32.             return root.searchParent(node); 
  33.         } else { 
  34.             return null
  35.         } 
  36.     } 
  37.  
  38.     //查找要删除的节点 
  39.     public Node search(int value) { 
  40.         if (root == null) { 
  41.             return null
  42.         } else { 
  43.             return root.search(value); 
  44.         } 
  45.     } 
  46.  
  47.      
  48.     public int delRightTreeMin(Node node) { 
  49.         Node target = node; 
  50.         //循环查找左节点 
  51.         while (target.left != null) { 
  52.             target = target.left
  53.         } 
  54.         //删除最小节点 
  55.         delNode(target.value); 
  56.         return target.value; 
  57.     } 
  58.  
  59.      
  60.     public int delLeftTreeMax(Node node) { 
  61.         Node target = node; 
  62.         while (target.right != null) { 
  63.             target = target.right
  64.         } 
  65.  
  66.         //删除最大节点 
  67.         delNode(target.value); 
  68.         return target.value; 
  69.     } 
  70.  
  71.     //删除节点 
  72.     public void delNode(int value) { 
  73.         if (root == null) { 
  74.             return
  75.         } else { 
  76.             Node targetNode = search(value); 
  77.             if (targetNode == null) { 
  78.                 return
  79.             } 
  80.             if (targetNode == root) { 
  81.                 root = null
  82.                 return
  83.             } 
  84.             Node parentNode = searchParent(targetNode); 
  85.  
  86.             if (targetNode.left == null && targetNode.right == null) { 
  87.                 //如果要删除的节点是叶子节点 
  88.                 if (parentNode.left != null && parentNode.left.value == targetNode.value) { 
  89.                     parentNode.left = null
  90.                 } 
  91.                 if (parentNode.right != null && parentNode.right.value == targetNode.value) { 
  92.                     parentNode.right = null
  93.                 } 
  94.             } else if (targetNode.left != null && targetNode.right != null) { 
  95.                 //如果要删除的节点是有两个子树的节点 
  96.                 int minValue = delRightTreeMin(targetNode.right); 
  97.                 targetNode.value = minValue; 
  98.                 //上下代码删除效果一样 
  99.                 //int maxValue = delLeftTreeMax(targetNode.left); 
  100.                 //targetNode.value = maxValue; 
  101.             } else { 
  102.                 //要删除的节点是只有左子节点 
  103.                 if (targetNode.left != null) { 
  104.                     if (parentNode != null) { 
  105.                         if (parentNode.left == targetNode) { 
  106.                             parentNode.left = targetNode.left
  107.                         } else { 
  108.                             parentNode.right = targetNode.left
  109.                         } 
  110.                     } else { 
  111.                         //如果父节点是空,让root换位 
  112.                         root = targetNode.left
  113.                     } 
  114.                 } else {//要删除的节点是只有右子节点 
  115.                     if (parentNode != null) { 
  116.                         if (parentNode.left == targetNode) { 
  117.                             parentNode.left = targetNode.right
  118.                         } else { 
  119.                             parentNode.right = targetNode.right
  120.                         } 
  121.                     } else { 
  122.                         //如果父节点是空,让root换位 
  123.                         root = targetNode.right
  124.                     } 
  125.  
  126.                 } 
  127.             } 
  128.         } 
  129.     } 
  130.  
  131.     //添加节点 
  132.     public void add(Node node) { 
  133.         if (root == null) { 
  134.             root = node; 
  135.         } else { 
  136.             root.add(node); 
  137.         } 
  138.     } 
  139.  
  140.     //中序遍历 
  141.     public void infixOrder() { 
  142.         if (root != null) { 
  143.             root.infixOrder(); 
  144.         } else { 
  145.             System.out.println("二叉排序为空,不能遍历"); 
  146.         } 
  147.     } 
  148.  
  149. class Node { 
  150.     int value; 
  151.     Node left
  152.     Node right
  153.  
  154.     public Node(int value) { 
  155.         this.value = value; 
  156.     } 
  157.  
  158.      
  159.     public int leftHeight() { 
  160.         if (left == null) { 
  161.             return 0; 
  162.         } 
  163.         return left.height(); 
  164.     } 
  165.  
  166.      
  167.     public int rightHeight() { 
  168.         if (this.right == null) { 
  169.             return 0; 
  170.         } 
  171.         return right.height(); 
  172.     } 
  173.  
  174.      
  175.     public int height() { 
  176.         return Math.max(this.left == null ? 0 : this.left.height(), this.right == null ? 0 : this.right.height()) + 1; 
  177.     } 
  178.  
  179.      
  180.     public void leftRotate() { 
  181.         //创建新的节点,以当前根节点的值 
  182.         Node newNode = new Node(value); 
  183.         //把新的节点的左子树设置为当前节点的左子树 
  184.         newNode.left = left
  185.         //把新的右子树设置为当前节点的右子树的左子树 
  186.         newNode.right = right.left
  187.         //把当前节点的值替换成右子节点的值 
  188.         value = right.value; 
  189.         //把当前节点的右子树设置成当前节点的右子节点的右子树 
  190.         right = right.right
  191.         //把当前的节点的左子节点(左子树),设置成新的节点 
  192.         left = newNode; 
  193.     } 
  194.  
  195.      
  196.     public void rightRotate() { 
  197.         //创建新的节点,以当前根节点的值 
  198.         Node newNode = new Node(value); 
  199.         //把新的节点的右子树设置成当前节点的右子树 
  200.         newNode.right = right
  201.         //把新的节点的左子树设置为当前节点左子树的右子树 
  202.         newNode.left = left.right
  203.         //把当前节点的值换为左子节点的值 
  204.         value = left.value; 
  205.         //把当前节点左子树设置成左子树的左子树 
  206.         left = left.left
  207.         //把当前节点的右子树设置新节点 
  208.         right = newNode; 
  209.     } 
  210.  
  211.      
  212.     public Node searchParent(Node node) { 
  213.         //如果当前节点就是要删除节点的父节点就返回 
  214.         if ((this.left != null && this.left.value == node.value) || 
  215.                 (this.right != null && this.right.value == node.value)) { 
  216.             return this; 
  217.         } else { 
  218.             if (this.left != null && node.value < this.value) { 
  219.                 //如果查找的节点的值小于当前节点的值,向左子树递归查找 
  220.                 return this.left.searchParent(node); 
  221.             } else if (this.right != null && value >= this.value) { 
  222.                 //如果查找的节点的值小于当前节点的值,向左子树递归查找 
  223.                 return this.right.searchParent(node); 
  224.             } else { 
  225.                 return null
  226.             } 
  227.         } 
  228.     } 
  229.  
  230.      
  231.     public Node search(int value) { 
  232.         if (value == this.value) { 
  233.             return this; 
  234.         } else if (value < this.value) { 
  235.             if (this.left != null) { 
  236.                 return this.left.search(value); 
  237.             } else { 
  238.                 return null
  239.             } 
  240.         } else { 
  241.             if (this.right != null) { 
  242.                 return this.right.search(value); 
  243.             } else { 
  244.                 return null
  245.             } 
  246.         } 
  247.     } 
  248.  
  249.     //递归的形式添加节点,满足二叉排序树的要求 
  250.     public void add(Node node) { 
  251.         if (node == null) { 
  252.             return
  253.         } 
  254.         if (node.value < this.value) { 
  255.             if (this.left == null) { 
  256.                 this.left = node; 
  257.             } else { 
  258.                 //递归向左子树添加 
  259.                 this.left.add(node); 
  260.             } 
  261.         } else { 
  262.             if (this.right == null) { 
  263.                 this.right = node; 
  264.             } else { 
  265.                 //递归向右子节点添加 
  266.                 this.right.add(node); 
  267.             } 
  268.         } 
  269.  
  270.         //当添加完一个节点后,如果(右子树高度-左子树高度)> 1 ,进行左旋转 
  271.         if (rightHeight() - leftHeight() > 1) { 
  272.             //如果它的右子树的左子树的高度大于它的右子树的右子树的高度,需要先对右子树进行右旋转,再对当前节点进行左旋转 
  273.             if(right != null && right.leftHeight()>right.rightHeight()){ 
  274.                 right.rightRotate(); 
  275.                 leftRotate(); 
  276.             }else
  277.                 //直接进行左旋转即可 
  278.                 leftRotate(); 
  279.             } 
  280.             return
  281.         } 
  282.  
  283.         //当添加完一个节点后,如果(左子树高度-右子树高度)> 1 ,进行右旋转 
  284.         if (leftHeight() - rightHeight() > 1) { 
  285.             //如果它的左子树的右子树高度大于它的左子树的左子树高度,需要对左子树先进行左旋转,再对当前节点进行右旋转 
  286.             if(left != null && left.rightHeight() > left.leftHeight()){ 
  287.                 left.leftRotate(); 
  288.                 rightRotate(); 
  289.             }else
  290.                 //直接进行右旋转即可 
  291.                 rightRotate(); 
  292.             } 
  293.  
  294.         } 
  295.     } 
  296.  
  297.     //中序遍历 
  298.     public void infixOrder() { 
  299.         if (this.left != null) { 
  300.             this.left.infixOrder(); 
  301.         } 
  302.         System.out.println(this); 
  303.         if (this.right != null) { 
  304.             this.right.infixOrder(); 
  305.         } 
  306.     } 
  307.  
  308.     @Override 
  309.     public String toString() { 
  310.         return "Node{" + 
  311.                 "value=" + value + 
  312.                 '}'
  313.     } 

 【编辑推荐】

  1. 微服务面试必问的Dubbo,这么详细还怕自己找不到工作?
  2. 2021年值得关注的5个IT行业发展趋势
  3. 免费的安全软件落寞!让人唏嘘
  4. 界面UI即将大改!Windows1021H2最新预览版抢先看
  5. 微软为 Windows101909 推送 KB5000850 更新,修复资源管理器搜索等问题

 

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯