【数据结构】AVL树

简介: 【数据结构】AVL树

前言

在C++的STL标准库中的set、map、multiset和multimap的底层实现都是二叉搜索树。

而进行搜索数据的方式有:

  1. 暴力搜索。
  2. 二分搜索(缺陷:二分查找需要有序数组,伴随着插入,维护的成本较高)
  3. 二叉搜索树(缺陷:在极端场景下会退化成链表,其效率无法保证)
  4. 二叉平衡搜索树
  5. 多叉平衡搜索树:B树
  6. 哈希表

由于二叉搜索树的自身缺陷,假如往树中插入的元素有序或者接近有序,二叉树就会退化成为单支树,时间复杂度为O(N),因此map、set等关联式容器的底层结构式对二叉树进行了平衡处理,及采用平衡树来实现。而AVL树的实现就是二叉平衡平衡树。

AVL树的概念

       二叉搜索树虽然可以缩短查找的效率,但是如果数据有序或者接近有序,二叉搜索树就会退化成为单支树,查找元素相当于在顺序表里查找,效率低下。

       在1962年,俩位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis发明了一种解决上述问题的方法:当向二叉搜索树中插入新节点后,如果可以保证每一个节点的左右子树高度之差的绝对值不超过1(需要对树中的节点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者式空树,或者是具有以下性质的二叉搜索树:

  • 其左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

平衡因子的计算方式:平衡因子=右子树高度-左子树高度

AVL树的本质是高度平衡二叉搜索树,其二叉树只有在满二叉树的情况,平衡因子才会相等,否则进行增加与删除操作的时候,平衡因子肯定会被改变。

如果一棵二叉搜索树是高度平衡的,其就是AVL树。

如果有n个节点,其高度可以保持在O(logN),搜索时间复杂度O(logN)。

  • 当为满二叉树:2^h-1=N
  • AVL树:2^h-x= N(x的范围:1

AVL树的模拟实现

AVL树的基本框架

template<class k, class v>
struct AVLTreeNode
{
  //构造
  AVLTreeNode(const k& date)
    :_left(nullptr)
    , _right(nullptr)
    , _parent(nullptr)
    ,_kv(kv)
    ,_bf(0)
  {}
 
  AVLTreeNode<k, v>* _left;
  AVLTreeNode<k, v>* _right;
  AVLTreeNode<k, v>* _parent;
  pair<k,v> _kv;
  int _bf;//平衡因子
};
 
template<class k, class v>
class AVLTree
{
  typedef AVLTreeNode<k,v> Node;
  AVLTree()
    :_node(nullptr)
  {}
 
private:
  Node* _root;
};

AVL树的插入

       AVL树就是在二叉搜索树的基础之上引入了平衡因子,所以在插入节点的同时可以参考AVL树,然后通过结合平衡因子来解决问题。

其核心步骤分为三步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子
  3. 根据平衡因子观察是否达到平衡,如果没有则进行旋转。

插入新节点

  bool Insert(const pair<k,v>& kv)
  {
    //插入新节点
    if (_root == nullptr)
    {
      _root = new Node<kv>;
      return true;
    }
 
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur)
    {
      if (cur->_kv.first > kv.first)
      {
        parent = cur;
        cur = cur->_left;
      }
      else if (cur->_kv.first < kv.first)
      {
        parent = cur;
        cur = cur->_right;
      }
      else
      {
        return false;
      }
    }
    cur = new Node<kv>;
    if (cur->_kv.first > kv.first)
    {
      parent->_left = cur;
    }
    else if (cur->_kv.first < kv.first)
    {
      parent->_right = cur;
    }
    else
    {
      assert(false);
    }
    cur->_parent = parent;
    //计算平衡因子
  }

平衡因子的计算

       当新的节点插入之后,AVL树的平衡性可能会被破坏,此时就需要更新破坏因子,并检测是否破坏了AVL树的平衡性。

  • 平衡因子=右子树的高度-左子树的高度。

观察这棵已经是AVL树的每一个平衡因子,当cur插入之后,parent的平衡因子一定会被调整,在插入cur之前,parent的平衡因子一共会分成三种情况:-1/0/1。

当插入cur之后,可能出现的结果如下:

  1. 新增在左,parent的平衡因子减1。
  2. 新增在右,parent的平衡因子加1。
  3. 更新后parent的平衡因子==0,说明parent所在的子树高度不变,不会影响祖先,不用再继续沿着root的路径往上更新。
  4. 更新后parent的平衡因子==1 or ==-1,说明parent所在的子树高度变化,会对祖先造成影响,需要再继续沿着到root的路径往上更新。
  5. 更新后parent的平衡因子==2 or ==-2,说明parent所在的子树高度已经变化,并且不平衡,需要立刻对parent所在的子树进行旋转,让其平衡。
  6. 平衡因子向上更新到根节点——>插入结束。

我们在更新平衡因子的时候可以发现,平衡因子相当于是用于检测这棵AVL树的平衡的,平衡因子的区间是[-2,2]。

  bool Insert(const pair<k,v>& kv)
  {
    //插入新节点
    if (_root == nullptr)
    {
      _root = new Node<kv>;
      return true;
    }
 
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur)
    {
      if (cur->_kv.first > kv.first)
      {
        parent = cur;
        cur = cur->_left;
      }
      else if (cur->_kv.first < kv.first)
      {
        parent = cur;
        cur = cur->_right;
      }
      else
      {
        return false;
      }
    }
    cur = new Node<kv>;
    if (cur->_kv.first > kv.first)
    {
      parent->_left = cur;
    }
    else if (cur->_kv.first < kv.first)
    {
      parent->_right = cur;
    }
    else
    {
      assert(false);
    }
    cur->_parent = parent;
    //更新平衡因子
    while (parent)
    {
      //第一、二步:更新parent的平衡因子
      if (cur == parent->_left)
      {
        parent->_bf--;
      }
      else if (cur == parent->_right)
      {
        parent->_bf++;
      }
      //第三步:更新后parent的平衡因子==0
      if (parent->_bf == 0)
      {
        break;
      }
      //第四步:更新后parent的平衡因子==1 or ==-1
      else if (parent->_bf == 1 || parent->_bf == -1)
      {
        cur = parent;
        parent = cur->_parent;
      }
      //第五步:更新后parent的平衡因子==2 or ==-1
      else if (parent->_bf == 2 || parent->_bf == -2)
      {
        //旋转
        break;
      }
      else
      {
        assert(false);
      }
    }
  }

AVL树的旋转

       在一棵原本是是AVL平衡树中插入一个节点,可能会造成不平衡,此时必须调整树的结构,使之平衡。

       根据节点的插入位置的不同,AVL树的旋转会分为4种:

  1. 新节点插入较高左子树的左侧——右单旋
  2. 新节点插入较高右子树的右侧——左单旋
  3. 新节点插入较高左子树的右侧——先左单旋再右单旋
  4. 新节点插入较高右子树的左侧——先右单旋再左单旋

下面将进行详细讲解:

首先,旋转之前需要注意的俩个问题:

  • 保持其是搜索树
  • 变成平衡树且降低这个子树的高度。
左单旋

在上述的情况中,h的可能值为0,1,2,3,4...,a,b,c是符合AVL树的子树。

当h==2的时候,a和b是x,y,z中的一种,c只能是z,然后在这种情况下,才会导致M、N之间的旋转。

在插入前的可能形状的组合:3*3*1=9种;新元素可能插入的位置存在4种;这样当h==2的时候,合计情况有36种。

【注意】我们在插入新元素之后,会导致parent的平衡因子成为2,cur的平衡因子成为1,在这种情况下,需要左单旋转操作。

在进行左单旋操作的核心操作是:

  1. 将b转移到M的右子树之下;
  2. 如果b不等于空,将b的父亲转换为M;如果b为空,则不进行操作;
  3. 将M转移到N的左子树之下;
  4. 记录M的父亲
  5. 将M的父亲转换为N;
  6. 将记录的N的原父转换为N的父亲;并判断M是原父的左子还是右子。

这里可以总结:需要进行改变的数据一共有:parent的parent的孩子的指向,parent的右孩子的指向,parent的父亲指向,cur的左孩子指向,cur的父亲指向,cur的左孩子的父亲指向。

代码展示:

//左单旋
void RotateL(Node* parent)
{
  Node* cur = parent->_right;
  Node* curleft = cur->_left;
 
  parent->_right = curleft;
  if (curleft)
  {
    curleft->_parent = parent;
  }
  cur->_left = parent;
  Node* pparent = parent->_parent;
  parent->_parent = cur;
  if (pparent == nullptr)
  {
    _root = cur;
    cur->_parent = nullptr;
  }
  else
  {
    if (pparent->_left == parent)
    {
      pparent->_left = cur;
    }
    else //if(pparent->_right == parent)
    {
      pparent->_right = cur;
    }
    cur->_parent = pparent;
  }
  parent->_bf = cur->_bf = 0;
}

 

右单旋

右单旋与左单旋的方式类似,方式如图:

代码展示:

//右单旋
void RotateR(Node* parent)
{
  Node* cur = parent->_left;
  Node* curright = cur->_right;
  parent->_left = curright;
  if (curright)
  {
    curright->_parent = parent;
  }
  cur->_right = parent;
  Node* pparent = parent->_parent;
  parent->_parent = cur;
  if (pparent == nullptr)
  {
    _root = cur;
    cur->_parent = nullptr;
  }
  else
  {
    if (pparent->_left == parent)
    {
      pparent->_left = cur;
    }
    else
    {
      pparent->_right = cur;
    }
    cur->_parent = pparent;
  }
  parent->_bf = cur->_bf = 0;
}
右左旋转

       在了解完左单旋和右单选之后,我们基本对这俩种单旋转方式有了基本的认识,这俩种单旋的方式都是直线插入的,而在进行折线插入的时候需要实现另外一种方法。

【注意】我们可以在c位置的左子树位置或右子树位置添加节点。

当h==2的时候:

a和d是x,y,z中的任意一棵子树,c只能是z这棵子树;

这样合计下来:一共有3*3*4=36种情况。(3*3是a,d之前的组合;4是当h等于2的时候,可以新增的数据个数)。

此时可以发现,使用单旋的方式已经不能解决问题,需要使用双旋转的方式来解决问题:

  1. 以60为旋转点,进行右单旋转
  2. 以30为旋转点,进行左单旋转

右左双旋转的结果本质上是:

  • 60为根
  • 60的左边是30
  • 60的右边是90
  • 60的左边给了30的右边
  • 60的右边给了30的左边

通过对图例的了解,当h==0的时候,60的左右子树都为空,即60是新增的元素;当h>0的时候,在60的左子树或者60的右子树新增元素,才会构成右左旋转。

处理节点的平衡因子的时候,可以将平衡因子分成3份,判断的主要条件是60在旋转之前的的平衡因子:

  • 当60旋转之前的平衡因子为0,则旋转之后30,60,90的平衡因子都为0;
  • 当60旋转之前的平衡因子为1,则旋转之后60,90的平衡因子为0,30的平衡因子为-1;
  • 当60旋转之前的平衡因子为-1,则旋转之后60,30的平衡因子为0,90的平衡因子为1;

则进行代码展示:

  //左右旋转
  void RotateLR(Node* parent)
  {
    Node* cur = parent->_left;
    Node* curright = cur->_right;
    int bf = curright->_bf;
 
    RotateL(cur);
    RotateR(parent);
 
    if (bf == 0)
    {
      curright->_bf = 0;
      parent->_bf = 0;
      cur->_bf = 0;
    }
    else if (bf == 1)
    {
      curright->_bf = 0;
      parent->_bf = -1;
      cur->_bf = 0;
    }
    else if (bf == -1)
    {
      curright->_bf = 0;
      parent->_bf = 0;
      cur->_bf = 1;
    }
    else
    {
      assert(false);
    }
  }

 

左右旋转

左右旋转与右左旋转的思路相同,即:先对30进行左单选,然后再对90进行右单旋,旋转之后考虑平衡因子的更新。

处理节点的平衡因子的时候,可以将平衡因子分成3份,判断的主要条件是60在旋转之前的的平衡因子:

  • 当60旋转之前的平衡因子为0,则旋转之后30,60,90的平衡因子都为0;
  • 当60旋转之前的平衡因子为1,则旋转之后30,60的平衡因子为0,30的平衡因子为-1;
  • 当60旋转之前的平衡因子为-1,则旋转之后60,90的平衡因子为0,90的平衡因子为1;

则进行代码展示:

//右左旋转
void RotateRL(Node* parent)
{
  Node* cur = parent->_right;
  Node* curleft = cur->_left;
  int bf = curleft->_bf;
 
  RotateR(cur);
  RotateL(parent);
 
  if (bf == 0)
  {
    curleft->_bf = 0;
    parent->_bf = 0;
    cur->_bf = 0;
  }
  else if (bf == 1)
  {
    curleft->_bf = 0;
    parent->_bf = 0;
    cur->_bf = -1;
  }
  else if (bf == -1)
  {
    curleft->_bf = 0;
    parent->_bf = 0;
    cur->_bf = 1;
  }
  else
  {
    assert(false);
  }
}

AVL树的验证

      AVL树由于是再二叉搜索树的基础之上加入了平衡性的因素,因此如果需要验证AVL树,可以分为俩步:

1.验证是否为二叉搜索树:

如果中序遍历可以得到一个有序的序列,就可以说明是二叉搜索树。

2.验证是否为平衡树

  • 每一个节点子树的高度差的绝对值不会超过1(注意节点中如果没有平衡因子)
  • 节点的平衡因子的计算是否正确
  int Height(Node* root)
  {
    if (root == nullptr)
    {
      return 0;
    }
    int leftHeight = Height(root->_left);
    int rightHeight = Height(root->_right);
 
    return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
  }
 
  bool IsBalance()
  {
    return IsBalance(_root);
  }
 
  bool IsBalance(Node* root)
  {
    if (root == nullptr)
    {
      return true;
    }
    int leftHeight = Height(root->_left);
    int rightHeight = Height(root->_right);
 
    if (rightHeight - leftHeight != root->_bf)
    {
      cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
      return false;
    }
    
    return abs(rightHeight - leftHeight) < 2
      && IsBalance(root->_left)
      && IsBalance(root->_right);
        
  }

 

AVL树的删除

       由于AVL树也是二叉搜索树,可以按照二叉搜索树的方式将节点删除,步骤如下:

  1. 按照搜索树的规则查找节点删除
  2. 更新平衡因子,当cur为0的时候,更新parent的平衡因子
  3. 出现异常旋转。

AVL树的性能

       AVL树是一棵绝对平衡的二叉搜索树,要求每一个节点的左右子树高度之差的绝对值不能超过1,这样可以保证在查询的时候具有高效性,时间复杂度为logN。

       但是对于AVL树的一些结构修改的操作性能是非常低的,就比如:插入时需要维护其平衡,旋转的次数比较多,更差的是在删除的时候,有可能一直让旋转持续到根的位置。

代码展示

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
 
template<class k, class v>
struct AVLTreeNode
{
  //构造
  AVLTreeNode(const pair<k,v>& kv)
    :_left(nullptr)
    , _right(nullptr)
    , _parent(nullptr)
    ,_kv(kv)
    ,_bf(0)
  {}
 
  AVLTreeNode<k, v>* _left;
  AVLTreeNode<k, v>* _right;
  AVLTreeNode<k, v>* _parent;
  pair<k,v> _kv;
  int _bf;//平衡因子
};
 
template<class k, class v>
class AVLTree
{
public:
  typedef AVLTreeNode<k,v> Node;
  AVLTree()
    :_root(nullptr)
  {}
 
  bool Insert(const pair<k,v>& kv)
  {
    //插入新节点
    if (_root == nullptr)
    {
      _root = new Node(kv);
      return true;
    }
 
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur)
    {
      if (cur->_kv.first > kv.first)
      {
        parent = cur;
        cur = cur->_left;
      }
      else if (cur->_kv.first < kv.first)
      {
        parent = cur;
        cur = cur->_right;
      }
      else
      {
        return false;
      }
    }
    cur = new Node(kv);
    if (parent->_kv.first > kv.first)
    {
      parent->_left = cur;
    }
    else if (parent->_kv.first < kv.first)
    {
      parent->_right = cur;
    }
    else
    {
      assert(false);
    }
    cur->_parent = parent;
    //更新平衡因子
    while (parent)
    {
      //第一、二步:更新parent的平衡因子
      if (cur == parent->_left)
      {
        parent->_bf--;
      }
      else if (cur == parent->_right)
      {
        parent->_bf++;
      }
      //第三步:更新后parent的平衡因子==0
      if (parent->_bf == 0)
      {
        break;
      }
      //第四步:更新后parent的平衡因子==1 or ==-1
      else if (parent->_bf == 1 || parent->_bf == -1)
      {
        cur = parent;
        parent = cur->_parent;
      }
      //第五步:更新后parent的平衡因子==2 or ==-1
      else if (parent->_bf == 2 || parent->_bf == -2)
      {
        //旋转
        if (parent->_bf == 2 && cur->_bf == 1)
        {
          RotateL(parent);
        }
        else if (parent->_bf == -2 && cur->_bf == -1)
        {
          RotateR(parent);
        }
        else if (parent->_bf == 2 && cur->_bf == -1)
        {
          RotateRL(parent);
        }
        else if (parent->_bf == -2 && cur->_bf == 1)
        {
          RotateLR(parent);
        }
        break;
      }
      else
      {
        assert(false);
      }
    }
    return true;
  }
  //左单旋
  void RotateL(Node* parent)
  {
    Node* cur = parent->_right;
    Node* curleft = cur->_left;
 
    parent->_right = curleft;
    if (curleft)
    {
      curleft->_parent = parent;
    }
    cur->_left = parent;
    Node* pparent = parent->_parent;
    parent->_parent = cur;
    if (pparent == nullptr)
    {
      _root = cur;
      cur->_parent = nullptr;
    }
    else
    {
      if (pparent->_left == parent)
      {
        pparent->_left = cur;
      }
      else //if(pparent->_right == parent)
      {
        pparent->_right = cur;
      }
      cur->_parent = pparent;
    }
    parent->_bf = cur->_bf = 0;
  }
  //右单旋
  void RotateR(Node* parent)
  {
    Node* cur = parent->_left;
    Node* curright = cur->_right;
    parent->_left = curright;
    if (curright)
    {
      curright->_parent = parent;
    }
    cur->_right = parent;
    Node* pparent = parent->_parent;
    parent->_parent = cur;
    if (pparent == nullptr)
    {
      _root = cur;
      cur->_parent = nullptr;
    }
    else
    {
      if (pparent->_left == parent)
      {
        pparent->_left = cur;
      }
      else
      {
        pparent->_right = cur;
      }
      cur->_parent = pparent;
    }
    parent->_bf = cur->_bf = 0;
  }
  //左右旋转
  void RotateLR(Node* parent)
  {
    Node* cur = parent->_left;
    Node* curright = cur->_right;
    int bf = curright->_bf;
 
    RotateL(cur);
    RotateR(parent);
 
    if (bf == 0)
    {
      curright->_bf = 0;
      parent->_bf = 0;
      cur->_bf = 0;
    }
    else if (bf == 1)
    {
      curright->_bf = 0;
      parent->_bf = -1;
      cur->_bf = 0;
    }
    else if (bf == -1)
    {
      curright->_bf = 0;
      parent->_bf = 0;
      cur->_bf = 1;
    }
    else
    {
      assert(false);
    }
  }
  //右左旋转
  void RotateRL(Node* parent)
  {
    Node* cur = parent->_right;
    Node* curleft = cur->_left;
    int bf = curleft->_bf;
 
    RotateR(cur);
    RotateL(parent);
 
    if (bf == 0)
    {
      curleft->_bf = 0;
      parent->_bf = 0;
      cur->_bf = 0;
    }
    else if (bf == 1)
    {
      curleft->_bf = 0;
      parent->_bf = 0;
      cur->_bf = -1;
    }
    else if (bf == -1)
    {
      curleft->_bf = 0;
      parent->_bf = 0;
      cur->_bf = 1;
    }
    else
    {
      assert(false);
    }
  }
 
  int Height(Node* root)
  {
    if (root == nullptr)
    {
      return 0;
    }
    int leftHeight = Height(root->_left);
    int rightHeight = Height(root->_right);
 
    return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
  }
 
  bool IsBalance()
  {
    return IsBalance(_root);
  }
 
  bool IsBalance(Node* root)
  {
    if (root == nullptr)
    {
      return true;
    }
    int leftHeight = Height(root->_left);
    int rightHeight = Height(root->_right);
 
    if (rightHeight - leftHeight != root->_bf)
    {
      cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
      return false;
    }
    
    return abs(rightHeight - leftHeight) < 2
      && IsBalance(root->_left)
      && IsBalance(root->_right);
        
  }
private:
  Node* _root;
};


相关文章
|
11月前
|
算法
数据结构之博弈树搜索(深度优先搜索)
本文介绍了使用深度优先搜索(DFS)算法在二叉树中执行遍历及构建链表的过程。首先定义了二叉树节点`TreeNode`和链表节点`ListNode`的结构体。通过递归函数`dfs`实现了二叉树的深度优先遍历,按预序(根、左、右)输出节点值。接着,通过`buildLinkedList`函数根据DFS遍历的顺序构建了一个单链表,展示了如何将树结构转换为线性结构。最后,讨论了此算法的优点,如实现简单和内存效率高,同时也指出了潜在的内存管理问题,并分析了算法的时间复杂度。
269 0
|
7月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
157 3
 算法系列之数据结构-Huffman树
|
7月前
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
510 19
|
9月前
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
194 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
9月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
185 12
|
9月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
166 10
|
9月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
261 3
|
11月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
284 5
|
11月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
395 16
|
11月前
|
算法
数据结构之文件系统模拟(树数据结构)
本文介绍了文件系统模拟及其核心概念,包括树状数据结构、节点结构、文件系统类和相关操作。通过构建虚拟环境,模拟文件的创建、删除、移动、搜索等操作,展示了文件系统的基本功能和性能。代码示例演示了这些操作的具体实现,包括文件和目录的创建、移动和删除。文章还讨论了该算法的优势和局限性,如灵活性高但节点移除效率低等问题。
209 0