【算法总结】二叉树(王道机试指南第三章)

我们从二叉树的遍历谈起。

众所周知,在对二叉树的遍历过程中,根据遍历每一个结点的左子树、结点本身、右子树的顺序不同可将对二叉树的遍历方法分为前序遍历、中序遍历、后序遍历。我们摒弃数据结构教科书上复杂的遍历方式,而是使用我们在上一章所重点讨论过的递归程序来简单的实现它。

假设二叉树结点由以下结构体表示:

struct Node
{
    Node *lchild;//指向其左儿子结点的指针,当其不存在左儿子时为NULL
    Node *rchild;//指向其右儿子结点的指针,当其不存在右儿子时为NULL
    /*
    *
    *
    其他节点信息的操作*/
 } ; 

我们以中序遍历为例,给出其遍历方法。

void inOrder(Node *Tree)
{
    if(Tree->lchild != NULL)inOrder(Tree->lchild);//递归遍历左子树
    /*
    *
    *
    对当前根结点做遍历操作*/
     if(Tree->rchild != NULL)inOrder(Tree->rchild);//递归遍历右子树
     return;
}

如上所见,用递归方式编写的二叉树遍历代码较原始使用堆栈来编写的相同功能代码,在代码量上得到巨大的优化。为了完成对二叉树的中序遍历,在遍历任意一个结点 Tree 时,我们首先递归遍历其左儿子及其子树,再遍历该结点本身,最后遍历其右儿子及其子树,从而完成对二叉树的中序遍历。 相同的,若我们需要其他两种形式的遍历方式,只需简单的修改遍历自身结点和递归遍历左右儿子的相关语句顺序即可。

 例 3.4 二叉树遍历

解题思路:

该例题涉及二叉树的建立、由二叉树的两种遍历结果还原二叉树、二叉树的遍历等多种知识点。我们以分析该例题为例,介绍关于二叉树各知识点。

由该例要求,首先我们需要根据给定的二叉树前序和中序遍历结果还原该二叉树。其次,我们需要将还原的二叉树以二叉树的形式保存在内存中。最后,我们需要对建立的二叉树进行后序遍历。

后序遍历在前文中已经有所提及。下面我们对前两部分做重点的阐述。

由给定的前序和中序遍历还原得到该二叉树。以前序遍历结果 XDAGFE, 和中序遍历结果 ADGXFE 为例详细讨论其还原方法。

由前序遍历结果的首个元素为 X 可知,原树必是由 X 为根结点。在中序遍历中,遍历结果 ADGXFE 以 X 为界分为两个子串。其中第一个子串 ADG 为 X 的左子树的中序遍历结果,第二个子串 FE 为 X 的右子树的中序遍历结果。这样我们知道 X 的左子树具有 3 个元素,X 的右子树具有 2 个元素。根据元素的数量我们同样可以得知,在先序遍历中去除根结点 X 后剩余的串 DAGFE 中,前 3 个字符 DAG 为 X 的左子树的前序遍历结果,后 2 个字符 FE 为 X 的右子树的前序遍历结果。

同样的对于确定的左子树前序遍历结果 DAG 和中序遍历结果 ADG 重复以上确定过程,可知 D 为该子树根结点,其左儿子为 A,右儿子为 G。

X 的右子树前序遍历结果 FE 和中序遍历结果 FE 同样可以确定该子树以 F 为根节点,其左儿子不存在,右儿子为 E。

这样我们就还原了原始二叉树。

我们还需将还原出来的树保存在内存中。使用结构体:

struct Node//树结点结构体
{
    Node *lchild;//左儿子指针
    Node *rchild;//右儿子指针
    char c;//结点字符信息
}Tree[50];//静态内存分配数组

表示树的一个结点,其字符信息保存在字符变量 c,若该结点存在左儿子或者右儿子,则指向他们的指针保存在 lchild 或 rchild 中,否则该指针为空。

下面给出本例代码,详细了解其实现。

#include<cstdio>
#include<cstring>

struct Node//树结点结构体
{
    Node *lchild;//左儿子指针
    Node *rchild;//右儿子指针
    char c;//结点字符信息
}Tree[50];//静态内存分配数组
int loc;//静态数组中已经分配的结点个数,方便确定下标

Node *creat() //申请一个结点空间,返回指向其的指针
{
    Tree[loc].lchild = Tree[loc].rchild = NULL;//初始化左右儿子为空
    return &Tree[loc++];//返回指针,且loc累加
}

char str1[30], str2[30];//保存前序和中序遍历结果的字符串

void postOrder(Node *T)//后序遍历
{
    if (T->lchild != NULL)postOrder(T->lchild);//左子树不为空,递归遍历左子树
    if (T->rchild != NULL)postOrder(T->rchild);//左子树不为空,递归遍历左子树
    printf("%c", T->c);//遍历该结点,输出其字符信息
}

Node *build(int s1, int e1, int s2, int e2)//由字符串的前序遍历和中序遍历还原原树,并返回其根节点,前序遍历结果由str[s1]到str1[e1],后序遍历结果为str2[s2]到str2[e2]
{
    Node *ret = creat();//为该树根结点申请空间
    ret->c = str1[s1];//该结点字符为前序遍历中的第一个字符,即根结点
    int rootidx;//前序根结点在中序中的位置编号
    for (int i = s2; i <= e2; i++)//查找该根结点字符在中序遍历中的位置,这样可以分成两个子树
    {
        if (str2[i] == str1[s1])
        {
            rootidx = i;
            break;
        }
    }
    if (rootidx != s2)//如果左子树不为空
    {
        ret->lchild = build(s1 + 1, s1 + (rootidx - s2), s2, rootidx - 1);//递归还原左子树
    }
    if (rootidx != e2)//如果右子树不为空
    {
        ret->rchild = build(s1 + (rootidx - s2) + 1, e1, rootidx + 1, e2);//递归还原右子树
    }
    return ret;//返回根结点指针,此时根结点是还原二叉树的根结点
}

int main()
{
    while (scanf("%s", str1) != EOF)
    {
        scanf("%s", str2);
        loc = 0;//初始化静态内存空间中已经使用结点的个数为0
        int L1 = strlen(str1);
        int L2 = strlen(str2);//计算两个字符串的长度
        Node *T = build(0, L1 - 1, 0, L2 - 1);//还原整棵树,其根结点保存在T中
        postOrder(T);//后序遍历
        printf("\n");//输出换行
    }
    return 0;
}

原文地址:https://www.cnblogs.com/yun-an/p/11070307.html

时间: 2024-10-07 20:26:21

【算法总结】二叉树(王道机试指南第三章)的相关文章

机试指南第六章-搜索-例题自解

枚举: 枚举是最简单也是最直白的搜索方式,它依次尝试搜索空间中所有的解,测试其是否符合条件,若符合则输出答案,否则继续测试下一组解. 例6.1 百鸡问题 #include<iostream> using namespace std; int main() { int n; while (cin >> n) { for (int x = 0; x <= 100; x++) { for (int y = 0; y <= 100 - x; y++) { int z = 100

计算机考研机试指南(六) ——栈

机试指南 cha 3 栈的应用 括号匹配问题 1 #include <iostream> 2 #include <stdio.h> 3 #include <algorithm> 4 #include <queue> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <string.h> 9 #include <std

华为机试 --- 求最大三位数

题目:输入10位0-9数字,取其中三位不同数字组合,求组合出来的最大三位数. 如输入 1 2 3 4 5 6 7 8 9 0,组合出来987最大. 测试代码如下: #include <stdio.h> #include <stdlib.h> int IsSame(int *a, int num); int main() { int i=0; int j=0; int a[10]={0}; int input =0; int length =0; int temp=0; for (i

机试指南第二章-经典入门-Hash的应用自解

Hash的应用: Hash即散列,不像数据结构与算法中讲的各种Hash方法和冲突处理等过多的阐述,以下主要介绍Hash在机试试题解答中的作用. 例2.5 统计同成绩学生人数 Hash解法AC代码:(一般想到的也是这种解法) #include<cstring> #include<iostream> using namespace std; int grade[105]; int main() { int n, m, index; memset(grade, 0, sizeof(gra

【算法】搞定[机试]算法刷题 全文超过80页pdf

目录 算法专题 一.树和图 1. 二叉树构造和遍历 2. 朋友圈 - 并查集 3. 公共朋友 - 非朋友圈 4. 哈夫曼树 5. 其他二叉树性质相关计算 6. 图的连通分量 7. 最小生成树 8. 单源最短路径 - dijkstra 二.枚举搜索 1. 按钮开关问题 2. 多层枚举问题 三.递归搜索 1. 简单递归 2. 递增排列组合类 3. 全排列问题 4. 草丛问题 5. 迷宫问题 6. 广度优先搜索的剪枝 7. 总操作步数固定枚举问题 四.数学问题 1. 高精度计算 2. 素数和质因子分解

机试指南第二章-经典入门-查找例题自解

查找: 对于查找问题,有难有易.可能只是直接地对某个数字的查找,也可能涉及搜索等相对难度更大的算法.这里先介绍查找的基础概念和方法. 例 2.9 找 x AC代码: #include<cstring> #include<iostream> using namespace std; int num[205]; int main() { int n, m, x; memset(num, 0, sizeof(num)); while (cin >> n) { bool fla

机试指南例2.1排序

题目描述 对输入的n个数进行排序并输出. 输入描述:     输入的第一行包括一个整数n(1<=n<=100).     接下来的一行包括n个整数. 输出描述:     可能有多组测试数据,对于每组数据,将排序后的n个整数输出,每个数后面都有一个空格.     每组测试数据的结果占一行. 示例1 输入 4 1 4 3 2 输出 1 2 3 4 代码2.1 使用冒泡排序 #include<stdio.h> int main() { int n; int buf[100]; while

机试指南第二章-经典入门-贪心例题自解

例2.11 FatMouse's Trade 解题思路 贪心策略.每次都买剩余物品中性价比(即重量价格比)最高的物品,直到该物品被买完或者钱耗尽.若该物品已经被买完,则我们继续在剩余的物品中寻找性价比最高的物品 AC代码 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; struct Thing { double j;

机试指南例2.2成绩排序

题目描述 有N个学生的数据,将学生数据按成绩高低排序,如果成绩相同则按姓名字符的字母序排序,如果姓名的字母序也相同则按照学生的年龄排序,并输出N个学生排序后的信息. 输入描述: 测试数据有多组,每组输入第一行有一个整数N(N<=1000),接下来的N行包括N个学生的数据. 每个学生的数据包括姓名(长度不超过100的字符串).年龄(整形数).成绩(小于等于100的正数). 输出描述: 将学生信息按成绩进行排序,成绩相同的则按姓名的字母序进行排序. 然后输出学生信息,按照如下格式: 姓名 年龄 成绩