AVL树实现,AVL树特点

  AVL树实现,AVL树特点

  Yyds干货库存

  写之前要遇到C中最难的部分,也是一些大厂可能会出的面试题,比如手撕一颗红黑树。这篇博客的内容可能有点耗脑,所以我尽量接地气的分享给大家。

  AVL树这种树也叫平衡二叉树。这是以前的二叉查找树的变体。前面说过,搜索二叉树对于更有序的元素来说有点深,甚至转换成单树,所以搜索效率有点低。所以我们提出AVL树。所谓平衡二叉树,就是在二叉查找树中插入新的节点时,如果保证每个节点的左右子树高度差的绝对值小于1(树中的节点需要调整),就可以降低树的高度,从而减少平均搜索长度。这是AVL树的定义。至于怎么做,我们这里不需要关心。

  我们先来看看AVL树的特点。一般来说,对于那些比较有序的元素,我们通过一系列手段插入元素,降低树的高度,最终的树还是符合二叉查找树的特点的。

  AVL树的实现我们已经知道AVL树是什么了。今天我们就来简单实现一下。这个过程说实话有点难。我们在这里实现KV模型。不过还是先把框架搭好吧。

  命名空间位

  {

  模板类T,类T

  AVLTree级

  {

  };

  }平衡因素我们在前面设置了大框架,所以我们需要在这里开始构建树的节点。先看一段时间,再完善。我们已经在这里建立了,为什么还要加入父节点呢?等用上了再说吧。

  结构AVLTreeNode

  {

  AVLTreeNode(const T data=T())

  :_praent(nullptr)

  ,_left(nullptr)

  ,_right(nullptr)

  ,_data(数据)

  {}

  AVLTreeNode T * _ praent

  AVLTreeNode T * _ left

  AVLTreeNode T * _ right

  T _ data

  };现在我们需要谈一个问题。我们如何保证左右节点的差距只有一个?这里,我们需要使用一个平衡因子。父节点的平衡因子记录了左右子树的高度差。这样可以看到插入时平衡因子的变化,从而判断是否移动树。这里,我们的平衡因子记录了右边的子树减去左边的子树。至于如何修改平衡因子的值,这个就解释了。

  模板类T

  结构AVLTreeNode

  {

  AVLTreeNode(const T data=T())

  :_praent(nullptr)

  ,_left(nullptr)

  ,_right(nullptr)

  ,_data(数据)

  ,_bf(0)

  {}

  AVLTreeNode T * _ praent

  AVLTreeNode T * _ left

  AVLTreeNode T * _ right

  T _ data

  int _ bf

  };插入元素现在我们的大框架已经奠定,现在开始重点工作,比较烧脑。我们也只实现插入操作。

  模板类T

  结构AVLTreeNode

  {

  AVLTreeNode(const T data=T())

  :_praent(nullptr)

  ,_left(nullptr)

  ,_right(nullptr)

  ,_data(数据)

  ,_bf(0)

  {}

  AVLTreeNode T * _ praent

  AVLTreeNode T * _ left

  AVLTreeNode T * _ right

  T _ data

  int _ bf

  };

  模板类别K,类别T

  AVLTreeV级

  {

  公共:

  typedef AVLTreeNode T节点;

  AVLTree()

  :_pRoot(nullptr)

  {}

  布尔插入(常量数据)

  {

  返回true

  }

  公共:

  Node * _ pRoot

  };第一步是搜索二叉树的简单操作。我们先找出插入的节点在哪里。

  布尔插入(常量数据)

  {

  //第一步是判断第一次插入

  if (_pRoot==nullptr)

  {

  _pRoot=新节点(数据);

  _ pRoot-_ BF=0;

  返回true

  }

  //步骤2搜索二叉树找到位置

  Node * cur=_ pRoot

  Node * parent=nullptr

  而(cur!=nullptr)

  {

  if (cur- _data数据)

  {

  //去右边的树找

  parent=cur

  cur=cur-_ right;

  }

  else if (cur- _data数据)

  {

  //左树搜索

  parent=cur

  cur=cur-_ left;

  }

  其他

  {

  //我们这里不接受重复的值,你可以自己实现

  返回false

  }

  }

  //第三步:new带着节点出来

  Node* node=新节点(数据);

  node-_ BF=0;//记住不要相信构造函数,它可能不是你写的,也不会自动默认为0。

  //步骤4确定插入左树还是右树。

  if (parent- _data数据)

  {

  parent-_ left=node;

  }

  其他

  {

  parent-_ right=node;

  }

  //这里需要一个链接

  node-_ parent=parent;

  cur=节点;

  //更新平衡因子

  //.

  返回true

  }这里我们保留了最关键的一步,就是更新平衡因子。这里要讨论的东西太多了。首先我们要判断插入的节点是左树还是右树,方便我们修改父节点的平衡因子。

  布尔插入(常量数据)

  {

  //第一步是判断第一次插入

  if (_pRoot==nullptr)

  {

  _pRoot=新节点(数据);

  _ pRoot-_ BF=0;

  返回true

  }

  //步骤2搜索二叉树找到位置

  Node * cur=_ pRoot

  Node * parent=nullptr

  而(cur!=nullptr)

  {

  if (cur- _data数据)

  {

  //去右边的树找

  parent=cur

  cur=cur-_ right;

  }

  else if (cur- _data数据)

  {

  //左树搜索

  parent=cur

  cur=cur-_ left;

  }

  其他

  {

  //我们这里不接受重复的值,你可以自己实现

  返回false

  }

  }

  //第三步:new带着节点出来

  Node* node=新节点(数据);

  node-_ BF=0;//记住不要相信构造函数,它可能不是你写的,也不会自动默认为0。

  //步骤4确定插入左树还是右树。

  if (parent- _data数据)

  {

  parent-_ left=node;

  }

  其他

  {

  parent-_ right=node;

  }

  //这里需要一个链接

  node-_ parent=parent;

  cur=节点;

  //更新平衡因子

  而(家长!=nullptr)

  {

  //如果插入了右边的子树

  if (cur==parent- _right)

  {

  parent-_ BF;

  }

  其他

  {

  parent-_ BF-;

  }

  //开始检测

  if (parent- _bf==0)

  {

  打破;

  }

  else if(parent-_ BF==1 parent-_ BF==-1)

  {

  cur=parent

  parent=parent-_ parent;

  }

  else if(parent-_ BF==2 parent-_ BF==-2)

  {

  //这是大头。

  if (cur- _bf==1 parent- _bf==2)

  {

  }

  else if(cur-_ BF==-1 parent-_ BF==-2)

  {

  }

  //.

  }

  其他

  {

  断言(假);

  }

  }

  返回true

  }前面我们简单描述了平衡因子的变化。这里,我们具体情况具体分析。首先,我们是按照祖先来更新的,这一点毋庸置疑。

  if (cur==prev- _right)

  {

  prev-_ BF;

  }

  其他

  {

  prev-_ BF-;

  }也就是说我们修改平衡因子,在我们的子树高度没有变化的时候,也就是平衡因子为0的时候,停止更新。如果高度变为1,那么右边子树的高度一定更高,这就需要我们不断往上走。如果-1是左树的高度,我们也应该往上走。这里重点是-2和2,这是我们要讲的重点,也是我们。

  左旋假设右边的子树比左边的子树高两倍,它也满足一定的条件。让我们使用左旋转。我们先分析一下情况。

  至于怎么轮换,暂且不谈。先说一些具体情况。

  当h=0时

  当h=1时

  当h=2时,这里有很多情况。

  我们就不回去分析了,下面是具体用法,还是按照理论来轮换。

  将SURL赋予父的右边子树,将父赋予SURL的左边子树,以修改逻辑顺序和平衡因子。

  if (cur- _bf==1 parent- _bf==2)

  {

  //左撇子

  RotateL(父母);

  }

  void RotateL(节点*父节点)

  {

  node * subR=parent-_ right;

  node * subRL=subR-_ left;

  parent-_ right=subRL;

  if(subRL)

  subRL-_ praent=parent;

  //记录祖父节点

  node * grande=parent-_ praent;

  parent-_ praent=subR;

  subR-_ left=parent;

  if(祖父母==nullptr)

  {

  //parent是根节点。

  _ pRoot=subR

  _ pRoot-_ praent=nullptr;

  }

  其他

  {

  subR- _praent=祖父母;

  //在判断原始父节点和祖父节点之间的关系时

  if(parent==grande-_ left)

  {

  祖父母-_ left=subL;

  }

  其他

  {

  祖父母-_ right=subL;

  }

  }

  //修改平衡因子

  subR-_ BF=0;

  parent-_ BF=0;

  }我们先分析一下特殊情况。subRL可能为空,即h=0,这就需要判断subRL何时修改父节点。还有,你不觉得我们太圆滑了吗?如何确定父节点一定是根节点?我们面前的模型是一个子树模型,不一定保证是一棵完整的树。这个问题还是要判断的,不然就有问题了。

  正确的旋转相对简单。我们直接看这里的模型。正确的旋转是正确的高度,这里需要移到右边。

  else if(cur-_ BF==-1 parent-_ BF==-2)

  {

  //右撇子

  RotateR(父母);

  打破;

  }

  空旋转器(节点*父节点)

  {

  node * subL=parent-_ left;

  node * subLR=subL-_ right;

  parent-_ left=subLR;

  if (subLR)

  subLR-_ parent=parent;

  节点*祖父母=父母-_父母;

  parent-_ parent=subL;

  subL-_ right=parent;

  if(祖父母==nullptr)

  {

  _ pRoot=subL

  _ pRoot-_ parent=nullptr;

  }

  其他

  {

  subL- _parent=祖父母;

  if(祖父母-_左==父母)

  {

  祖父母-_ left=subL;

  }

  其他

  {

  祖父母-_ right=subL;

  }

  }

  //更新平衡因子

  subL-_ BF=parent-_ BF=0;

  }我们先测试一下单旋转,也就是我们尝试插入有序元素。这里,我们只需要一次旋转操作。但在这里,我们需要注意。这里不能确定我们的树是AVL树,只是看我们的代码是否符合逻辑。

  void test1()

  {

  AVLTree int,int a;

  for(int I=0;I 8;我)

  {

  a.插入(I 1);

  }

  Cout“序列遍历”endl

  a . level order();

  Cout“有序遍历”endl

  a . in order();

  }

  左右旋转现在我们开始一个复杂的情况,就是双旋转的问题。如果直接插入8,6,7,就有意思了。看一看。你会发现简单的单一旋转是解决不了问题的。这里需要一些手段。

  我们先看一下模型,然后再讨论解决方案。这里就像一个折线图。

  这里我们也列举几个具体情况。

  这个时候我们在想这个应该怎么旋转。我们需要一些灵活性。先看大政方针。

  也就是说,我们需要先用subL做左单旋,再用parent做右单旋。

  else if(cur-_ BF==1 parent-_ BF==-2)

  {

  RotateLR(parent);

  打破;

  }

  void RotateLR(节点*父节点)

  {

  RotateL(parent-_ left);

  RotateR(父母);

  //修改平衡因子

  //.

  }注意我们的轮换没有问题。这里可以直接重用前面的,但难点是需要修改平衡因子。然后就有以下几个问题。看看下面的方法

  我们需要保存这些节点,并讨论如何修改这些节点的平衡因子。

  void RotateLR(节点*父节点)

  {

  node * subL=parent-_ left;

  node * subLR=subL-_ right;

  RotateL(parent-_ left);

  RotateR(父母);

  //修改平衡因子

  //.

  }现在我们剩下的问题是如何修改平衡因子。我们知道SULR一定是0,但是subL和parent不一定。这个需要具体情况具体分析,那么我们讨论的基础是什么呢?那很有趣。我们就按照subLR的平衡因子的值来讨论吧。但是我们需要先录下来,单转的时候会修改。我们按照下图修改一下就可以了。

  void RotateLR(节点*父节点)

  {

  node * subL=parent-_ left;

  node * subLR=subL-_ right;

  int BF=subLR-_ BF;

  RotateL(parent-_ left);

  RotateR(父母);

  //修改平衡因子

  如果(bf==0)

  {

  parent-_ BF=0;

  subL-_ BF=0;

  subLR-_ BF=0;

  }

  else if (bf==1)

  {

  parent-_ BF=0;

  subL-_ BF=-1;

  subLR-_ BF=0;

  }

  else if (bf==-1)

  {

  parent-_ BF=1;

  subL-_ BF=0;

  subLR-_ BF=0;

  }

  其他

  {

  断言(假);

  }

  }左右双旋这个和上面一样,也是折线。我们需要左右单旋。

  看解决方案,这里就不举具体例子了。

  else if(cur-_ BF==-1 parent-_ BF==2)

  {

  rotater l(parent);

  打破;

  }

  void rotator 1(节点*父节点)

  {

  node * subR=parent-_ right;

  node * subRL=subR-_ left;

  int BF=subRL-_ BF;

  rotator(parent-_ right);

  RotateL(父母);

  //修改平衡因子

  }我们还是把重点放在如何修改平衡因子上,需要简单分析一下,但思路同上。

  void rotator 1(节点*父节点)

  {

  node * subR=parent-_ right;

  node * subRL=subR-_ left;

  int BF=subRL-_ BF;

  rotator(parent-_ right);

  RotateL(父母);

  //修改平衡因子

  如果(bf==0)

  {

  subR-_ BF=0;

  parent-_ BF=0;

  subRL-_ BF=0;

  }

  else if(bf==1)

  {

  subR-_ BF=0;

  parent-_ BF=-1;

  subRL-_ BF=0;

  }

  else if (bf==-1)

  {

  subR-_ BF=1;

  parent-_ BF=0;

  subRL-_ BF=0;

  }

  其他

  {

  断言(假);

  }

  为了避免代码错误,我们还在这里测试了无序度。

  void test2()

  {

  int arr[]={ 1,4,3,7,5,8,1,10 };

  int SZ=sizeof(arr)/sizeof(arr[0]);

  AVLTree int,int a;

  for(int I=0;我SZ;我)

  {

  a.insert(arr[I]);

  }

  Cout“序列遍历”endl

  a . level order();

  Cout“有序遍历”endl

  a . in order();

  }

  AVL树的判定这里需要看看我们写的AVL树是否正确。在这里,我们可以直接上传代码。

  公共:

  bool isBalanceTree()

  {

  return _ is balance tree(_ pRoot);

  }

  私人:

  int _height(节点*根)

  {

  if (root==nullptr)

  返回0;

  int LH=_ height(root-_ left);

  int RH=_ height(root-_ right);

  返回左侧右侧?LH 1:RH 1;

  }

  bool _IsBalanceTree(节点*根)

  {

  //空树也是平衡二叉树

  if (root==nullptr)

  返回true

  //记录左右节点的高度

  int left=_ height(root-_ left);

  int right=_ height(root-_ right);

  int diff=左-右;

  //判断平衡因子

  如果(绝对值(差值)=2)

  {

  Cout“平衡系数更新错误”endl

  返回false

  }

  如果(右-左!=root- _bf)

  {

  Cout 节点平衡系数与实际不符 endl

  返回false

  }

  return _ is balance tree(root-_ left)_ is balance tree(root-_ right);

  让我们测试有序和无序插入。

  无效测试4()

  {

  int N=1024

  AVLTree int,int a;

  for(int I=0;I N;我)

  {

  a.插入;

  }

  cout is it balanced a . is balance tree()endl;

  }

  无效测试5()

  {

  int N=1024 * 1024

  AVLTree int,int a;

  srand(time(0));

  for(int I=0;I N;我)

  {

  a.insert(rand());

  }

  cout is it balanced a . is balance tree()endl;

  }

  红树说完AVL树,这里我们需要看一下红黑树。你不觉得我们的AVL树太严格了吗?我们总是移动子树。我们先来看看红黑树的定义。

  红色的树是一个二叉查找树,但是每个节点都添加了一个存储位来指示节点的颜色,可以是红色或黑色。通过限制从根到叶的任意路径上每个节点的着色方法,红黑树保证了没有一条路径会比其他路径长一倍,所以近乎平衡。红黑树的搜索效率仍然低于AVL树,但综合效率并不差。

  我们提取了两个基本观点。

  节点颜色受颜色限制,保证没有一条路比其他路强一倍。

  红树的特性我们需要分析红黑树的特性,以便后期实现。

  每个节点不是红色就是黑色。根节点是黑色的。如果一个节点是红色的,它的两个子节点是黑色的,即红色是不连续的。对于每个节点,从该节点到其所有后代叶子的简单路径包含相同数量的黑色节点。每个叶子节点都是黑色的(这里叶子节点指的是空节点),弥补了特性3。首先,我们要分析一下为什么满足上述特征。这里我们可以看到一棵红黑树。首先,我们可以这样想。最短路径被假定为黑色。根据特性4,有相同的黑色节点,所以如果有最长的节点,就增加一些红色节点。根据特征3,红色节点不能连续,最长的大概是一红一黑。

  在这里,我们需要看看红黑树的节点是如何设计的。

  枚举颜色

  {

  红色,

  黑色

  };

  模板类别K,类别V

  结构RBTreeNode

  {

  RBTreeNode(常数对K,V kv)

  :_left(nullptr)

  ,_right(nullptr)

  ,_parent(nullptr)

  ,_kv(千伏)

  ,_col(红色)

  {}

  RBTreeNode K,V _ left

  RBTreeNode K,V _ right

  RBTreeNode K,V _ parent

  对K,V _ kv

  颜色_颜色;

  };这里我们想知道为什么默认节点是红色的?我们可以这样想。如果我们是黑的,不管父节点是红的还是黑的,我们都不需要旋转,但是这样会导致我插入的元素的一个分支的黑色增加1,这就破坏了特征4。此外,这种情况很难处理。如果默认为红色,如果父节点为红色,那么根据红色就不能连续,所以我们需要旋转。但这只影响我们父子。

  让我们先建立这个数据结构的框架。

  模板类别K,类别V

  类RBTree

  {

  公共:

  typedef RBTreeNode K,V节点;

  私人:

  Node * _ pRoot=nullptr

  };我们可以在这里插入元素。我们同意当前节点是cur,p是父节点,g是祖父节点,c是叔叔节点。插红黑树很重要。首先,我们之前的插页比较简单,就像二叉查找树一样。

  布尔插入(常数对K,V数据)

  {

  //第一步是判断第一次插入

  if (_pRoot==nullptr)

  {

  _pRoot=新节点(数据);

  _pRoot- _col=黑色;

  返回true

  }

  //步骤2搜索二叉树找到位置

  Node * cur=_ pRoot

  Node * parent=nullptr

  而(cur!=nullptr)

  {

  if (cur- _kv.first data.first)

  {

  //去右边的树找

  parent=cur

  cur=cur-_ right;

  }

  else if (cur- _kv.first data.first)

  {

  //左树搜索

  parent=cur

  cur=cur-_ left;

  }

  其他

  {

  //我们这里不接受重复的值,你可以自己实现

  返回false

  }

  }

  //知道节点

  Node* node=新节点(数据);

  node- _col=红色;//记住不要相信构造函数,它可能不是你写的,也不会自动默认为0。

  //插入元素

  if(数据优先母公司- _kv优先)

  {

  parent-_ right=node;

  }

  其他

  {

  parent-_ left=node;

  }

  node-_ parent=parent;

  cur=节点;

  //.

  }这里需要判断。想想我们的节点是红色的。如果我爸爸是黑人,我们可以直接插在这里,什么都不做。如果我父亲的节点是红色的,就会发生别的事情。

  if (parent- _col==BLACK)

  {

  返回true

  }我们开始处理父亲是红节点的情况,这里有几种情况。其中,我们发现后两种情况是一样的,只是做法不同。

  大叔存在,是红的。大叔不存在也不存在,黑大叔不存在也不存在,黑大叔存在也红。我们先分析一下这个相对简单的情况。注意我们的是一个子树,当然也可能是一棵完整的树。

  第一种情况,abcde是空树,其中cur是新节点,我们的父节点是红色的,所以不可能是有父节点的节点。所以这里一定有一个祖父节点。我们暂时假设我下面的树是一个完整的教训。这里有一个供讨论的问题集。

  假设是一棵完整的树,但是这里根节点变红了,不符合要求。当我们插入它时,我们必须最终修改根节点的颜色,以确保它是黑色的。

  布尔插入(常数对K,V数据)

  {

  //早期阶段

  //插入操作

  _pRoot- _col=黑色;

  返回true

  }具体案例2,ab不是空树,而且是红色的。那么cde就是以下任何一个xyzm,只要有黑节点。

  然后cur由黑变红,也就是经过上面的步骤,爷爷把它赋给cur,重新判断。

  随着我们逐渐向高层转变,后面的情况很多,但我们不会逃脱大叔存在和知名度的限制。这里就不讨论了。

  案例3判断cur是P的左还是右,P是g的左还是右,这会影响案例1吗?不会,案例一方向不重要,因为它只变色不旋转。让我们以案例二为例。

  while (parent parent- _col==RED)

  {

  node * Granger=parent-_ parent;

  if (parent- _left==parent)

  {

  //判断大叔节点

  节点*大叔=爷爷-_右;

  if(大叔大叔- _col==RED)

  {

  //变色

  parent-_ col=BLACK;

  大叔-_ col=BALCK;

  祖父- _col=红色;

  cur=祖父;

  parent=cur-_ parent;

  }

  Else //大叔不存在或者存在于黑

  {

  }

  }

  其他

  {

  Node*大叔=爷爷-_左;

  //大叔存在并且是红色的

  if(大叔大叔- _col==RED)

  {

  //变色

  parent-_ col=BLACK;

  大叔- _col=黑;

  祖父- _col=红色;

  cur=祖父;

  parent=cur-_ parent;

  }

  其他

  {

  }

  }

  }大叔不存在或者存在又是黑的,这是我们红黑树的困境。需要设计成旋转的,但是单次旋转还是比较简单的。这里需要注意的是如何改变颜色。

  叔叔不存在。很简单。cur是一个新节点。

  第一种情况,abcde都是空树,cur是新加的,其中大叔不存在。你需要把父亲变成黑色,祖父变成红色,然后进行一次旋转。难得的是不是旋转,而是变色。确保这个子树的黑色节点不变,那么P会变黑,G会变红。我们不会让cur变黑,但是如果它变黑了,为什么不直接插入黑节点呢?

  Cur是父的左边,parent是祖父的左边,右手cur是父的右边,parent是祖父的左右。

  而且还有黑这种情况。cur不能是新的。cur本来是黑色的,但是从第一种情况开始变了。c是xyzm中的任意一个,D和E可能是空的,后者是红的。

  当然,以上是我们讨论的最简单的情况。de也可以黑,加一层就行了。

  这里就不解释了。反正涉及到旋转,只有左手和右手的区别。

  node * Granger=parent-_ parent;

  if (parent- _left==parent)

  {

  //判断大叔节点

  节点*大叔=爷爷-_右;

  if(大叔大叔- _col==RED)

  {

  //变色

  parent-_ col=BLACK;

  大叔-_ col=BALCK;

  祖父- _col=红色;

  cur=祖父;

  parent=cur-_ parent;

  }

  Else //大叔不存在或者存在于黑

  {

  if (cur==parent- _left)

  {

  //g

  //p

  //c

  //右撇子

  RotateR(祖父);

  //变色

  parent-_ col=BLACK;

  祖父- _col=红色;

  }

  其他

  {

  }

  打破;

  }

  }

  其他

  {

  Node*大叔=爷爷-_左;

  //大叔存在并且是红色的

  if(大叔大叔- _col==RED)

  {

  //变色

  parent-_ col=BLACK;

  大叔- _col=黑;

  祖父- _col=红色;

  cur=祖父;

  parent=cur-_ parent;

  }

  其他

  {

  if (cur==parent- _right)

  {

  //g

  //p

  //c

  //左撇子

  RotateL(祖父);

  //变色

  parent-_ col=BLACK;

  祖父- _col=红色;

  }

  其他

  {

  }

  打破;

  }

  }大叔不存在或存在且黑。我们需要分析这种情况。我们会发现我们都在分析一条直线。和AVL树一样,我们也要考虑双旋的问题。在这里,我们将直接向您演示原理,而不是视具体情况而定。

  node * Granger=parent-_ parent;

  if (parent- _left==parent)

  {

  //判断大叔节点

  节点*大叔=爷爷-_右;

  if(大叔大叔- _col==RED)

  {

  //.

  }

  Else //大叔不存在或者存在于黑

  {

  if (cur==parent- _left)

  {

  }

  其他

  {

  //g

  //p

  //c

  //左撇子右撇子

  RotateL(父母);

  RotateR(祖父);

  cur- _col=黑色;

  祖父- _col=红色;

  }

  打破;

  }

  }

  其他

  {

  Node*大叔=爷爷-_左;

  //大叔存在并且是红色的

  if(大叔大叔- _col==RED)

  {

  }

  其他

  {

  if (cur==parent- _right)

  {

  }

  其他

  {

  //g

  //p

  //c

  //右撇子左撇子

  RotateR(父母);

  RotateL(祖父);

  cur- _col=黑色;

  祖父- _col=红色;

  }

  打破;

  }

  }旋转最后我们可以旋转写下来。AVL树种我们已经分析过了,这里就不解释了。

  void RotateL(节点*父节点)

  {

  node * sub=parent-_ right;

  node * subR=sub-_ left;

  parent-_ right=subR;

  if (subR)

  subR-_ parent=parent;

  node * prev=parent-_ parent;

  sub-_ left=parent;

  parent-_ parent=sub;

  if (prev==nullptr)

  {

  _ pRoot=sub

  _ pRoot-_ parent=nullptr;

  }

  其他

  {

  if (prev- _left==parent)

  prev-_ left=sub;

  其他

  prev-_ right=sub;

  sub-_ parent=prev;

  }

  }

  空旋转器(节点*父节点)

  {

  node * subL=parent-_ left;

  node * subLR=subL-_ right;

  parent-_ left=subLR;

  if (subLR)

  subLR-_ parent=parent;

  node * prev=parent-_ parent;

  parent-_ parent=subL;

  subL-_ right=parent;

  //pParent可能不是根。

  if (prev==nullptr)

  {

  _ pRoot=subL

  _ pRoot-_ parent=nullptr;

  返回;

  }

  if (prev- _left==parent)

  prev-_ left=subL;

  其他

  prev-_ right=subL;

  subL-_ parent=prev;

  }验证红黑树我们需要验证自己写的红黑树是否正确。这里需要写一个函数来判断。

  让我们先看看中序遍历,以确保我们是一个二叉查找树。

  int main()

  {

  bit:RBTree int,int rb

  for(int I=0;i 50我)

  {

  如果(i==4)

  {

  cout endl

  }

  rb。Insert(make_pair(i,I));

  }

  Rb . in order();

  返回0;

  }

  我们需要测量无序度。

  int main()

  {

  int N=10

  srand(time(0));

  bit:RBTree int,int rb

  for(int I=0;I N;我)

  {

  int ret=rand();

  rb。Insert(make_pair(ret,ret));

  }

  Rb . in order();

  //Rb . level order();

  返回0;

  }

  我们在这里思考,如果能知道它的最长路径和最短路径,是否能在某种程度上判断它是一棵红黑树?这种方法不太好,但在某种程度上是可能的。

  int main()

  {

  int N=1024

  bit:RBTree int,int rb

  for(int I=0;I N;我)

  {

  rb。Insert(make_pair(i,I));

  }

  cout Rb . max height()endl;

  cout Rb . min height()endl;

  返回0;

  }

  我们发现最长路径不会超过最短路径的两倍。但不能确定红色节点是否连续。下面的方法是正确的。

  公共:

  bool IsValidRBTree()

  {

  Node * pRoot=_ pRoot

  //空树也是红黑树

  if (nullptr==pRoot)

  返回true

  //检查根节点是否满足条件。

  如果(黑!=pRoot- _col)

  {

  “Cout”违反红黑树属性II:根节点必须是黑的“endl

  返回false

  }

  //获取任意路径中黑色节点的数量

  size _ t blackCount=0;

  Node * pCur=pRoot

  while (pCur)

  {

  if (BLACK==pCur- _col)

  blackCount

  pCur=pCur-_ left;

  }

  //检查是否满足红黑树的属性,用k记录路径中黑色节点的个数。

  size _ t k=0;

  return _IsValidRBTree(pRoot,k,black count);

  }

  私人:

  bool _IsValidRBTree(Node* pRoot,size_t k,const size_t blackCount)

  {

  //去null后,判断K和black是否相等。

  if (nullptr==pRoot)

  {

  如果(k!=黑计数)

  {

  “Cout”违例属性4:每个路径中的黑节点数必须相同“endl

  返回false

  }

  返回true

  }

  //计算黑色节点的数量

  if (BLACK==pRoot- _col)

  k;

  //检查当前节点及其父节点是否都是红色的。如果你遇到一个红色的节点,检查你的父亲

  node * p parent=pRoot-_ parent;

  if(p parent RED==p parent-_ col RED==pRoot-_ col)

  {

   Cout 违背自然3:没有红节点 endl 连在一起;

  返回false

  }

  return _ IsValidRBTree(pRoot-_ left,k,blackCount)

  _IsValidRBTree(pRoot- _right,k,black count);

  }

  int main()

  {

  int N=1024

  bit:RBTree int,int rb

  for(int I=0;I N;我)

  {

  rb。Insert(make_pair(i,I));

  }

  Cout 它平衡吗 rb。IsValidRBTree()endl;

  返回0;

  }

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • office2010激活密钥大全 怎么永久激活office2010
  • project2010产品密钥免费_project2010激活密钥永久激活码
  • c语言调用退出函数 c语言退出整个程序怎么写
  • c语言中怎么给函数初始化 c语言的初始化语句
  • c语言编写函数计算平均值 c语言求平均函数
  • chatgpt是什么?为什么这么火?
  • ChatGPT为什么注册不了?OpenAI ChatGPT的账号哪里可以注册?
  • OpenAI ChatGPT怎么注册账号?ChatGPT账号注册教程
  • chatgpt什么意思,什么是ChatGPT ?
  • CAD中怎么复制图形标注尺寸不变,CAD中怎么复制图形线性不变
  • cad中怎么创建并使用脚本文件,cad怎么运行脚本
  • cad中快速计算器的功能,cad怎么快速计算
  • cad中快速修改单位的方法有哪些,cad中快速修改单位的方法是
  • cad中心点画椭圆怎么做,cad轴测图怎么画椭圆
  • CAD中常用的快捷键,cad各种快捷键的用法
  • 留言与评论(共有 条评论)
       
    验证码: