数据结构之二叉树构建、广度/深度优先(前序、中序、后序)遍历

这篇具有很好参考价值的文章主要介绍了数据结构之二叉树构建、广度/深度优先(前序、中序、后序)遍历。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、二叉树

1.1 树

说到树,我们暂时忘记学习,来看一下大自然的树:

二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
哈哈 以上照片是自己拍的,大家凑合看看

回归正题,那么在数据结构中,树是什么呢,通过上面的图片大家也可以理解

树是一种非线性的数据结构
像这样 ,有多个节点组成的有一定层次关系的结构,像是一颗相对于天空逆生长的一颗树,节点像是一片片树叶,黑色的线条像是树枝,而最顶上的那个节点像是树根,因此得名 树
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

2.2 二叉树

二叉树长什么样子呢?
来看一下大自然的 “二叉树”
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
百度出来的图
是不是下面这个样子
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

从根开始,开始分叉,每次只分两个叉,就这样一直分下去
二叉树有什么特点呢?

1.最顶上的那个节点叫做整棵树的 根节点
2.根据根节点可以将整棵树划分为 左右子树
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
3.叶子节点,像最末端的 8~15 ,没有继续分叉的,就是叶子节点了,只要还继续有分叉的不被叫做叶子节点,叶子没法分叉了对吧
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
4.Parents 双亲节点(父母节点) 像 1 就是 2 、3 的 双亲节点,有时候也叫父节点,或者说,相对于 4 5 来说,2 就像是根节点一样
5. 左孩子、右孩子 ,像 4 就是 2 的左孩子节点、5 是 2 的右孩子节点
6.层:4 层
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
7.树的高度:每加一层 代表树的高度 + 1 像上图树的高度就是 4
8.节点的度 ,这里的度指的是 “出度” 就是 一个 双亲节点 有几个 孩子节点 像 节点 2 的 度就是 2,而像之前说的 不是二叉树的树,像这样:节点 1 的出度就是 4
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

大概了解了二叉树的特点之后我们再来看一下下面这两个比较特殊的二叉树

2.2.1 满二叉树

也就是每一层的节点数都达到最大值,满满的没有空缺的,除了最后一层的叶子节点外,所有的节点都拥有两个孩子节点,就是下面这个样子
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

2.2.2 完全二叉树

完全二叉树,就是在满二叉树的基础上,允许每个节点不一定分两个叉,也就是说
满二叉树,除了叶子节点,其他的所有节点的度都是 2
完全二叉树,并不是所有的节点的度都是 2 ,但是 这一特定仅限定于倒数第二层的节点,例如:
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

允许 节点 9 的 度 为 1 ,每个节点的子节点(左孩子、右孩子)必须严格按照现有左再有右的顺序,
不允许
1.没有左孩子节点但是有右孩子节点,例如:
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

2.从上至下,按层往下,除了最后一层,其他层未达到最大节点数,例如:
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
总结:
满二叉树 从上到下,从左至右,从 0 ~ n 中间 不间断
完全二叉树:每一层(根节点为第 0 层)的节点数都满足 2^n 个

二、顺序存储构建二叉树

例如:给定一个vector 包含若干个元素,要求按照元素的顺序构建出一颗二叉树
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
刚开始可能没有思路,那我们先看一下二叉树的特点

1.需要存储当前节点的值
2.通过该节点可以找到左孩子、右孩子

通过上述两个特点,我们可以想象出二叉树每个节点的大概结构

1.data 存储节点的值
2.left 指针 ,访问该节点的左孩子节点
3.right 指针,访问该节点的右孩子节点

二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
代码则为:

struct TreeNode
{
	int val;
	TreeNode* left;
	TreeNode* right;
};

二叉树的节点类型确定了,那么我们如何将数组中的元素,构建成一个逻辑上的树形结构呢?
我们来分析一下数组元素下标与构建成功之后的二叉树的下标关系
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
通过分析二叉树中元素下标我们可以总结一下规律:
当前节点的元素下标为 n ,那么该节点的左孩子 节点元素下标为 2n + 1,右孩子节点元素下标为 2n+2;
例如:
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
现在将节点和数组元素的关系理清之后,接下来需要考虑如何将数组中的元素逐个,按照二叉树中从上至下,从左至右构建。

这里我们需要用到队列来控制从上至下、从左至右顺序;
通过入队(根、左、右)、取队头、出队,保持顺序,通过元素下标控制节点的值

	TreeNode* MakeBinaryTree(vector<int>& vctTemp)
	{
		if (vctTemp.empty())
		{
			cout << "vector is empty" << endl;
			return nullptr;
		}
		else
		{
			//先将根节点入队
			TreeNode* root = new TreeNode();
			root->val = vctTemp[0];
			root->left = nullptr;
			root->right = nullptr;
			queue<TreeNode*> quTemp;
			quTemp.push(root);
			//元素下标
			int n = 0;
			while (!quTemp.empty())
			{	
				//先取出队头元素
				TreeNode* nodeTemp = quTemp.front();
				quTemp.pop();
				//计算左孩子节点下标
				int leftIndex = 2 * n + 1;
				if (leftIndex <= vctTemp.size() - 1)
				{
					//构建左孩子节点
					TreeNode* leftnode = new TreeNode();
					leftnode->val = vctTemp[leftIndex];
					leftnode->left = nullptr;
					leftnode->right = nullptr;
					nodeTemp->left = leftnode;
					quTemp.push(leftnode);
				}
				//计算右孩子节点下标
				int rightIndex = 2 * n + 2;
				if (rightIndex <= vctTemp.size() - 1)
				{
					//构建右孩子节点
					TreeNode* rightnode = new TreeNode();
					rightnode->val = vctTemp[rightIndex];
					rightnode->left = nullptr;
					rightnode->right = nullptr;
					nodeTemp->right = rightnode;
					quTemp.push(rightnode);
				}
				//下标 + 1,每次仅出队一个元素
				n++;
			}

			return root;
		}
		
	}

三、常规遍历方法

3.1 广度优先遍历

广度优先遍历 英文简称为:BFS (Breadth-First-Search)
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
广度优先从上至下,从左至右依次进行遍历,这里是不是想到了我们构建的时候也是按照从上至下,从左至右的构建顺序,没错这种方法就是 BFS 广度优先遍历
按照构建的思路,我们同样需要一个队列来辅助我们进行顺序控制

图示:
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++
直到最后一个叶子节点 7 pop 出去,queue 为空循环结束,整个二叉树也遍历完成

代码示例:

	void BFS(TreeNode* root)
	{
		if (root == nullptr)
		{
			cout << " root is empty" << endl;
		}
		else
		{
			vector<int> vctResult;
			queue<TreeNode*> quTemp;
			//先将根节点入队
			quTemp.push(root);
			vctResult.push_back(root->val);
			while (!quTemp.empty())
			{
				//取队头
				TreeNode* nodeTemp = quTemp.front();
				quTemp.pop();
				//左孩子不为空,先将左孩子入队
				if (nodeTemp->left != nullptr)
				{
					quTemp.push(nodeTemp->left);
					vctResult.push_back(nodeTemp->left->val);
				}
				//右孩子不为空,再将右孩子入队
				if (nodeTemp->right != nullptr)
				{
					quTemp.push(nodeTemp->right);
					vctResult.push_back(nodeTemp->right->val);
				}
			}
			//打印元素
			Print(vctResult);
		}
	}

运行:
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

3.2 深度优先遍历

深度优先遍历不同于广度优先那样一层一层、从左至右遍历,而是按照前序或中序、或后序那样,从根节点不断的向下遍历,并将整棵树分左右子树,先将一颗子树遍历完(达到该子树的叶子节点)再去遍历另一棵树,因此被称为深度优先遍历。
深度优先遍历顺序又分为一下三种遍历方法:

以下是我整理的一篇二叉树的 前/中/后序 逻辑图形示例,不清楚逻辑的可以先看一下:
二叉树的前序/中序/后序遍历新手入门介绍

图示:

二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

3.2.1前序遍历

前序遍历:遍历顺序(先根、再左、再右) DLR

递归遍历代码为:

	void DLR(TreeNode* root)
	{
		if (root == nullptr)
		{
			return;
		}
		cout << root->val << " ";
		DLR(root->left);
		DLR(root->right);
	}

关于递归的思想我们这里以前序遍历做详细的步骤分析,帮助大家理解递归的思路和思想
二叉树广度优先遍历,数据结构,C++,数据结构,深度优先,算法,二叉树,C++

关于递归,大家可以有时候想不到递归函数如何去写,可以考虑以下三个方面,来思考递归怎么写

1.函数调用自身
2.函数终止条件
3.向终止条件前进的执行动作

例如:

	//递归函数本身
	void DLR(TreeNode* root)
	{
		//终止条件
		if (root == nullptr)
		{
			return;
		}
		cout << root->val << " ";
		//不断向叶子节点遍历,逼近 root == nullptr 条件
		//调用函数自身
		DLR(root->left);
		DLR(root->right);
	}

3.2.2 中序遍历

中序遍历:遍历顺序(先左、再根、再右)
递归遍历代码为:

	void LDR(TreeNode* root)
	{
		if (root == nullptr)
		{
			return;
		}
		LDR(root->left);
		cout << root->val << " ";
		LDR(root->right);
	}

3.2.3后序遍历

后序遍历:遍历顺序(先左、再右、再根)
递归遍历代码为:

	//后序遍历
	void LRD(TreeNode* root)
	{
		if (root == nullptr)
		{
			return;
		}
		LRD(root->left);
		LRD(root->right);
		cout << root->val << " ";
	}

完整测试代码如下:
BinaryTree.h

#pragma once
#include<iostream>
#include<vector>
#include<queue>
using namespace std;

struct TreeNode
{
	int val;
	TreeNode* left;
	TreeNode* right;
};

class BinaryTree
{
public:
	BinaryTree()
	{

	}
	~BinaryTree()
	{

	}
	TreeNode* MakeBinaryTree(vector<int>& vctTemp)
	{
		if (vctTemp.empty())
		{
			cout << "vector is empty" << endl;
			return nullptr;
		}
		else
		{
			TreeNode* root = new TreeNode();
			root->val = vctTemp[0];
			root->left = nullptr;
			root->right = nullptr;
			queue<TreeNode*> quTemp;
			quTemp.push(root);
			int n = 0;
			while (!quTemp.empty())
			{
				TreeNode* nodeTemp = quTemp.front();
				quTemp.pop();
				int leftIndex = 2 * n + 1;
				if (leftIndex <= vctTemp.size() - 1)
				{
					TreeNode* leftnode = new TreeNode();
					leftnode->val = vctTemp[leftIndex];
					leftnode->left = nullptr;
					leftnode->right = nullptr;
					nodeTemp->left = leftnode;
					quTemp.push(leftnode);
				}
				int rightIndex = 2 * n + 2;
				if (rightIndex <= vctTemp.size() - 1)
				{
					TreeNode* rightnode = new TreeNode();
					rightnode->val = vctTemp[rightIndex];
					rightnode->left = nullptr;
					rightnode->right = nullptr;
					nodeTemp->right = rightnode;
					quTemp.push(rightnode);
				}
				n++;
			}

			return root;
		}
		
	}
	void BFS(TreeNode* root)
	{
		if (root == nullptr)
		{
			cout << " root is empty" << endl;
		}
		else
		{
			vector<int> vctResult;
			queue<TreeNode*> quTemp;
			quTemp.push(root);
			vctResult.push_back(root->val);
			while (!quTemp.empty())
			{
				TreeNode* nodeTemp = quTemp.front();
				quTemp.pop();
				if (nodeTemp->left != nullptr)
				{
					quTemp.push(nodeTemp->left);
					vctResult.push_back(nodeTemp->left->val);
				}
				if (nodeTemp->right != nullptr)
				{
					quTemp.push(nodeTemp->right);
					vctResult.push_back(nodeTemp->right->val);
				}
			}
			cout << "BFS BinaryTree Result is:" << endl;
			Print(vctResult);
		}
	}
	//前序遍历
	void DLR(TreeNode* root)
	{
		if (root == nullptr)
		{
			return;
		}
		cout << root->val << " ";
		DLR(root->left);
		DLR(root->right);
	}
	//中序遍历
	void LDR(TreeNode* root)
	{
		if (root == nullptr)
		{
			return;
		}
		LDR(root->left);
		cout << root->val << " ";
		LDR(root->right);
	}
	//后序遍历
	void LRD(TreeNode* root)
	{
		if (root == nullptr)
		{
			return;
		}
		LRD(root->left);
		LRD(root->right);
		cout << root->val << " ";
	}
	void Print(vector<int> &vctTemp)
	{
		for (auto a : vctTemp)
		{
			cout << a << " ";
			a++;
		}
		cout << endl;
	}
private:
	vector<int> m_vector;

};

BinaryTree.cpp文章来源地址https://www.toymoban.com/news/detail-767861.html

#include"BinaryTree.h"
int main()
{
	cout << "please input 10 numbers" << endl;
	int n = 0;
	vector<int> vctTemp;
	while (n != 10)
	{
		int a;
		cin >> a;
		vctTemp.push_back(a);
		n++;
	}
	BinaryTree test;
	TreeNode* root = test.MakeBinaryTree(vctTemp);
	test.BFS(root);
	cout << "前序遍历结果为:" << endl;
	test.DLR(root);
	cout << endl;
	cout << "中序遍历结果为:" << endl;
	test.LDR(root);
	cout << endl;
	cout << "后序遍历结果为:" << endl;
	test.LRD(root);
	cout << endl;



	return 0;
}

到了这里,关于数据结构之二叉树构建、广度/深度优先(前序、中序、后序)遍历的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 数据结构之二叉树(Java)

    数据结构之二叉树(Java)

    在这里先说明一下,结点和节点其实一样的,无须关注这个。 1. 概念:树是一种非线性的数据结构,它是由n个有限节点组成一个具有层次关系的集合。 如上图所示,把此种数据结构称作树是因为它看起来像一个倒挂的树。  2. 特点 有一个特殊的节点,称为根节点,它是唯一

    2024年02月07日
    浏览(8)
  • 数据结构之二叉树(详细版)

    数据结构之二叉树(详细版)

            二叉树作为数据结构的一种,尤为重要,下面是对二叉树的详细讲解。想要了解二叉树,首先要了解 二叉树的基本概念,以及创建二叉树的结构,再深层点,遍历二叉树的前序中序和后续,其次是层序,后面将会讲解如何计算二叉树的高和叶结点 等等。        

    2024年02月03日
    浏览(13)
  • 数据结构之二叉树简介

    二叉树是一种非线性数据结构,代表“祖先”与“后代”之间的派生关系,体现了“一分为二”的分治逻辑。与链表相似,二叉树的基本单元是节点,每个节点包含值,左子节点的索引,右子节点的索引 当给定一个二叉树的节点时,我们将该节点的左子节点及其以下节点形成

    2024年02月01日
    浏览(18)
  • 数据结构之二叉树的实现

    数据结构之二叉树的实现

    目录 前言 1. 二叉树的遍历 1.1二叉树的前、中、后序遍历 1.2 层序遍历 2.二叉树的实现 2.1 二叉树的结构 2.2构建二叉树  2.2 前序遍历的实现 2.3 中序遍历的实现 2.4 后序遍历的实现 2.5 计算树的节点个数 2.6 计算树的深度 2.7 计算叶子节点个数 2.8 计算树第k层的节点数 2.9 以内容

    2023年04月10日
    浏览(11)
  • 《数据结构与算法》之二叉树(补充树)

    《数据结构与算法》之二叉树(补充树)

    二叉搜索树,也称二叉排序树或二叉查找树 二叉搜索树:一棵二叉树,可以为空,如果不为空,应该满足以下性质: 非空左子树的所有结点小于其根结点的键值 非空右子树的所有结点大于其根结点的键值 左右子树都是二叉搜索树 对于二叉树的查找,其实沿用的是分治法的

    2024年02月08日
    浏览(9)
  • 数据结构之二叉树的性质与存储结构

    数据结构之二叉树的性质与存储结构

      数据结构是程序设计的重要基础,它所讨论的内容和技术对从事软件项目的开发有重要作用。学习数据结构要达到的目标是学会从问题出发,分析和研究计算机加工的数据的特性,以便为应用所涉及的数据选择适当的逻辑结构、存储结构及其相应的操作方法,为提高利用

    2024年01月21日
    浏览(15)
  • 数据结构奇妙旅程之二叉树初阶

    数据结构奇妙旅程之二叉树初阶

    ꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶ 个人主页:xiaoxieʕ̯

    2024年01月19日
    浏览(14)
  • 数据结构之二叉树的数组表示

    若某节点的索引为 i ,则该节点的左子节点的索引为 2i+1 ,右子节点的索引为 2i+2 给定某节点,获取它的左右字节点,父节点 获取前序遍历,中序遍历,后序遍历,层序遍历

    2024年01月18日
    浏览(13)
  • 数据结构之二叉树(C语言附详细代码)

    数据结构之二叉树(C语言附详细代码)

    目录 一,树和二叉树 1.树 ①定义 ②关于树的一些概念 2.二叉树 ①定义 ②特殊的二叉树 ③二叉树的性质 ④二叉树的存储结构(顺序结构,只适用于完全二叉树) ⑤二叉树的遍历 二,二叉树操作代码 1.头文件 2.函数代码 ①创建二叉树 ②前序遍历二叉树 ③中序遍历二叉树 ④后序

    2024年02月01日
    浏览(12)
  • 数据结构初阶之二叉树的详细解析

    数据结构初阶之二叉树的详细解析

    个人主页:点我进入主页 专栏分类:C语言初阶      C语言程序设计————KTV       C语言小游戏     C语言进阶 C语言刷题       数据结构初阶    Linux 欢迎大家点赞,评论,收藏。 一起努力,共赴大厂。 目录 1.前言  2.二叉树各个功能代码实现 2.1二叉树结构体 2.2二叉

    2024年02月05日
    浏览(13)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包