二叉树的遍历,前序中序后序,递归与非递归,c/c++描述

  首先推荐bilibili的优秀up主“懒猫老师”,她的关于二叉树遍历的视频,使我入门了,视频链接如下:懒猫老师-数据结构-(33)二叉树遍历的非递归算法
https://www.bilibili.com/video/BV12g4y187oZ?t=80
  接着是我自己的一点学习总结。二叉树遍历的递归算法很简单,几行就完事。前序:根左右,DLR;中序左根右LDR;后序左右根LRD。D这里指的是data,根节点的数据成员,LR则指的是根节点的左右指针成员。但递归算法的效率不是最高,频繁调用系统提供的栈,保存被调用函数的状态信息,包括执行位置、调用发生时的被调用函数变量的值。待调用结束后,程序返回被调函数,从栈中取得被调函数的状态信息,继续执行下去。 所以,我们也可以在程序中创建自己的栈,不用递归方法遍历二叉树,但这么做的前提是,你对递归调用的函数执行流程也已非常熟悉。
  先序遍历:先访问根节点,再访问左子树,再访问右子树。
  中序遍历:先访问左子树,再访问根节点,再访问右子树。
  后序遍历:先访问左子树,再访问右子树,再访问根节点。
  二叉树链表保存的是节点之间的纵向关系,由祖先节点到父母节点,到子孙节点,所以纵向访问很方便。但横向访问、依次访问同一行的所有节点,就很困难,由左子节点到访问右子节点就很困难。假如一个指针在二叉树里游走遍历的话,当它越过根节点,指向左子节点后,就再也不可能指向右子节点了。因为单向链表,指针只能前进,不能后退。所以,如果我们要实现二叉树里横向访问的功能,我们就要借助于数组等有记忆保存功能的变量,来保存指针访问过的根节点,比如用顺序栈来配合指针遍历二叉树。
  前序非递归遍历的思路:首先入栈根节点,访问完根节点后,弹出栈顶根节点,再按照先右子节点再左子节点的顺序,入栈其两个子节点,再从栈顶开始访问,就是按照先左子节点再右子节点的先序顺序访问二叉树节点。这三个节点访问完后,相当于访问完了栈里更底层根节点的左子树或者右子树。而这个栈里更底层的根节点,也已经在早些时候入栈出栈访问过了。由函数preOrderNoRecusion完成,前序递归函数preOrderRecursion。前序非递归遍历是最简单的,因为访问到根节点,就输出其值再弹出,整个栈的深度较浅,不会太深,分析起来逻辑也比较容易。跟后面俩相比。
  中序非递归遍历的思路:因为我们要按照左子节点,根节点,右子节点的顺序访问。所以即使指针指向了某个节点,我们也不能将其访问输出,而是要先访问其左子节点。所以我们要把根节点和左子节点都入栈,再从栈顶开始访问,并逐个弹出栈顶节点。这样就实现了先左再根的顺序。栈里的每个节点既是其下层节点的左子节点,又是其上层左子节点的根节点。栈顶节点则以空节点null为其左子节点。这时候才可以访问输出栈顶节点,其相当于根节点。而且其右子节点不一定为空。当栈顶节点的右子节点不为空时,右子节点也不能直接访问,而是要视其为其子树的根节点,入栈,直至栈顶元素的左子节点为空null为止。然后我们再弹出输出这个右子节点或者右子树。至此完成了一个左根右的完整循环。由函数inOrderNoRecursion完成,中序递归函数inOrderRecursion。文字其实不好理解,代码写出来也未必这么长。只是详细叙述了程序执行的要点。读者直接看代码,或许比这一段文字好理解。而且我们不是每次都要把左子节点非空的节点入栈,只有是从根节点到叶节点的从上到下的访问路径时,才要进栈节点,从叶节点弹出输出栈顶元素时,访问方向是从下往上,就不必进栈已访问过的节点了,要不就死循环了,由逻辑量bool checkLeftChild控制这个选择。
  后序非递归遍历的思路:先输出左子树,再输出右子树,再输出根节点。所以,即使指针指向了根节点,也不能访问其数据。应该在入栈根节点后,入栈其所有的左子节点的左子节点,直到栈顶节点的左子节点为null。栈里的每个节点,既是其下层根节点的左子节点,也是其上层左子节点的根节点。我们还要考察栈顶节点的右子节点是否存在。若其右子节点存在,则将右子节点作为根节点入栈,并入栈其所有的左子孙节点。依次类推。若栈顶节点的右子节点为空,则可以输出栈顶这个叶子节点。否则应先输出其右子节点。再输出栈顶节点。而至此,我们已输出了栈里更底层根节点的左子树或者右子树。区别依据是刚刚弹出的栈顶元素是否是新栈顶元素的右子节点,由指针*ptPopped保存最近一次弹出的节点地址。是栈顶节点的右子节点的话,继续弹出并访问栈顶根节点;否的话,若右子节点非空,就继续查找并入栈右子节点。依次类推,入栈或者出栈,直到整个栈为空。至此则表示,整个二叉树已访问完毕。由函数postOrderNoRecursion完成,后序递归函数postOrderRecursion。
  谢谢阅读。接着给出完整代码。main函数所在源文件代码如下:

#include<iostream>
#include<stdio.h>
using namespace std;
#define STACKDEPTH 15
struct BiTreeNode {
	char value;
	BiTreeNode* leftChild;
	BiTreeNode* rightChild;
};
extern void createBTree(BiTreeNode*& bTreeRoot, char*& ptChar);
extern void displayBTree(BiTreeNode*& bTreeRoot);

extern void preOrderRecursion(BiTreeNode*& bTreeRoot);
extern void inOrderRecursion(BiTreeNode*& bTreeRoot);
extern void postOrderRecursion(BiTreeNode*& bTreeRoot);

extern void preOrderNoRecusion(BiTreeNode*& bTreeRoot);
extern void inOrderNoRecursion(BiTreeNode*& bTreeRoot);
extern void postOrderNoRecursion(BiTreeNode*& bTreeRoot);


int main() {
	char array[] = "A(B(C(,F(G)),E),D)";
	char* ptChar = array;  //c++里只能这样分开定义,要不会出错。
	BiTreeNode* bTreeRoot = NULL;

	createBTree(bTreeRoot,ptChar);
	cout << "the char array is :";
	for (int i = 0; array[i] != '\0'; i++)
		cout << array[i];
	cout<< endl<< "binary tree is    :";
	displayBTree(bTreeRoot);

	cout << endl << "preorder , recursion    : "; preOrderRecursion(bTreeRoot);
	cout << endl << "preorder , no recursion : "; preOrderNoRecusion(bTreeRoot);
	cout << endl;
	cout << endl << "inorder , recursion    : "; inOrderRecursion(bTreeRoot);
	cout << endl << "inorder , no recursion : "; inOrderNoRecursion(bTreeRoot);
	cout << endl;
	cout << endl << "postorder , recursion    : "; postOrderRecursion(bTreeRoot);
	cout << endl << "post order ,no recursion : "; postOrderNoRecursion(bTreeRoot);
	cout << endl;

	return 0;
}

  各个被调用函数所在的源文件的代码如下:

#include<iostream>
#include<stdio.h>
using namespace std;
#define STACKDEPTH 15
struct BiTreeNode {
	char value;
	BiTreeNode* leftChild;
	BiTreeNode* rightChild;
};

struct Stack{
	BiTreeNode *nodes[STACKDEPTH];
	int indexTop = -1;
};

void createBTree(BiTreeNode*& bTreeRoot, char*& ptChar) {
	struct {
		BiTreeNode* ptsBiTree[STACKDEPTH];
		int indexTop = -1;
	}sequStack;

	BiTreeNode* ptNew = NULL;
	char s;
	int leftRight;//1 is left   2 is right
	while (*ptChar != '\0') {
		s = *ptChar;
		if ('A' <= s && s <= 'Z') {
			ptNew = new BiTreeNode;
			ptNew->value = s;
			ptNew->leftChild = ptNew->rightChild = NULL;

			if (bTreeRoot == NULL)
				bTreeRoot = ptNew;
			else if (leftRight == 1)
				sequStack.ptsBiTree[sequStack.indexTop]->leftChild = ptNew;
			else if (leftRight == 2)
				sequStack.ptsBiTree[sequStack.indexTop]->rightChild = ptNew;
		}
		else if (s == '(') {
			sequStack.indexTop++;
			sequStack.ptsBiTree[sequStack.indexTop] = ptNew;
			leftRight = 1;
		}
		else if (s == ',')
			leftRight = 2;
		else if (s == ')')
			sequStack.indexTop--;

		ptChar++;
	}
}

void displayBTree(BiTreeNode*& bTreeRoot) {   // 本查找方法是先序遍历
	if (bTreeRoot == NULL)
		return;//if binary tree does not exsit,return
	cout << bTreeRoot->value;
	if (bTreeRoot->leftChild != NULL || bTreeRoot->rightChild != NULL) {
		cout << '(';
		displayBTree(bTreeRoot->leftChild);
		if (bTreeRoot->rightChild != NULL) {
			cout << ',';
			displayBTree(bTreeRoot->rightChild);
		}
		cout << ')';
	}
}
void preOrderRecursion(BiTreeNode*& biTreeRoot) {
	if (biTreeRoot == NULL)
		return;
	cout << biTreeRoot->value << ' ';
	preOrderRecursion(biTreeRoot->leftChild);
	preOrderRecursion(biTreeRoot->rightChild);
}

void inOrderRecursion(BiTreeNode*& biTreeRoot) {
	if (biTreeRoot == NULL)
		return;

	inOrderRecursion(biTreeRoot->leftChild);
	cout << biTreeRoot->value << ' ';
	inOrderRecursion(biTreeRoot->rightChild);

}
void postOrderRecursion(BiTreeNode*& biTreeRoot) {
	if (biTreeRoot == NULL)
		return;
	postOrderRecursion(biTreeRoot->leftChild);
	postOrderRecursion(biTreeRoot->rightChild);
	cout << biTreeRoot->value << ' ';
}

void preOrderNoRecusion(BiTreeNode*& biTreeRoot) {
	if (biTreeRoot == NULL)
		return ;

	Stack stack;
	stack.indexTop++;
	stack.nodes[stack.indexTop] = biTreeRoot;
	BiTreeNode* pt;

	while (stack.indexTop > -1) {
		pt = stack.nodes[stack.indexTop];
		cout << pt->value << ' ';
		stack.indexTop--;
	
		if (pt->rightChild != NULL) {
			stack.indexTop++;
			stack.nodes[stack.indexTop] = pt->rightChild;
		}
			
		if (pt->leftChild != NULL) {
			stack.indexTop++;
			stack.nodes[stack.indexTop] = pt->leftChild;
		}
	}
}
void inOrderNoRecursion(BiTreeNode*& biTreeRoot) {
	if (biTreeRoot == NULL)
		return;

	Stack stack;
	BiTreeNode* pt;
	stack.indexTop++;
	stack.nodes[stack.indexTop] = biTreeRoot;
	bool checkLeftChild = true;
	//表示指针是否需要检测栈顶元素的左子节点,是为true,不需要为false
	while (stack.indexTop > -1) {
		pt = stack.nodes[stack.indexTop];
		//引入checkLeftChild的目的是为了决定此循环是否执行
		while (checkLeftChild && pt->leftChild != NULL) {
			stack.indexTop++;
			stack.nodes[stack.indexTop] = pt->leftChild;
			pt = pt->leftChild;
		}

		pt = stack.nodes[stack.indexTop];//pt重新指向栈顶
		cout << pt->value << ' ';
		stack.indexTop--;
		checkLeftChild = false;

		if (pt->rightChild != NULL) {
			stack.indexTop++;
			stack.nodes[stack.indexTop] = pt->rightChild;
			checkLeftChild = true;
		}

	}
}
void postOrderNoRecursion(BiTreeNode*& biTreeRoot) {
	if (biTreeRoot == NULL)
		return;

	Stack stack;
	BiTreeNode* pt,*ptPopped = NULL;
	stack.indexTop++;
	stack.nodes[stack.indexTop] = biTreeRoot;
	bool checkLeftChild = true;

	while (stack.indexTop > -1) {
		pt = stack.nodes[stack.indexTop];
		while (checkLeftChild && pt->leftChild != NULL) {
			stack.indexTop++;
			stack.nodes[stack.indexTop] = pt->leftChild;
			pt = pt->leftChild;
		}

		pt = stack.nodes[stack.indexTop];
		if (pt->rightChild == NULL || pt->rightChild == ptPopped) {
			cout << pt->value << ' ';
			stack.indexTop--;
			checkLeftChild = false;
			ptPopped = pt;
		}
		else {
			stack.indexTop++;
			stack.nodes[stack.indexTop] = pt->rightChild;
			checkLeftChild = true;
			ptPopped = NULL;//初始化,防止二叉树中有值相同的节点
		}
	
	}

}

二叉树和对应测试结果如下:
在这里插入图片描述

在这里插入图片描述






在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值