数据结构——遍历二叉树

数据结构------树、二叉树基础概念-CSDN博客https://blog.csdn.net/wy_05136/article/details/163543888?spm=1001.2014.3001.5502

一、二叉树遍历原理

二叉树的遍历是指从根结点出发,按照某种次序访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。

二、二叉树结点定义

cpp 复制代码
/* 二叉链表有效结点 */
typedef int ElemType;
 
typedef struct BTNode
{
    ElemType data;             //数据域
    struct BTNode* leftchild;  //左孩子指针域
    struct BTNode* rightchild; //右孩子指针域
}BTNode;

三、二叉树遍历方法

(一)前序遍历(根---左---右)

1.遍历规则

若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。

2.遍历算法及代码实现

(1)递归版
cpp 复制代码
void preOrder(BTNode* root)
{
	if (root == NULL) return;

	//处理根结点
	printf("%c ", root->data);
	
	//递归处理左子树
	preOrder(root->leftchild);

	//递归处理右子树
	preOrder(root->rightchild);
}
(2)非递归版(单栈法)
cpp 复制代码
void preOrder_NoRecursion(BTNode* root)
{
	//0.判空,空树直接返回
	if (root == NULL) return;

    //1.申请一个栈,并将根节点入栈
	std::stack<BTNode*> st;
	st.push(root);

    //2.进入while循环,循环条件:栈不为空
	while (!st.empty())
	{
        //3.取出栈顶结点,并将其值打印处理
		BTNode* tmp = st.top();
		printf("%c ", tmp->data);
		st.pop();

        //4.将刚处理的tmp结点的两个孩子按照先右再左的顺序进行判断处理,如果存在则压入栈中
		if (tmp->rightchild != NULL)
		{
			st.push(tmp->rightchild);
		}
		if (tmp->leftchild != NULL)
		{
			st.push(tmp->leftchild);
		}
	}
    //5.当while结束,即栈空,遍历结束
}

(二)中序遍历(左---根---右)

1.遍历规则

若二叉树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。

2.遍历算法及代码实现

(1)递归版
cpp 复制代码
void inOrder(BTNode* root)
{
	if (root == NULL) return;

    //递归处理左子树
    inOrder(root->leftchild);

	//处理根结点
	printf("%c ", root->data);

	//递归处理右子树
	inOrder(root->rightchild);
}
(2)非递归版(单栈法)
cpp 复制代码
void inOrder_NoRecursion(BTNode* root)
{
	//0.判空,空树直接返回
	if (root == NULL) return;

	//1.申请栈存储节点,tag标记节点是否为第一次遇见
	bool tag = true;
	std::stack<BTNode*> st;
	st.push(root);

	//2.栈不为空持续循环遍历
	while (!st.empty())
	{
		//3.首次遇见节点且存在左子树,持续向左入栈,捋完所有左分支
		while (tag && st.top()->leftchild != NULL)
		{
			st.push(st.top()->leftchild);
		}

		//4.左子树处理完毕,访问当前根节点、出栈
		BTNode* tmp = st.top();
		printf("%c ", tmp->data);
		st.pop();

		//5.判断并处理右子树,更新标记位状态
		if (tmp->rightchild != NULL)
		{
			//有右孩子:右节点入栈,标记为首次访问
			st.push(tmp->rightchild);
			tag = true;
		}
		else
		{
			//无右孩子:当前节点分支遍历完毕,后续节点为回退二次访问
			tag = false;
		}
	}
	//6.栈空,整棵树中序遍历结束
}

(三)后续遍历(左---右---根)

1.遍历规则

若二叉树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。

2.遍历算法及代码实现

(1)递归版
cpp 复制代码
void postOrder(BTNode* root)
{
	if (root == NULL) return;

    //递归处理左子树
    postOrder(root->leftchild);

    //递归处理右子树
	postOrder(root->rightchild);

	//处理根结点
	printf("%c ", root->data);
}
(2)非递归版
法一:双栈法
cpp 复制代码
void postOrder_NoRecursion1(BTNode* root)
{
    //0.判空,空树直接返回
    if (root == NULL) return;

    //1.申请两个栈:S1用于遍历结点,S2用于逆序存储后序结果
    std::stack<BTNode*> S1;
    std::stack<BTNode*> S2;

    //2.根结点先入遍历栈S1
    S1.push(root);

    //3.S1不为空,持续遍历所有结点
    while (!S1.empty())
    {
        //4.S1栈顶结点出栈,存入结果栈S2(暂不打印)
        BTNode* tmp = S1.top();
        S2.push(tmp);
        S1.pop();

        //5.先左、后右入S1,保证后续S2出栈顺序为:左---右---根
        if (tmp->leftchild != NULL) S1.push(tmp->leftchild);
        if (tmp->rightchild != NULL) S1.push(tmp->rightchild);
    }

    //6.S1遍历完毕,S2中结点逆序输出即为后序遍历结果
    while (!S2.empty())
    {
        printf("%c ", S2.top()->data);
        S2.pop();
    }
}
法二:单栈法
cpp 复制代码
void postOrder_NoRecursion2(BTNode* root)
{
	//0.判空,空树直接返回
	if (root == NULL) return;

    ///1.申请一个栈,额外申请bool tag,再额外申请BTNode *preNode
	std::stack<BTNode*> st;
	st.push(root);
	bool tag = true;         //标记是否首次访问节点
	BTNode* preNode = NULL;  //记录上一个已访问的节点

	//3.进入while循环,循环条件是栈不空即可
	while (!st.empty())
	{
		//4.1 tag为true:栈顶是新节点,优先遍历左子树,捋完所有左分支
		while (tag == true && st.top()->leftchild != NULL)
		{
			st.push(st.top()->leftchild);
			tag = false;
		}
		//4.2 tag为false:栈顶是回溯老节点,左子树已处理完毕,准备处理右子树

		//5.判定右子树是否未被处理
		//右孩子存在且不是上一个访问节点 说明右子树未遍历
		if (st.top()->rightchild != NULL && st.top()->rightchild != preNode)
		{
			st.push(st.top()->rightchild);
			tag = true;
		}

		//5.2 右子树为空 / 右子树已处理完毕:左右处理完成,访问根节点
		else
		{
			//6.处理根节点:出栈打印,更新标记位与前驱结点
			BTNode* tmp = st.top();
			printf("%c ", tmp->data);
			st.pop();
			tag = false;
			preNode = tmp;
		}
	}
	//7.栈空,后序遍历结束
}

(四)层序遍历

1.普通

(1)遍历规则

若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。

(2)遍历算法及代码实现
cpp 复制代码
void Level_Traverse(BTNode* root)
{
	//0.判空,空树直接返回
	if (root == NULL) return;

	//1.申请队列,根结点入队
	std::queue<BTNode*> q;
	q.push(root);

	//2.队列不为空,持续循环遍历
	while (!q.empty())
	{
		//3.取出队头结点、访问数据、出队
		BTNode* tmp = q.front();
		printf("%c ", tmp->data);
		q.pop();

		//先左后右,子结点存在则入队,保证层序从左到右
		if (tmp->leftchild != NULL)
		{
			q.push(tmp->leftchild);
		}
		if (tmp->rightchild != NULL)
		{
			q.push(tmp->rightchild);
		}
	}
	//4.队列为空,遍历完成
}

2.正S

(1)遍历规则

若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在奇数层中,按从左到右的顺序对结点逐个访问,在偶数层中,按从右到左的顺序对结点逐个访问。

(2)遍历算法及代码实现
cpp 复制代码
void S_Level_Traverse(BTNode* root)
{
	//0.判空,空树直接返回
	if (root == NULL) return;

    //1.根结点(第一层/奇数层)入栈S1
	std::stack<BTNode*> s1, s2;
	s1.push(root);

	//2.两个栈任意一个非空,持续遍历
	while (!s1.empty() || !s2.empty())
	{
		//3.处理奇数层:S1非空、S2为空,从左向右打印
		while (!s1.empty())
		{
			BTNode* tmp = s1.top();
			printf("%c ", tmp->data);
			s1.pop();

			//先右后左入栈,保证下一层(偶数层)正序输出
			if (tmp->rightchild != NULL)
			{
				s2.push(tmp->rightchild);
			}
			if (tmp->leftchild != NULL)
			{
				s2.push(tmp->leftchild);
			}
		}

		//4.处理偶数层:S2非空、S1为空,从右向左打印
		while (!s2.empty())
		{
			BTNode* tmp = s2.top();
			printf("%c ", tmp->data);
			s2.pop();

			//先左后右入栈,保证下一层(奇数层)逆序输出
			if (tmp->leftchild != NULL)
			{
				s1.push(tmp->leftchild);
			}
			if (tmp->rightchild != NULL)
			{
				s1.push(tmp->rightchild);
			}
		}
	}
}

3.倒S

(1)遍历规则

若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在奇数层中,按从右到左的顺序对结点逐个访问,在偶数层中,按从左到右的顺序对结点逐个访问。

(2)遍历算法及代码实现
cpp 复制代码
void S_Level_Traverse(BTNode* root)
{
	//0.判空,空树直接返回
	if (root == NULL) return;

    //1.根结点(第一层/奇数层)入栈S1
	std::stack<BTNode*> s1, s2;
	s1.push(root);

	//2.两个栈任意一个非空,持续遍历
	while (!s1.empty() || !s2.empty())
	{
		//3.处理奇数层:S1非空、S2为空,从右向左打印
		while (!s1.empty())
		{
			BTNode* tmp = s1.top();
			printf("%c ", tmp->data);
			s1.pop();

			//先左后右入栈,保证下一层(偶数层)逆序输出
            if (tmp->leftchild != NULL)
			{
				s2.push(tmp->leftchild);
			}
			if (tmp->rightchild != NULL)
			{
				s2.push(tmp->rightchild);
			}
		}

		//4.处理偶数层:S2非空、S1为空,从左向右打印
		while (!s2.empty())
		{
			BTNode* tmp = s2.top();
			printf("%c ", tmp->data);
			s2.pop();

			//先右后左入栈,保证下一层(奇数层)正序输出
			if (tmp->rightchild != NULL)
			{
				s1.push(tmp->rightchild);
			}
			if (tmp->leftchild != NULL)
			{
				s1.push(tmp->leftchild);
			}

		}
	}
}
相关推荐
evans在进步17 分钟前
LeetCode 238 除自身以外数组的乘积:前缀积与后缀积详解
算法·leetcode·职场和发展
Brilliantwxx26 分钟前
【C++】初入嵌入式C++复习-----经典面试题100道
开发语言·c++·算法·面试·职场和发展
动词ing29 分钟前
【学习笔记】字符串(遍历+反转+替换+子串匹配+KMP)+题目解析
学习·算法
大模型码小白39 分钟前
MCP协议开发实战:从零搭建AI Agent工具链
运维·人工智能·算法·机器学习·自动化
luj_17681 小时前
断舍离中的项目管理智慧
c语言·开发语言·c++·经验分享·算法
老王爱玩车1 小时前
关于函数递归的优缺点分析
开发语言·数据结构·学习·算法
无限码力1 小时前
拼多多 春招&秋招 笔试真题题库目录|笔试题库 + 算法考点详解
算法·pdd笔试真题·pdd笔试真题题解·拼多多春招笔试真题·拼多多秋招笔试真题·拼多多笔试真题题解
OPEN-F1 小时前
C++进阶教程:文件流与字符串流
开发语言·c++·算法
lv__pf1 小时前
Mysql索引优化实战1【TL mysql4】
数据结构