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

开发 后端 算法
本篇继续给大家介绍关于Java编程的相关知识,今天主要介绍数据结构与算法之平衡二叉树,希望对你有所帮助!

[[390860]]

二叉排序树可能的问题

给定一个数列{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.      * 找到以node 根的二叉排序树的最小值,并删除以node 为根节点的二叉排序树的最小节点 
  49.      * 
  50.      * @param node 传入节点(当做二叉排序树的根节点) 
  51.      * @return 返回以node为根节点的二叉排序树的最小节点值 
  52.      */ 
  53.     public int delRightTreeMin(Node node) { 
  54.         Node target = node; 
  55.         //循环查找左节点 
  56.         while (target.left != null) { 
  57.             target = target.left
  58.         } 
  59.         //删除最小节点 
  60.         delNode(target.value); 
  61.         return target.value; 
  62.     } 
  63.  
  64.     /** 
  65.      * 找到以node 根的二叉排序树的最大值,并删除以node 为根节点的二叉排序树的最大节点 
  66.      * 
  67.      * @param node 传入节点(当做二叉排序树的根节点) 
  68.      * @return 返回以node为根节点的二叉排序树的最大节点值 
  69.      */ 
  70.     public int delLeftTreeMax(Node node) { 
  71.         Node target = node; 
  72.         while (target.right != null) { 
  73.             target = target.right
  74.         } 
  75.  
  76.         //删除最大节点 
  77.         delNode(target.value); 
  78.         return target.value; 
  79.     } 
  80.  
  81.     //删除节点 
  82.     public void delNode(int value) { 
  83.         if (root == null) { 
  84.             return
  85.         } else { 
  86.             Node targetNode = search(value); 
  87.             if (targetNode == null) { 
  88.                 return
  89.             } 
  90.             if (targetNode == root) { 
  91.                 root = null
  92.                 return
  93.             } 
  94.             Node parentNode = searchParent(targetNode); 
  95.  
  96.             if (targetNode.left == null && targetNode.right == null) { 
  97.                 //如果要删除的节点是叶子节点 
  98.                 if (parentNode.left != null && parentNode.left.value == targetNode.value) { 
  99.                     parentNode.left = null
  100.                 } 
  101.                 if (parentNode.right != null && parentNode.right.value == targetNode.value) { 
  102.                     parentNode.right = null
  103.                 } 
  104.             } else if (targetNode.left != null && targetNode.right != null) { 
  105.                 //如果要删除的节点是有两个子树的节点 
  106.                 int minValue = delRightTreeMin(targetNode.right); 
  107.                 targetNode.value = minValue; 
  108.                 //上下代码删除效果一样 
  109.                 //int maxValue = delLeftTreeMax(targetNode.left); 
  110.                 //targetNode.value = maxValue; 
  111.             } else { 
  112.                 //要删除的节点是只有左子节点 
  113.                 if (targetNode.left != null) { 
  114.                     if (parentNode != null) { 
  115.                         if (parentNode.left == targetNode) { 
  116.                             parentNode.left = targetNode.left
  117.                         } else { 
  118.                             parentNode.right = targetNode.left
  119.                         } 
  120.                     } else { 
  121.                         //如果父节点是空,让root换位 
  122.                         root = targetNode.left
  123.                     } 
  124.                 } else {//要删除的节点是只有右子节点 
  125.                     if (parentNode != null) { 
  126.                         if (parentNode.left == targetNode) { 
  127.                             parentNode.left = targetNode.right
  128.                         } else { 
  129.                             parentNode.right = targetNode.right
  130.                         } 
  131.                     } else { 
  132.                         //如果父节点是空,让root换位 
  133.                         root = targetNode.right
  134.                     } 
  135.  
  136.                 } 
  137.             } 
  138.         } 
  139.     } 
  140.  
  141.     //添加节点 
  142.     public void add(Node node) { 
  143.         if (root == null) { 
  144.             root = node; 
  145.         } else { 
  146.             root.add(node); 
  147.         } 
  148.     } 
  149.  
  150.     //中序遍历 
  151.     public void infixOrder() { 
  152.         if (root != null) { 
  153.             root.infixOrder(); 
  154.         } else { 
  155.             System.out.println("二叉排序为空,不能遍历"); 
  156.         } 
  157.     } 
  158.  
  159. class Node { 
  160.     int value; 
  161.     Node left
  162.     Node right
  163.  
  164.     public Node(int value) { 
  165.         this.value = value; 
  166.     } 
  167.  
  168.     /** 
  169.      * 返回左子树的高度 
  170.      * 
  171.      * @return 
  172.      */ 
  173.     public int leftHeight() { 
  174.         if (left == null) { 
  175.             return 0; 
  176.         } 
  177.         return left.height(); 
  178.     } 
  179.  
  180.     /** 
  181.      * 返回右子树的高度 
  182.      * 
  183.      * @return 
  184.      */ 
  185.     public int rightHeight() { 
  186.         if (this.right == null) { 
  187.             return 0; 
  188.         } 
  189.         return right.height(); 
  190.     } 
  191.  
  192.     /** 
  193.      * 返回以该节点为根节点的树的高度 
  194.      * 
  195.      * @return 
  196.      */ 
  197.     public int height() { 
  198.         return Math.max(this.left == null ? 0 : this.left.height(), this.right == null ? 0 : this.right.height()) + 1; 
  199.     } 
  200.  
  201.     /** 
  202.      * 左旋转 
  203.      */ 
  204.     public void leftRotate() { 
  205.         //创建新的节点,以当前根节点的值 
  206.         Node newNode = new Node(value); 
  207.         //把新的节点的左子树设置为当前节点的左子树 
  208.         newNode.left = left
  209.         //把新的右子树设置为当前节点的右子树的左子树 
  210.         newNode.right = right.left
  211.         //把当前节点的值替换成右子节点的值 
  212.         value = right.value; 
  213.         //把当前节点的右子树设置成当前节点的右子节点的右子树 
  214.         right = right.right
  215.         //把当前的节点的左子节点(左子树),设置成新的节点 
  216.         left = newNode; 
  217.     } 
  218.  
  219.     /** 
  220.      * 右旋转 
  221.      */ 
  222.     public void rightRotate() { 
  223.         //创建新的节点,以当前根节点的值 
  224.         Node newNode = new Node(value); 
  225.         //把新的节点的右子树设置成当前节点的右子树 
  226.         newNode.right = right
  227.         //把新的节点的左子树设置为当前节点左子树的右子树 
  228.         newNode.left = left.right
  229.         //把当前节点的值换为左子节点的值 
  230.         value = left.value; 
  231.         //把当前节点左子树设置成左子树的左子树 
  232.         left = left.left
  233.         //把当前节点的右子树设置新节点 
  234.         right = newNode; 
  235.     } 
  236.  
  237.     /** 
  238.      * 查找要删除节点的父节点 
  239.      * 
  240.      * @param node 要删除的节点 
  241.      * @return 要删除节点的父节点 
  242.      */ 
  243.     public Node searchParent(Node node) { 
  244.         //如果当前节点就是要删除节点的父节点就返回 
  245.         if ((this.left != null && this.left.value == node.value) || 
  246.                 (this.right != null && this.right.value == node.value)) { 
  247.             return this; 
  248.         } else { 
  249.             if (this.left != null && node.value < this.value) { 
  250.                 //如果查找的节点的值小于当前节点的值,向左子树递归查找 
  251.                 return this.left.searchParent(node); 
  252.             } else if (this.right != null && value >= this.value) { 
  253.                 //如果查找的节点的值小于当前节点的值,向左子树递归查找 
  254.                 return this.right.searchParent(node); 
  255.             } else { 
  256.                 return null
  257.             } 
  258.         } 
  259.     } 
  260.  
  261.     /** 
  262.      * 查找要删除的节点 
  263.      * 
  264.      * @param value 要删除的节点的值 
  265.      * @return 删除的节点 
  266.      */ 
  267.     public Node search(int value) { 
  268.         if (value == this.value) { 
  269.             return this; 
  270.         } else if (value < this.value) { 
  271.             if (this.left != null) { 
  272.                 return this.left.search(value); 
  273.             } else { 
  274.                 return null
  275.             } 
  276.         } else { 
  277.             if (this.right != null) { 
  278.                 return this.right.search(value); 
  279.             } else { 
  280.                 return null
  281.             } 
  282.         } 
  283.     } 
  284.  
  285.     //递归的形式添加节点,满足二叉排序树的要求 
  286.     public void add(Node node) { 
  287.         if (node == null) { 
  288.             return
  289.         } 
  290.         if (node.value < this.value) { 
  291.             if (this.left == null) { 
  292.                 this.left = node; 
  293.             } else { 
  294.                 //递归向左子树添加 
  295.                 this.left.add(node); 
  296.             } 
  297.         } else { 
  298.             if (this.right == null) { 
  299.                 this.right = node; 
  300.             } else { 
  301.                 //递归向右子节点添加 
  302.                 this.right.add(node); 
  303.             } 
  304.         } 
  305.  
  306.         //当添加完一个节点后,如果(右子树高度-左子树高度)> 1 ,进行左旋转 
  307.         if (rightHeight() - leftHeight() > 1) { 
  308.             //如果它的右子树的左子树的高度大于它的右子树的右子树的高度,需要先对右子树进行右旋转,再对当前节点进行左旋转 
  309.             if(right != null && right.leftHeight()>right.rightHeight()){ 
  310.                 right.rightRotate(); 
  311.                 leftRotate(); 
  312.             }else
  313.                 //直接进行左旋转即可 
  314.                 leftRotate(); 
  315.             } 
  316.             return
  317.         } 
  318.  
  319.         //当添加完一个节点后,如果(左子树高度-右子树高度)> 1 ,进行右旋转 
  320.         if (leftHeight() - rightHeight() > 1) { 
  321.             //如果它的左子树的右子树高度大于它的左子树的左子树高度,需要对左子树先进行左旋转,再对当前节点进行右旋转 
  322.             if(left != null && left.rightHeight() > left.leftHeight()){ 
  323.                 left.leftRotate(); 
  324.                 rightRotate(); 
  325.             }else
  326.                 //直接进行右旋转即可 
  327.                 rightRotate(); 
  328.             } 
  329.  
  330.         } 
  331.     } 
  332.  
  333.     //中序遍历 
  334.     public void infixOrder() { 
  335.         if (this.left != null) { 
  336.             this.left.infixOrder(); 
  337.         } 
  338.         System.out.println(this); 
  339.         if (this.right != null) { 
  340.             this.right.infixOrder(); 
  341.         } 
  342.     } 
  343.  
  344.     @Override 
  345.     public String toString() { 
  346.         return "Node{" + 
  347.                 "value=" + value + 
  348.                 '}'
  349.     } 

 【编辑推荐】

 

责任编辑:姜华 来源: 今日头条
相关推荐

2021-03-19 10:25:12

Java数据结构算法

2021-03-22 09:00:22

Java数据结构算法

2021-03-29 10:13:47

Java编程数据结构算法

2020-11-02 09:15:47

算法与数据结构

2021-03-18 08:44:20

Java数据结构算法

2021-09-29 10:19:00

算法平衡二叉树

2020-09-23 18:25:40

算法二叉树多叉树

2021-04-20 08:37:14

数据结构二叉树

2021-04-19 07:47:42

数据结构二叉树Tree

2021-04-07 09:26:37

Java数据结构算法

2021-04-28 20:12:27

数据结构创建

2021-03-24 10:41:04

Java数据结构算法

2021-03-09 06:30:32

JAVA数据结构算法

2021-04-13 09:37:41

Java数据结构算法

2021-05-12 09:07:09

Java数据结构算法

2013-01-30 10:34:02

数据结构

2021-04-23 09:12:09

Java数据结构算法

2023-04-06 07:39:48

2021-03-10 08:42:19

Java数据结构算法

2021-03-08 06:28:57

JAVA数据结构与算法稀疏数组
点赞
收藏

51CTO技术栈公众号