【数据结构】定义二叉树 | 二叉树前序遍历 | 二叉树中序遍历详解

220 阅读6分钟

​一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情。​

前言:

本章将会详细讲解二叉树遍历的四种方式,分别为前序遍历、中序遍历、后续遍历和层序遍历。在学习遍历之前,会先带大家回顾一下二叉树的基本概念。学习二叉树的基本操作前,需要先创建一颗二叉树,然后才能学习其相关的基本操作,考虑到我们刚刚接触二叉树,为了能够先易后难地进行讲解,我们将暂时手动创建一颗简单的二叉树,用来方便大家学习。等二叉树结构了解的差不多后,后期我们会带大家研究二叉树地真正的创建方式。

Ⅰ. 定义二叉树

0x00 二叉树的概念(回顾)

我们之前讲过二叉树的概念了,这里我们简单的回顾下二叉树的概念。

🔗 复习链接:【数据结构】二叉树的概念 | 满二叉树和完全二叉树 | 二叉树的基本性质

❓ 二叉树是什么?① 空树 ② 非空:根节点、根节点的左子树与根节点的又子树组成的。

🔑 解读:从概念中我们不难看出,二叉树的定义是递归式的。因此后续基本操作中,我们基本都是按照该概念来实现的!我们可以来看一下,我们不去看 A,我们来看 A 的左子树,把 B 看作为根节点,是不是一颗二叉树?

🔺 所以,我们可以通过采用递归的手法来玩二叉树。

0x00 定义二叉树

💬 BinaryTree.h:

typedef int BTDataType;              

typedef struct BinaryTreeNode {
	struct BinaryTreeNode* left;       // 记录左节点
	struct BinaryTreeNode* right;      // 记录右节点
	BTDataType data;                   // 存储数据
} BTNode;                         

🔑 解读:

① 还是老规矩,我们创建一个二叉树的数据类型 BTDataType 。

② 由于是链式二叉树,根据二叉树的概念我们定义出 leftright 来分别记录根节点的左子树与根节点的右子树,再创建一个变量来存储节点中的数据即可。

③ 最后为了方便表达,我们将这个结构体 typedef 为 BTNode,因为 "struct BinaryTreeNode" 比较麻烦。

0x01 手动创建二叉树

在学习二叉树的基本操作前,需要先创建一颗二叉树,然后才能学习其相关的基本操作。由于我们刚刚接触二叉树,为了能够先易后难地学习,我们手动创建一颗简单的二叉树来来方便大家学习。等二叉树结构了解后,后期我们会带着读者研究二叉树地真正的创建方式。

💬 手动创建一颗二叉树(以上图为例来创建)

/* 创建新节点 */
BTNode* BuyNode(BTDataType x) {
	BTNode* new_node = (BTNode*)malloc(sizeof(BTNode));
	if (new_node == NULL) {
		printf("malloc failed!\n");
		exit(-1);
	}
	new_node->data = x;
	new_node->left = new_node->right = NULL;

	return new_node;
}

/* 手动创建二叉树 */
BTNode* CreateBinaryTree() {
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');

	nodeA->left = nodeB;
	nodeA->right = nodeC;
	nodeB->left = nodeD;
	nodeC->left = nodeE;
	nodeC->right = nodeF;

	return nodeA;
}

int main(void)
{
	BTNode* root = CreateBinaryTree();
}

🔑 画图解析:


0x00 关于遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历,就是按照某种特定的规则,一次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。 访问节点所做的操作要看具体的应用问题。遍历是二叉树上最重要的运算之一,也是二叉树上进行其他运算的基础。

📚 二叉树遍历(Traversal):沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。 按照规则,二叉树的遍历有:前序 / 中序 / 后序 的递归结构遍历。除了前序、中序和后续遍历外,我们还可以对二叉树进行层序遍历

0x01 二叉树前序遍历

📚 前序遍历(Preorder Traversal):访问根节点的操作发生在遍历其右子树之前。

即,首先访问根结点,然后遍历左子树,最后遍历右子树。

💬 代码实现前序遍历:

(这里我们用 Ø 符号来表示 NULL)

/* 二叉树前序遍历 */
void PreOrder(BTNode* root) {
	/* 首先判断根是否为空,为空就返回 */
	if (root == NULL) {        
		printf("Ø ");	// 暂时打印出来,便于观察	   
		return;				   
	}

	/* 走到这里说明不为空,根据前序遍历,先访问根节点 */
	printf("%c ", root->data);

	/* 然后遍历左子树(利用递归) */
	PreOrder(root->left);

	/* 最后遍历右子树(利用递归) */
	PreOrder(root->right);	                  
}

🔑 解读:

① 首先判断根是否为空,如果根为空,则返回。这里为了表示,我们把空节点以 " Ø " 打印出来。

② 如果跟不为空,这说明有数据。由于是前序遍历(Preorder),前序遍历是先访问根节点,然后遍历左子树,最后再遍历右子树。所以,我们这里先要访问的是根节点,我们把根节点的数据打印出来。

③ 然后我们需要遍历左子树,这时我们利用递归就可以实现。将根节点 root 的左数 left 传入 PreOrder 函数(将其左树看作根),一直递归下去,直到碰到 root == NULL 则返回。

④ 最后,遍历完左子树后遍历右子树。利用递归,方法同上。

0x02 二叉树中序遍历

📚 中序遍历(Inorder Traversal):访问根节点的操作发生在遍历其左右子树之中。

即,首先遍历左子树,然后访问根结点,最后遍历右子树。

/* 二叉树中序遍历 */
void InOrder(BTNode* root) {
	/* 首先判断根是否为空,为空就返回 */
	if (root == NULL) {
		printf("Ø ");  // 暂时打印出来,便于观察
		return;
	}

	/* 走到这里说明不为空,根据中序遍历,先遍历左子树 */
	InOrder(root->left);

	/* 然后访问根节点(利用递归) */
	printf("Ø ", root->data);

	/* 最后遍历右子树(利用递归) */
	InOrder(root->right);
}

🔑 解读:

① 首先判断根是否为空,如果根为空,则返回。

② 如果跟不为空,这说明有数据。由于是中序遍历(Inorder),中序遍历是先遍历左子树,然后访问根节点,最后遍历右子树。

参考资料:

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. baike.baidu.com/.

📌 笔者:王亦优

📃 更新: 2022.1.12

❌ 勘误: 无

📜 声明: 由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!

本篇完。