数据结构——遍历二叉树

数据结构------树、二叉树基础概念-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);
			}

		}
	}
}
相关推荐
yi0113 小时前
LeetCode 219:存在重复元素 II——哈希表记录“最近一次出现的位置”
数据结构·人工智能·笔记·python·算法·leetcode·哈希表
灯澜忆梦6 小时前
【面向对象编程C++】| 基础语法
java·c++·算法
禾小西6 小时前
Redis 数据结构:快速的 Redis 有哪些慢操作?
数据结构·数据库·redis
小蒋观天下6 小时前
专项方案:大场景港口AI安防、多干扰环境下的算法调优与落地实操
人工智能·深度学习·算法·安全·机器学习·计算机视觉·ai大模型
Evand J6 小时前
【电机滤波例程5】渐消因子自适应扩展卡尔曼滤波(EKF),MATLAB。PMSM负载突变下的状态估计。附下载链接
算法·matlab
H.莓飛6 小时前
【数据结构】二叉树_OJ题
linux·开发语言·数据结构·算法
好想像大佬一样能够ak所有7 小时前
Leetcode35.搜索插入位置
数据结构·算法·leetcode
aqiu1111117 小时前
【C++算法打怪专栏】LeetCode 165. 比较版本号(双指针与字符串流法)
c++·算法·leetcode
黄金龙PLUS7 小时前
如何基于置换算法来设计对称加密算法
算法·网络安全·密码学·哈希算法·同态加密
不会就选b7 小时前
算法日常・每日刷题--<动态规划>14
算法