IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 二叉树的链式存储表示,创建、遍历等算法,数据结构,C++ -> 正文阅读

[数据结构与算法]二叉树的链式存储表示,创建、遍历等算法,数据结构,C++

1. 二叉树的链式存储表示。

2. 二叉树的创建、遍历等算法。

【基本要求】

1. 创建二叉树的链式存储表示。由二叉树的先序序列和中序序列创建二叉树;

2. 按树状打印二叉树。例如:假设每个结点所含数据元素均的单个字母,左下图二叉树印为右下形状。

?

3. 统计二叉树的叶子结点个数。

4. 给出二叉树的后序和层次序。

5. 输出二叉树中从根结点到所有叶子结点的路径。

?

【测试数据】

1. 先序序列:EBADCFHGIKJ,中序序列:ABCDEFGHIJK

2. 先序序列:ABDFCEGH,中序序列:BFDAGEHC

3. 先序序列:ABDEHCFIMGJKL,中序序列DBHEAIMFCGKLJ

  • 需求分析:包含题目要求,程序功能,运行方式,测试数据等

题目要求能够创建二叉树的链式存储表示,并由二叉树的先序序列和中序序列创建二叉树,需要创建BitNode结构体,char型的data数据,由主函数中输入相应的序列,由两个序列创建二叉树。

typedef struct BiTNode {

??? char data;

??? struct BiTNode *Lchild, *Rchild; //左右孩子指针

} BiTNode, *BiTree;

按树状打印二叉树:要根据二叉树的深度和元素个数来决定相应的打印方法,设置深度n,从深度0开始答应,用递归去打印树,由例图可知要先打印右子树,并设置相应的换行和制表符来作为空格。相当于R-T-L打印,按中序序列的逆序列打印树图。

????? 二叉树的叶子结点个数:设置static静态计数n,当没有左孩子也没有右孩子时即为叶子结点,计数+1,再递归遍历左右孩子,返回n,n即为叶子结点。

????? 层次序和后序要按树的相应特点去计算给出,同时要用到队列和容器。

typedef struct {

??? BiTree? *base;? // 动态分配存储空间

??? int? front;??? // 头指针,若队列不空,指向队列头元素

? ??int? rear;???? // 尾指针,若队列不空,指向队列尾元素的下一个位置

} SqQueue;

出二叉树中从根结点到所有叶子结点的路径需要用到容器或者栈,将存入容器或栈的元素当到达叶子结点时输出容器中的内容。

  • 概要设计:包含抽象数据类型定义,程序模块等

第一个模块定义BitNode结构体,和顺序队列SqQueue相应的内容,数据,左右孩子指针,头尾指针,创建需要用到的函数,InitQueue构造一个空队列Q;EmptyQueue判断队列是否空;GetHead返回队列Q的队头元素,不修改队头指针;EnQueue插入元素e为Q的新的队尾元素;DeQueue若队列不空,则删除Q的队头元素,否则退出程序报错。相应的需要用到的基本算法,再用这些算法去实现题目所要求的遍历算法和统计结点,路径。

程序的主函数模块用于输入树的相关信息,并运行相应的算法函数。输出正确的题目所要求的结果。定义序列为string方便输入序列,同时方便用length函数去获得序列长度(结点个数)。

而函数定义模块用来定义相应的算法:printBiTree打印树,countLeaf计算叶子结点,LevelOrderTraverse用于输出二叉树层次序列,AllPath用于输出跟到叶子结点的路径。CrtBT即是题目要求的由二叉树的先序序列和中序序列创建二叉树。用PostOrderTraverse输出二叉树的后序序列。

  • 详细设计:抽象数据类型以及程序模块的具体实现,算法设计思想

二叉树的先序序列和中序序列创建二叉树:设置pre[ps..ps+n-1]为二叉树的先序序列,in[is..is+n-1]为二叉树的中序序列,n为结点个数,需要先由search函数在中序序列中查询根,当中序序列中元素等于先序序列的第一个时,即返回树根,否则返回-1。树根等于is代表该左子树已递归完成,否则继续递归左子树(ps+1,中序首元素下标即为is,结点个数由k减is),树根等于is+n-1代表该右子树已递归完成,否则继续递归右子树(ps+1,右子树先序首元素下标等于ps+1减去k-is,中序直接加一,结点个数同理)。

按树状打印二叉树: 设置n标志深度,从深度0开始打印,用递归去打印树,由图知需要按R-T-L顺序去打印树,即先打印右子树,递归,深度加一,换行按深度打印制表符,打印数据,同理,再打印左子树。

????? 统计二叉树的叶子结点个数:设置静态的int static n做计数数据,当没有左孩子也没有右孩子即为叶子结点,计数+1,否则即是按递归遍历左右子树,最后返回n,即是叶子结点个数。

给出二叉树的后序和层次序:后序遍历较为容易,L-R-T即是先递归遍历左子树,再递归遍历右子树,再输出结点数据。而层次序遍历要用到队列和相关函数,设置一个顺序队列,初始化队列后,根结点入队,队列非空即代表该层次未遍历完成,访问结点,左右子树入队,出队。完成层次遍历,并输出相应结果层次序列。

输出二叉树中从根结点到所有叶子结点的路径:要使用容器或栈去输出结果,这里使用容器较为便利,先将结点数据压入容器,当到达叶子结点时,代表已经无左右孩子,就输出容器中的内容,否则就递归运行该算法,递归遍历左右子树直到遇到叶子结点,输出路径。

  • 调试分析:包括算法的时间复杂度和空间复杂度分析等

递归遍历二叉树,因为每个结点被访问一次,无论按什么次序遍历,对含n个结点的二叉树,时间复杂度均为O(n),辅助空间即树的深度,所以空间复杂度也为O(n)。

按树状打印二叉树同理,但因为需要按深度打印制表符,所以时间复杂度为O(n2),空间复杂度为O(n)。

按先序序列,中序序列构造二叉链表表示的二叉树需要递归遍历子树,还要用search函数去求中序序列中查询根,所以时间复杂度为O(n2),有两个序列的辅助空间,所以空间复杂度也为O(n2)。

统计叶子结点个数的算法较为简单,只使用了递归遍历,对含n个结点的二叉树,时间复杂度均为O(n),辅助空间即树的深度,所以空间复杂度也为O(n)。

层次遍历二叉树,因为需要用到队列,出队和入队,同时需要递归遍历左右子树,辅助空间较多,同时用到队列空间和树BitNode空间,空间复杂度为O(n2),时间复杂度为O(n)。

输出根结点到所有叶子结点的路径中,要用到栈或容器,同时要定义迭代器,输出容器中的内容,加上需要递归子树,所以时间复杂度为O(n2),空间复杂度也为空间复杂度为O(n2)。

调试数据的时候,没有正确打印二叉树,经过调试发现是没有按深度去换行,加上换行符之后正确换行,打印的树才正确。

在层次遍历二叉树时,没有正确的递归子树,递归了传入算法的树,让其入队,相当于重复入队其首结点并输出,导致层次序列一直输出首结点直到队列满,后来发现是需要在递归函数的参数中传入队头的子树,才能正确递归左右子树,这样才正确入队和出队,达到目的。

创建二叉树时,没有在else if递归里传入正确参数,导致树的创建错误,应该按照中序序列和先序序列的规律正确计算,is,in的值,并传入正确的参数去递归算法,计算正确首元素下标和得到子序列的树根,才能在二叉链表中正确创建树。

计算叶子结点时定义n是全局变量,封装性不够好,在递归时最好定义静态static的数据,这样保证了数据的封装性也保证了输出能够正确,同时不破坏程序的模块化。

  • 头文件及源程序

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <queue>
#include <vector>

using namespace std;

#define MAXQSIZE  100  //最大队列长度

typedef struct BiTNode {
    char data;
    struct BiTNode *Lchild, *Rchild; //左右孩子指针
} BiTNode, *BiTree;

typedef struct {
    BiTree  *base;  // 动态分配存储空间
    int  front;    // 头指针,若队列不空,指向队列头元素
    int  rear;     // 尾指针,若队列不空,指向队列尾元素的下一个位置
} SqQueue;

void InitQueue (SqQueue &Q) {
    // 构造一个空队列Q
    Q.base = new BiTree[MAXQSIZE];
    if(!Q.base) exit(0);             // 存储分配失败
    Q.front = Q.rear = 0;
}

bool EmptyQueue(SqQueue Q){//判断队列是否空
    return Q.front == Q.rear;
}

BiTree GetHead(SqQueue Q) {
    //返回队列Q的队头元素,不修改队头指针
    if ( Q.front != Q.rear )
        return Q.base[Q.front];
    else
        return 0;
}

void EnQueue(SqQueue&Q,BiTree e) {
    // 插入元素e为Q的新的队尾元素
    if((Q.rear+1) %MAXQSIZE ==Q.front){
        cout<<"队列满";
        exit(0); //队列满
    }
    Q.base[Q.rear] = e;
    Q.rear = (Q.rear+1) % MAXQSIZE;
}

void DeQueue (SqQueue&Q) {
    //若队列不空,则删除Q的队头元素,
    //否则退出程序报错
    if (Q.front==Q.rear)  exit(0);
    Q.front = (Q.front+1) % MAXQSIZE;
}

void PostOrderTraverse(BiTree BT) {
    // 后序遍历二叉树
    if (BT) {
        PostOrderTraverse(BT->Lchild); // 递归遍历左子树
        PostOrderTraverse(BT->Rchild); // 递归遍历右子树
        cout<<BT->data;                // 访问结点
    }
}

int Search(string in,char root){
    int n = 0;
    while (in[n]!=root) {//当中序序列中元素等于先序序列的第一个时,即返回树根
        if(in[n] == '\0')
            return -1;//否则返回-1
        n++;
    }
    return n;//返回树根
}

void CrtBT(BiTree &BT,string pre, string in, int ps, int is, int n) {
    //已知pre[ps..ps+n-1]为二叉树的先序序列,in[is..is+n-1]为二叉树的中序序列
    //本算法由此两个序列构造二叉链表表示的二叉树,n为结点个数
    int k;
    if (n==0) BT = NULL;
    else {
        k=Search(in, pre[ps]); // 在中序序列中查询根
        if (k==-1) BT=NULL; // 序列错误,返回空树
        else {
            BT=new BiTNode;
            BT->data = pre[ps];
            if (k==is) BT->Lchild = NULL;//树根等于is代表该左子树已递归完成,否则继续递归左子树
            else CrtBT(BT->Lchild, pre, in, ps+1, is, k-is);
            //ps+1,中序首元素下标即为is,结点个数由k减is
            if (k==is+n-1) BT->Rchild = NULL;//树根等于is+n-1代表该右子树已递归完成,否则继续递归右子树
            else  CrtBT(BT->Rchild, pre, in, ps+1+(k-is), k+1, n-(k-is)-1);
            //ps+1,右子树先序首元素下标等于ps+1减去k-is,中序直接加一,结点个数同理
        }

    } //if

} // CrtBT

void printBiTree(BiTree BT,int n){
    //n标志深度,从深度0开始打印,用递归去打印树
    if(BT){
        if(BT->Rchild){//先打印右子树
            printBiTree(BT->Rchild,n+1);//递归,深度加一,换行
            cout<<endl;
        }
        for(int i = 0;i<n;i++){
            cout<<"\t";//按深度打印制表符
        }
        cout<<BT->data;//打印数据
        cout<<endl;
        if(BT->Lchild){//同理,再打印左子树
            printBiTree(BT->Lchild,n+1);
            cout<<endl;
        }
    }
}

int countLeaf(BiTree BT){
    int static n = 0;
    if(BT){
        if((!BT->Lchild)&&(!BT->Rchild))
            n++;//没有左孩子也没有右孩子即为叶子结点,计数+1
        countLeaf(BT->Lchild);//递归遍历左孩子
        countLeaf(BT->Rchild);//递归遍历右孩子
    }
    return n;
}

void LevelOrderTraverse(BiTree T) {
    //层次遍历二叉树,输出二叉树层次序列
    SqQueue Q;
    InitQueue(Q);
    if(T==NULL) return;
    BiTree p = T;
    EnQueue(Q,p); // 根结点入队
    while(!EmptyQueue(Q) ) {
        cout<<GetHead(Q)->data; // 访问结点
        if(GetHead(Q)->Lchild) EnQueue(Q,GetHead(Q)->Lchild); // 左子树入队
        if(GetHead(Q)->Rchild) EnQueue(Q,GetHead(Q)->Rchild); // 右子树入队
        DeQueue(Q);//出队
    }
}

void AllPath(BiTree BT,vector<char> vct) {
    if (BT) {
        vct.push_back(BT->data);//将结点数据压入容器
        if ((!BT->Lchild)&&(!BT->Rchild)){//当到达叶子结点时(无左右孩子)
            for(vector<char>::iterator iter =vct.begin();iter !=vct.end();iter++){
                //定义迭代器iter,输出容器中的内容
                cout<<*iter;
            }
            cout<<endl;
        }     
        else {
            AllPath(BT->Lchild,vct);//递归遍历左子树
            AllPath(BT->Rchild,vct);//递归遍历右子树
        }
    } // if(T)
} // AllPath

int main()
{
    BiTree BT;
    string pre,in;
    int n;
    cout<<"输入先序序列:"; cin>>pre;
    cout<<"输入中序序列:"; cin>>in;
    n = pre.length();
    CrtBT(BT,pre,in,0,0,n);
    printBiTree(BT,0);
    cout<<endl<<"叶子结点个数为:"<<countLeaf(BT);
    cout<<endl<<"后序序列为:";
    PostOrderTraverse(BT);
    cout<<endl<<"层次序列为:";
    LevelOrderTraverse(BT);
    cout<<endl<<"根结点到所有叶子结点的路径:"<<endl;
    vector<char> v;//容器创建
    AllPath(BT,v);
    return 0;
}

?测试结果:提供试验结果和数据,测试所有操作结果的正确性

?测试数据一,可见所有结果输出正确。

测试数据二,可见所有结果输出正确?

?

?

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 01:36:44  更:2022-09-04 01:41:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 17:45:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码