平衡二叉树

平衡二叉树又叫AVL树,首先它是一颗二叉搜索树,其次要求它的每个节点的左右子树高度差的绝对值不超过一。

平衡二叉树举例

平衡二叉树

分析平衡二叉树

平衡因子

将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子。

上图为例,根节点5的左子树高度为2,右子树高度为2,平衡因子(以后简称b)为2-2=0;处于平衡状态。
节点3的左子树高度为1,右子树高度为1,b=1-1=1;处于平衡状态

去掉节点4

可以看到去掉节点四的情况下,节点3的左子树高度为1,右子树高度为0,b=1-0=1;处于平衡状态。

插入节点1

插入节点1的情况下,节点3的左子树高度为2,右子树高度为0,b=2-0=2;处于非平衡状态,这个时候就需要进行平衡。

注意

如果一棵平衡二叉树在插入或者删除一个节点之后处于不平衡状态,那么经过一次调整之后一定会处于平衡状态

几种不平衡的情况

先定义Node节点

和上一次二叉搜索树的定义一样

    public class Node<T>
    {
        public T Data;
        public int Index;
        public Node<T> LeftChild;
        public Node<T> RightChild;
        public Node<T> Parent;

        public int Height;

        public bool Larger(int index)
        {
            if (Index == index)
                throw new Exception("index exist:" + index);
            return Index > index;
        }
    }

LL型平衡调整

LL型简单情况

上图是LL型的最简单情况,根节点是3,连续插入2和1之后,3处于不平衡状态,此时将2作为根节点,3作其右子树,1不动,处于平衡状态。

LL复杂情况

上图红圈处插入一个比1小的数,节点4处于不平衡状态,此时将2的右子树作为4的左子树。下面是LL平衡调整的代码

      private Node<T> LLRotate(Node<T> node)
      {
          var parent = node.Parent;
          var left = node.LeftChild;
          var leftRight = left.RightChild;

          node.Parent = left;
          left.Parent = parent;
          node.LeftChild = left.RightChild;
          left.RightChild = node;

          if (leftRight != null)
              leftRight.Parent = node;

          if (parent != null)
          {
              if (node.Index < parent.Index)
                  parent.LeftChild = left;
              else
                  parent.RightChild = left;
          }

          SetHeight(node);
          SetHeight(left);

          return left;
      }

RR型平衡调整

RR简单情况

上图是RR型的简单情况,根节点是3,连续插入4和5之后,3处于不平衡状态,此时将4作为根节点,3作其左子树,5不动,处于平衡状态。

RR复杂情况

RR复杂情况和LL差不多。将6的左子树赋值给3的右子树。下面是RR型平衡调整的代码

      private Node<T> RRRotate(Node<T> node)
      {
          var parent = node.Parent;
          var right = node.RightChild;
          var rightLeft = right.LeftChild;

          node.Parent = right;
          right.Parent = parent;
          node.RightChild = right.LeftChild;
          right.LeftChild = node;

          if (rightLeft != null) 
              rightLeft.Parent = node;

          if (parent != null)
          {
              if (node.Index < parent.Index)
                  parent.LeftChild = right;
              else
                  parent.RightChild = right;
          }

          SetHeight(node);
          SetHeight(right);

          return right;
      }

LR调整

LR简单情况

LR调整分为两步,先对节点2进行RR调整,在对节点4进行LL型调整,便可达到平衡。

RL调整

RL简单情况

和LR调整类似,分两步,先对节点4进行LL调整,在对节点2进行RR调整,达到平衡状态。

这两种的复杂情况我就不画了,有兴趣大家可以自己试试

平衡二叉树的插入和删除

说了这么多,终于要讲到关键性操作了,插入与删除。
先说一下基本思路,插入和二叉搜索树一样,小的找左子树,大的找右子树,一直找到一个为空的,把节点插入进去,然后从当前节点开始平衡。
删除分为几种情况
1、要删除的节点无子节点,直接删除当前节点。
2、有一个子节点,交换要删除节点和子节点的数据,删除子节点。
3、有两个子节点,判断当前节点左右子树哪个高,左边高,在左边找最大的,右边高,在右边找最小的,交换两个节点的数据,回到情况1。
删除完成之后总是从真正删除节点的父节点开始平衡。

下面是平衡二叉树的完整代码,关键部分都给出了注释,有问题请留言或者QQ412484723(初步测试代码是对的,可能也有问题)

using System;

namespace BTree
{
  public class Node<T>
  {
      public T Data;
      public int Index;
      public Node<T> LeftChild;
      public Node<T> RightChild;
      public Node<T> Parent;

      public int Height;

      public bool Larger(int index)
      {
          if (Index == index)
              throw new Exception("index exist:" + index);
          return Index > index;
      }
  }

  class BalanceTree<T>
  {
      private Node<T> Root;

      public void CreatBalanceTree(Node<T> data)
      {
          if (Root != null)
              throw new Exception("can't creat,root exist");

          Root = new Node<T>();
          Root = data;
      }


      private Node<T> LLRotate(Node<T> node)
      {
          var parent = node.Parent;
          var left = node.LeftChild;
          var leftRight = left.RightChild;

          node.Parent = left;
          left.Parent = parent;
          node.LeftChild = left.RightChild;
          left.RightChild = node;

          if (leftRight != null)
              leftRight.Parent = node;

          if (parent != null)
          {
              if (node.Index < parent.Index)
                  parent.LeftChild = left;
              else
                  parent.RightChild = left;
          }

          SetHeight(node);
          SetHeight(left);

          return left;
      }

      private Node<T> RRRotate(Node<T> node)
      {
          var parent = node.Parent;
          var right = node.RightChild;
          var rightLeft = right.LeftChild;

          node.Parent = right;
          right.Parent = parent;
          node.RightChild = right.LeftChild;
          right.LeftChild = node;

          if (rightLeft != null) 
              rightLeft.Parent = node;

          if (parent != null)
          {
              if (node.Index < parent.Index)
                  parent.LeftChild = right;
              else
                  parent.RightChild = right;
          }

          SetHeight(node);
          SetHeight(right);

          return right;
      }

      public Node<T> Insert(Node<T> node)
      {
          var curNode = InsertNode(node);

          curNode.Height = 1;
          Balance(node);

          return node;
      }

      /// <summary>
      /// 非递归平衡节点。跳出循环的条件有两个,第一个是遍历到根节点发现都平衡就跳出,第二个是发现有不平衡的节点,平衡之后跳出。
      /// </summary>
      /// <param name="node"></param>
      private void Balance(Node<T> node)
      {
          var curNode = node;

          while (true)
          {
              if (curNode.Parent == null)
              {
                  Root = curNode;
                  break;
              }

              SetHeight(curNode.Parent);

              var b = GetBanlace(curNode.Parent);

              if (MathF.Abs(b) < 2)
              {
                  curNode = curNode.Parent;
                  continue;
              }

              Node<T> finalNode = null;

              if (b > 1)
              {
                  var b1 = GetBanlace(curNode.Parent.LeftChild);

                  if (b1 >= 0)
                      finalNode = LLRotate(curNode.Parent);  //LL
                  else
                  {
                      var n = RRRotate(curNode.Parent.LeftChild);  //LR
                      finalNode = LLRotate(n.Parent);
                  }
              }

              if (b < -1)
              {
                  var b1 = GetBanlace(curNode.Parent.RightChild);

                  if (b1 <= 0)
                      finalNode = RRRotate(curNode.Parent);  //RR
                  else
                  {
                      var n = LLRotate(curNode.Parent.RightChild); //RL
                      finalNode = RRRotate(n.Parent);
                  }
              }

              if (finalNode != null)
              {
                  if (finalNode.Parent == null)
                      Root = finalNode;
                  break;
              }
          }
      }

      private Node<T> InsertNode(Node<T> node)
      {
          if (Root == null)
              throw new Exception("sort tree does't exist");

          Node<T> temp;

          temp = Root;
          while (true)
          {

              if (node.Larger(temp.Index))
              {
                  if (temp.RightChild == null)
                  {
                      temp.RightChild = node;
                      node.Parent = temp;
                      return node;
                  }
                  else
                  {
                      temp = temp.RightChild;
                  }
              }
              else
              {
                  if (temp.LeftChild == null)
                  {
                      temp.LeftChild = node;
                      node.Parent = temp;
                      return node;
                  }
                  else
                  {
                      temp = temp.LeftChild;
                  }
              }
          }
      }

      public Node<T> FindNode(int index)
      {
          Node<T> node = null;

          var curNode = Root;

          while(true)
          {
              if(index==curNode.Index)
              {
                  node = curNode;
                  break;
              }

              if(index>curNode.Index)
              {
                  if (curNode.RightChild == null)
                      throw new Exception("can't find this node :" + index);

                  curNode = curNode.RightChild;
                  continue;
              }

              if (index < curNode.Index)
              {
                  if (curNode.LeftChild == null)
                      throw new Exception("can't find this node :" + index);

                  curNode = curNode.LeftChild;
                  continue;
              }
          }
          if (node == null)
              throw new Exception("can't find the node :" + index);

          return node;
      }

      public void DeleteNode(Node<T> node)
      {
          DeleteNodeByIndex(node.Index);
      }

      public void DeleteNodeByIndex(int index)
      {
          if (index == Root.Index)
              throw new Exception("can't delete root node");

          var curNode = FindNode(index);

          if (curNode.LeftChild == null && curNode.RightChild == null)//无子节点直接删除,
          {
              DeleteLeafNode(curNode);
          }

          if (curNode.LeftChild == null && curNode.RightChild != null)//有右子树,交换删除节点和其右子树的数据,删除右子树
          {
              var deleteNode = Swap(curNode, curNode.RightChild);
              DeleteLeafNode(deleteNode);
          }

          if (curNode.LeftChild != null && curNode.RightChild == null)//有左子树,交换删除节点和其左子树的数据,删除左子树
          {
              var deleteNode = Swap(curNode, curNode.LeftChild);
              DeleteLeafNode(deleteNode);
          }

          if (curNode.LeftChild != null && curNode.RightChild != null)//左右子树都存在
          {
              var b = GetBanlace(curNode);//判断当前节点的b

              if (b >= 0)
              {
                  var max = FindMax(curNode.LeftChild);//左边高,在左边找最大的

                  var swapNode = Swap(curNode, max);

                  if(swapNode.LeftChild!=null)
                  {
                      var deleteNode = Swap(curNode, swapNode);
                      DeleteLeafNode(deleteNode);
                  }
                  else
                  {
                      DeleteLeafNode(swapNode);
                  }
              }
              else
              {
                  var min = FindMin(curNode.RightChild);//右边高,在右边找最小的

                  var swapNode = Swap(curNode, min);

                  if (swapNode.RightChild != null)
                  {
                      var deleteNode = Swap(curNode, swapNode);
                      DeleteLeafNode(deleteNode);
                  }
                  else
                  {
                      DeleteLeafNode(swapNode);
                  }
              }
          }
      }

      /// <summary>
      /// 删除叶子节点,每次删除之后从父节点开始平衡
      /// </summary>
      /// <param name="node"></param>
      private void DeleteLeafNode(Node<T> node)
      {
          var parent = node.Parent;

          if (node == parent.LeftChild)
              parent.LeftChild = null;
          else
              parent.RightChild = null;
          
          SetHeight(parent);
          Balance(parent);
      }

      private Node<T> Swap(Node<T> node1,Node<T> node2)
      {
          var index = node2.Index;
          var data = node2.Data;

          node2.Index = node1.Index;
          node2.Data = node1.Data;

          node1.Index = index;
          node1.Data = data;

          return node2;
      }

      public Node<T> FindMin(Node<T> node)
      {
          var curNode = node;

          while(true)
          {
              if (curNode.LeftChild != null)
                  curNode = curNode.LeftChild;
              else
                  return curNode;
          }
      }

      public Node<T> FindMax(Node<T> node)
      {
          var curNode = node;

          while (true)
          {
              if (curNode.RightChild != null)
                  curNode = curNode.RightChild;
              else
                  return curNode;
          }
      }

      public Node<T> GetRoot()
      {
          return Root;
      }

      private int GetBanlace(Node<T> node)
      {
          return GetHeight(node.LeftChild) - GetHeight(node.RightChild);
      }

      private int GetHeight(Node<T> node)
      {
          return node == null ? 0 : node.Height;
      }

      private void SetHeight(Node<T> node)
      {
          node.Height = (int)MathF.Max(GetHeight(node.LeftChild), GetHeight(node.RightChild)) + 1;
      }

      private void InOrder(Node<T> root)
      {
          if (root != null)
          {
              Console.WriteLine(string.Format("index:{0},data:{1}", root.Index, root.Data));
              InOrder(root.LeftChild);
              InOrder(root.RightChild);
          }
      }
      public void InOrder()
      {
          InOrder(Root);
      }

  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352