数据结构(树和二叉树 Part1)

数据结构(树和二叉树 Part2 树的创建):https://blog.csdn.net/qq_41605114/article/details/104690836 

目录

4树和二叉树

4.1树的基本概念

4.2.1图形表示法

4.2.2广义表表示法

4.2.3左孩子右兄弟表示法

4.3二叉树(数据结构更好处理)

4.3.1二叉树基本概念

4.3.2二叉树的遍历

遍历定义

遍历用途

遍历方法

程序部分 (二叉树)

先序 

 中序:

后序:

二叉树叶子节点的个数计算

计算树的高度

树的拷贝

树的释放

 


4树和二叉树

4.1树的基本概念

树的定义:

由一个或多个(n>=0)结点组成的有限集合T,有且仅有一个结点称为根(root)

当n>1时,其余的结点分为m(m>=0)个互相不相交的有限集合

T1,T2,T3..........Tm。

每个集合本身又是棵数,被称作这个根的子数。

树的结构特点:

  • 非线性结构,有一个直接前驱,但可能有多个直接后继(1:n)
  • 树的定义具有递归性,树中还有树
  • 树可以为空,即节点个数为零

相关术语:

  • 根:即根节点(没有前驱)
  • 叶子:即终端结点(没有后继)
  • 森林:指m棵不相交的树的集合(例如删除A后的子数个数)
  • 有序树:结点各子树从左到右有序,不能互换(左为第一)
  • 无序书:结点各子树可互换位置
  • 双亲:即上层的那个结点(直接前驱parent)
  • 孩子:即下层结点的子树(直接后继child)
  • 兄弟:同一双亲下的同层结点(孩子们之间互称兄弟)sibling
  • 堂兄弟:即双亲位于同一层的结点(但并非同一双亲)cousin
  • 祖先:即从根到该结点所经分支的所有结点
  • 子孙:即该结点下层子树中的任一结点
  • 结点:即树的数据元素
  • 结点的度:结点挂接的子树的个数(有几个直接后继就是几度)
  • 结点的层次:从根到该结点的层数(根结点算第一层)
  • 终端结点:即度为0的结点,即叶子
  • 分支结点:除树根以外的结点(也称为内部结点)
  • 树的度:所有结点度中的最大值(Max(各结点的度))
  • 树的深度(或高度):指所有结点中最大的层数(Max(各结点的层次))

如下图:

结点数:13

树的度:3

树的深度:4

                                               

4.2.1图形表示法

                                     

4.2.2广义表表示法

                                                       

4.2.3左孩子右兄弟表示法

                                                      
将上述多叉数转换为二叉树

顾名思义,左侧是孩子,右侧是孩子的兄弟

4.3二叉树(数据结构更好处理)

4.3.1二叉树基本概念

定义:

n(n>=0)个结点的有限集合,由一个根结点以及两颗互不相交(孩子个数不多于2的,分别称为左子树和右子树的二叉树组成。

逻辑结构:

一对二(1:2)

基本特征:

每个结点最多只有两颗子树(不存在度大于2的结点)

左子树和右子树次序不能颠倒(有序数)

基本形态:

二叉树性质:

性质1:在二叉树的第i层上至多有2i-1个结点(i>0)

性质2:深度为K的二叉树至多有2的K-1个结点(k>0)

性质3:对于任何一颗二叉树,若度为2的结点数有n2个,则叶子数(n0)必定为n2+1(n0 = n2 +1)

概念解释:

满二叉树

一颗深度为K且有2的k-1个结点的二叉树。

完全二叉树(数据结构堆——就是一种完全二叉树)

除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。

理解:k-1层与满二叉树完全相同,第k层结点尽力靠左

性质4:具有n个结点的完全二叉树的深度 | log2n | + 1

性质5:对完全二叉树,若从上至下,从左至右编号(从1开始编号),则编号为i的结点,其左孩子编号必须是2i

其右孩子编号必须是2i+1,其双亲的编号必为i/2(i = 1时为根,除外)

                                                       

                                                       如果不行,就把普通的树转换为完全二叉树 

                                                           

4.3.2二叉树的遍历

遍历定义

指按某条搜索路线遍访每个结点且不重复(又称周游)

遍历用途

它是树结构插入、删除、修改、查找和排序运算的前提,是二叉树一切运算的基础和核心

遍历方法

牢记一种约定:对每个结点的查看都是“先左后右”

限定先左后右,树的遍历有三种实现方案:

DLR:先(根)序遍历  先序遍历,即先根再左再右

LDR:中(根)序遍历  中序遍历,即先左再根再右

LRD:后(根)序遍历  后序遍历,即先左再左再根

注意:“先,中,后”的意思是指访问的结点D是先于子树出现还是后于子树出现。

从递归的角度看,这三种算法是完全相同的,或者说这三种遍历算法的访问路径是相同的

只是访问结点的时机不同。

以上图中的树作为实例

在访问的过程中,一旦在排序过程中碰见树,再次用排序原则进行排序。

                   

程序部分 (二叉树)

以上图中的树为例,用程序进行以下一系列操作: 

以上排序过程其实就是一个不断递归的过程

其递归调用的就是遍历的法则

先序 

.h 

struct BiNode
{
    QString ch;
    BiNode * lchild;//指向右孩子
    BiNode * rchild;//指向左孩子
};
//二叉树的递归遍历

void recursion(BiNode * root);

.cpp

void recursion(BiNode * root)
{
    if(nullptr == root)
        return;
    qDebug()<<"序号:"<<root->ch;
    //递归遍历左子树
    recursion(root->lchild);
    //递归遍历右子树
    recursion(root->rchild);
}

首先对输入的根节点进行判断,如果是空,直接return

因为是先序遍历,所以就先输出根节点的内容,之后再递归左右孩子

整个递归过程中,返回的标注就是:根结点为空,即返回,如此不断地递归

输出:

 中序:

.cpp

//先序遍历
void recursion(BiNode * root)
{
    if(nullptr == root)
        return;
    //递归遍历左子树
    recursion(root->lchild);
    qDebug()<<"序号:"<<root->ch;
    //递归遍历右子树
    recursion(root->rchild);
}

后序:

.cpp

void recursion(BiNode * root)
{
    if(nullptr == root)
        return;
    //递归遍历左子树
    recursion(root->lchild);
    //递归遍历右子树
    recursion(root->rchild);
    qDebug()<<"序号:"<<root->ch;

}

二叉树叶子节点的个数计算

.cpp

void leafpointNum(BiNode * root)
{
    if(nullptr == root)
        return;
    if(nullptr == root->lchild&&nullptr == root->rchild)
        num++;
    leafpointNum(root->lchild);
    leafpointNum(root->rchild);

}
    leafpointNum(&nodeA);
    qDebug()<<"Num:"<<num;

输出:

计算树的高度

思路:看图,A的高度  = MAX(左子树高度,右子树高度) + 1(即自身高度);

//树的高度
int getTreeHigh(BiNode * root)
{
    if(nullptr == root)
        return 0;
    int Lheight = getTreeHigh(root->lchild);
    int Rheight = getTreeHigh(root->rchild);
    int max = Lheight>Rheight ? Lheight+1:Rheight+1;
    return max;
}

如此不断的递归,就得到了结果

qDebug()<<getTreeHigh(&nodeA);

                                  

树的拷贝

思路:先拷贝B,再拷贝F,最后拷贝A,将A的指针指向B和F

先拷贝孩子,再拷贝父亲,最后将父亲的指针指向孩子

//树的拷贝
BiNode * copyTree(BiNode * root)
{
    if(nullptr == root)
        return nullptr;
    BiNode * Lcoyeresult = copyTree(root->lchild);
    BiNode * Rcoyeresult = copyTree(root->rchild);
    BiNode * node = new BiNode;
    node->ch = root->ch;
    node->lchild = Lcoyeresult;
    node->rchild = Rcoyeresult;

    return node;
}

操作:

    BiNode nodeA = {"A",nullptr,nullptr};
    BiNode nodeB = {"B",nullptr,nullptr};
    BiNode nodeC = {"C",nullptr,nullptr};
    BiNode nodeD = {"D",nullptr,nullptr};
    BiNode nodeE = {"E",nullptr,nullptr};
    BiNode nodeF = {"F",nullptr,nullptr};
    BiNode nodeG = {"G",nullptr,nullptr};
    BiNode nodeH = {"H",nullptr,nullptr};

    nodeA.lchild = &nodeB;
    nodeA.rchild = &nodeF;

    nodeB.lchild = nullptr;
    nodeB.rchild = &nodeC;

    nodeC.lchild = &nodeD;
    nodeC.rchild = &nodeE;

    nodeF.lchild = nullptr;
    nodeF.rchild = &nodeG;

    nodeG.lchild = &nodeH;
    nodeG.rchild = nullptr;

    recursion(&nodeA);
    leafpointNum(&nodeA);
    qDebug()<<"Num:"<<num;

    qDebug()<<getTreeHigh(&nodeA);

    BiNode * nodecopyA = copyTree(&nodeA);
    recursion(nodecopyA);

输出内容:

树的释放

从最底层,及叶子结点开始释放,同样是递归的思路

//树的释放
void FreeTree(BiNode * root)
{
    if(nullptr == root)
        return;
    FreeTree(root->lchild);
    FreeTree(root->rchild);
    delete root;

}

 

发布了85 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41605114/article/details/104377414