二叉树及其相关例题

目录

1.树

1.树的基本概念

2.结点之间的的关系描述(还是看上面的图)

3.结点之间的属性描述

4.有序树和无序树

5.森林

6.遍历顺序

1.前序遍历:从根结点——>根结点左子树——>根结点的右子树(中    左    右)

2.中序遍历:左子树——>根——>右子树(左    中    右)

3.后序遍历:左子树——>右子树——>根(左    右    中)

4.层序遍历:一层一层的去遍历

2.二叉树

1.二叉树的种类

(1)满二叉树:满二叉树的任意节点,要么度为0,要么度为2.换个说法即要么为叶子结点,要么同时具有                      左右分支

(2)完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1至h-1) 的结点数都达到最大个                              数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

(3)二叉搜索树:设立结点便于搜索,结点具有一定顺序,搜索一个结点的时间复杂度为O(log n)                             级别

(4)平衡二叉搜索树:左子树和右子树的高度差的绝对值并不能大于1,其实上图也是一个平衡二叉                                    搜索树

2.二叉树的存储方式

1.链式存储:用指针去指向下一个结点(这种情况用的最多)

2.线式存储:用一个数组来保存二叉树,用一个字符数组来保存二叉树,下标可以去代替指针

3.二叉树的遍历顺序

1.深度优先搜索(递归法和非递归法:迭代,都可以处理)

2.广度优先搜索:

3.总结

4.相关例题

第一题:美国血统

第二题:新二叉树

5.寄语:(写给每一个正在低谷期努力的人)


1.树

1.树的基本概念

树是有n个结点的有限集(一种递归定义的数据结构)

当n=0时,说明这个树是一个空树

当树不是空树时,有以下特征

1.有且只有一个根节点

2.没有后继的结点是叶子结点(终端结点)

3.有后继的结点(除了根结点)都称之为分支结点(非终端结点)

4.除了根结点外,其余结点都有且仅有一个前驱

5.每个结点都有0个或者多个后继

子树的概念:子树的概念是相对于某一个结点而言的,子树也是树。

比如说,B延伸所形成的树就是A的子树,E延伸所形成的树,就是B的子树,所以说,子树没有绝对的概念,只不过是相对的

树的图解

2.结点之间的的关系描述(还是看上面的图)

1.祖先结点:相对于E来说上面的B,A都是祖先结点

2.父亲结点:即B,就是其上一个结点

3.孩子结点:即K,L指自己延伸出去的结点

4.兄弟结点;即F,指的是和自己来自于同一个结点的结点

5.堂兄弟结点:一般是GHIJ,有可能会将F加入进来

6.什么是两个结点之间的路径:从上面的结点到下面的结点的路径

7.什么是路径长度:就是判断经过了几条边

3.结点之间的属性描述

1.结点的层次(深度):(默认从1开始,但是有可能也是0)从上往下数:如图所示

2.结点的高度从下往上数;和深度相反,KML是高度为1,到A是高度为4

3.树的高度:总共几层;这里就是4

4.结点的度:有几个分支,比如说A的度就是3,B的度为2;

5.树的度:各结点度的最大值。比如说在下图就是3,各结点最大分支数就是3;’

4.有序树和无序树

有序树——从逻辑上看,树中的结点的各子树从左到右都是有次序的(有逻辑顺序),不能交换

比如说家谱的记录,在同一层,都是按出生先后来记录的,不能改变顺序

无序树——从逻辑上看,树中结点的各子树从左到右都是无次序的,可以交换

比如说中国行政区的列举,你先列举河北还是先列举山东,都是一样的无伤大雅,可以去改变顺序

所以说,一棵树到底是有序树还是无序树,主要看的就是这棵树存的是什么,是否结点的左右位置会反映某些逻辑关系

5.森林

森林是m棵互不相交的树的集合;

森林连上同一个根结点就是一棵树,当然了,允许有空树也就允许有空森林,当m=0的时候,这个森林就是一个空森林

6.遍历顺序

(因为二叉树的考频更高,所以这里的遍历顺序是以二叉树为模版的)

这里说的前,中,后序遍历其实底层逻辑就是递归,通过递归的方式遍历这棵树,这三种遍历方式都属于dfs(深度优先搜索),不了解的可以看这篇博客:第一周算法训练之深度优先搜索

1.前序遍历:从根结点——>根结点左子树——>根结点的右子树(中    左    右)
void qian(shu* root)//前序遍历
{
    if (root == NULL)
    {
        return;
    }
    printf("%d ", root->data);
    qian(root->left);
    qian(root->right);
}
2.中序遍历:左子树——>根——>右子树(左    中    右)
void zhong(shu* root)//中序遍历
{
    if (root == NULL)
    {
        return;
    }
    zhong(root->left);
    printf("%d ", root->data);
    zhong(root->right);
}
3.后序遍历:左子树——>右子树——>根(左    右    中)
void hou(shu* root)//后序遍历
{
    if (root == NULL)
    {
        return;
    }
    hou(root->left);
    hou(root->right);
    printf("%d ", root->data);
}

总和代码:

#include<stdio.h>
#include<stdlib.h>

typedef struct tree//创建树的结构体
{
    int data;//树的代表的元素
    struct tree* left;//左指针
    struct tree* right;//右指针
} shu;

// 新建结点
shu* jie(int x)
{
    shu* node = (shu*)malloc(sizeof(shu));
    node->data = x;
    node->left = NULL;
    node->right = NULL;
    return node;
}

// 创建一个二叉树,返回根结点指针
shu* chuang()
{
    shu* node1 = jie(1);
    shu* node2 = jie(2);
    shu* node3 = jie(3);
    shu* node4 = jie(4);
    shu* node5 = jie(5);
    shu* node6 = jie(6);

    // 构建二叉树的关系
    node1->left = node2;
    node1->right = node4;
    node2->left = node3;
    node4->left = node5;
    node4->right = node6;

    return node1;
}

// 前序遍历二叉树
void qian(shu* root)
{
    if (root == NULL)
    {
        return;
    }
    printf("%d ", root->data);
    qian(root->left);
    qian(root->right);
}

// 中序遍历二叉树
void zhong(shu* root)
{
    if (root == NULL)
    {
        return;
    }
    zhong(root->left);
    printf("%d ", root->data);
    zhong(root->right);
}

// 后序遍历二叉树
void hou(shu* root)
{
    if (root == NULL)
    {
        return;
    }
    hou(root->left);
    hou(root->right);
    printf("%d ", root->data);
}

int main()
{
    shu* root = chuang();
    qian(root); // 前序遍历
    printf("\n");
    zhong(root); // 中序遍历
    printf("\n");
    hou(root); // 后序遍历
    printf("\n");
    return 0;
}

运行结果:

4.层序遍历:一层一层的去遍历

其实这种遍历方式就相当于bfs广度优先搜索,是需要依靠队列来实现的一种算法 

void ceng(shu* root)//层序遍历
{
    Queue q; // 创建一个队列
    QueueInit(&q); // 初始化队列
    if (root) // 如果根节点存在
        QueuePush(&q, root); // 将根节点入队

    while (!QueueEmpty(&q)) // 当队列不为空时
    {
        shu* front = QueueFront(&q); // 获取队列的第一个元素(即树的结点)
        QueuePop(&q); // 将队列的第一个元素出队
        printf("%d ", front->data); // 打印出队的结点的数据

        if (front->left) // 如果左子节点存在
            QueuePush(&q, front->left); // 将左子节点入队
        if (front->right) // 如果右子节点存在
            QueuePush(&q, front->right); // 将右子节点入队
    }
    QueueDestory(&q); // 销毁队列
}

2.二叉树

1.二叉树的种类

(1)满二叉树:满二叉树的任意节点,要么度为0,要么度为2.换个说法即要么为叶子结点,要么同时具有                      左右分支

以下两个都是满二叉树

(2)完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1至h-1) 的结点数都达到最大个                              数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

(通俗来说就是除了底层以外,其它层都是满的(且从左到右需要连续)(这里主要看的就是p2与p3))

(3)二叉搜索树:设立结点便于搜索,结点具有一定顺序,搜索一个结点的时间复杂度为O(log n)                             级别

(左子树的所有结点都小于根节点,右子树的所有结点都大于根结点)

(4)平衡二叉搜索树:左子树和右子树的高度差的绝对值并不能大于1,其实上图也是一个平衡二叉                                    搜索树

2.二叉树的存储方式

1.链式存储:用指针去指向下一个结点(这种情况用的最多)

typedef struct tree//创建树的结构体
{
    int data;//树的代表的元素
    struct tree* left;//左指针
    struct tree* right;//右指针
} shu;

2.线式存储:用一个数组来保存二叉树,用一个字符数组来保存二叉树,下标可以去代替指针

假设数组第i个元素,2*i+1,代表左指针,2*i+2代表右指针。

ps:这种做法会非常局限一般不会用到。因为只适用于满二叉树,这就会让我们很被动,所以就不详细介绍了

3.二叉树的遍历顺序

1.深度优先搜索(递归法和非递归法:迭代,都可以处理)

包括前序遍历,中序遍历,后序遍历

(可以用栈去模拟递归,递归的实现本身就是用栈这种数据结构去实现的)

2.广度优先搜索:

包括层序遍历

3.总结

二叉树是一种基础的数据结构,我们应当去了解其分类,以及存储方式还有他的遍历顺序

同时我们也应当了解他的定义方式,要能够自己去定义一个二叉树,在二叉树章节中,我们会发现递归出现的频率非常高,所以在这里我也再次复习一下我们的递归三部曲

1.明白这个递归函数到底要干嘛,以及它的参数是什么

2.明白这个递归的结束条件

3.单层递归都需要去遍历哪些元素

4.相关例题

第一题:美国血统

题解:通过前序第一个可以确定根节点,然后在中序就可以判断出来左子树和右子树,然后一直递归,先调用左子树,将左子树的结点输出,在调用右子树,将右子树的结点输出,最后输出最后的根结点

AC代码:

#include<bits/stdc++.h>
using namespace std;
void dfs(string a,string b)
{
    if((int)b.size()==0)//如果前序遍历里的数组为空的话,说明遍历完了,该返回上一层了
    {
        return ;
    }
    int flag=a.find(b[0]);//找到根结点的坐标
    dfs(a.substr(0,flag),b.substr(1,flag));//遍历左子树
    dfs(a.substr(flag+1),b.substr(flag+1));//遍历右子树
    printf("%c",b[0]);//输出最后的根结点
}
int main()
{
    char x[30];//中序遍历
    char y[30];//前序遍历
    scanf("%s",x);
    scanf("%s",y);
    dfs(x,y);
    return 0;
}

第二题:新二叉树

题解:由于是顺序输出,且第一个就是根结点,我们只需要用深搜去一搜到底就可以,找出所有的情况,但是这个地方不需要回溯,只需要找出先后出现的就可以,所以在内部要先搜索左子树,再搜索右子树

AC代码:

#include<bits/stdc++.h>
using namespace std;

char c[27][4];
void dfs(char t,int n)
{
    if(t=='*')
    {
        return ;
    }
    for(int i=0;i<n;i++)
    {
        if(c[i][0]==t)
        {
            printf("%c",t);
            dfs(c[i][1],n);//搜索左子树
            dfs(c[i][2],n);//搜索右子树
        }
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%s",c[i]);
    }
    dfs(c[0][0],n);
    return 0;
}

5.寄语:(写给每一个正在低谷期努力的人)

你也走了很远的路吧

每个人在最悲伤的时候会突然消失一阵子

到底发生了什么,我来告诉你

世间降临了一场暴雨

天上所有的星星都自杀身亡

但你要相信从此以后晴空万里

耐心点 你会做到的

那些曾经最孤单 最无望 最失落,最安静的经历

其实都不是你最潦倒的时光

你憋着这口气,最终能独自上岸

你耐住了孤单 扛过了无望,不再怕失落,也学会与安静为伴

虽然疼都是别人给的,但伤都是自己好的

你要忍,忍到春暖花开

你要走,走到灯火通明

你要看过世界辽阔 再评判是好是坏

你要铆足劲变好,再站在不敢想象的人身边 ,旗鼓相当

你要变成想象中的样子,这件事,一步都不能让

愿我们都有足够的运气与勇气,去遇见命里不同的风

愿你的世界,星光满载,初心不改,走过汹涌的人潮,历经生活的磨难,终遇良人

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值