数据结构六:二叉树非递归遍历

一 定义

二叉树非递归遍历采用的是栈结构进行管理输出方式,采用循环去替代递归。和层次遍历有相似之处。

二 结构

data left right flag

flag用于后顺遍历时标记右侧树的访问状态,若左右子树都被访问后(为null或者flag==1),则可以弹出当前节点并打印.

三 源码

#include 
#include 
using namespace std;

struct TreeNode
{

	int data;
	TreeNode* leftNode;
	TreeNode* rightNode;
	int flag;
};


TreeNode* creatTree(int arr[], int size, int* index) {
	// 空数组 || 已经遍历到最后
	if (!arr || *index >= size) {
		return NULL;
	}

	int value = arr[*index];
	(*index)++;
	// 值为 -1 时认为是空节点
	if (value == -1) {
		return NULL;
	}

	TreeNode* tree = new TreeNode();
	tree->data = value;
	tree->flag = 0;
	tree->leftNode = creatTree(arr, size, index);
	tree->rightNode = creatTree(arr, size, index);

	return tree;
}

/// 递归前序遍历
void prePrint(TreeNode* root) {
	if (root) {
		printf("%d ", root->data);
		prePrint(root->leftNode);
		prePrint(root->rightNode);
	}
}

/// 非递归前序遍历
/// 其核心逻辑是先将当前树节点的左子树压栈,当左子树为空时访问当前节点的右子树
void prePrintUseStack(TreeNode* root) {

	std::stack tempStack;
	while (root || !tempStack.empty()) {
		if (root) {
			printf("node:%d \n", root->data); // 根
			tempStack.push(root);
			root = root->leftNode; // 左
		}
		else {
			root = tempStack.top()->rightNode; // 右
			// 当节点已经访问了左子树和右子树就可以被弹出抛弃了
			tempStack.pop();
		}

	}

}

/// 非递归中序
void middlePrint(TreeNode* root) {

	std::stack tempStack;

	while (root || !tempStack.empty()) {
		// 将左侧节点全部压入
		if (root) {
			tempStack.push(root);
			root = root->leftNode; // 左
		}
		else {

			root = tempStack.top(); // 根
			//if (root) {
				printf("%d ", root->data);
				root = root->rightNode; // 右
			//}
			tempStack.pop();
		}

	}
}

void backOrder(TreeNode* root) {

	std::stack tempStack;

	while (root || !tempStack.empty()) {

		if (root) {
			tempStack.push(root);
			root = root->leftNode; // 左
		}
		else {
			TreeNode* tempNode = tempStack.top(); // 根
			if (tempNode) {
				if (tempNode->rightNode && tempNode->rightNode->flag == 0) {
					root = tempNode->rightNode;
				}
				else {		// 右节点为空或者已经被访问,那就访问当前根节点
					printf("%d ", tempNode->data);
					tempNode->flag = 1; // 设置当前节点已经访问
					tempStack.pop();
				}
			}
		}


	}


}





int main() {
	int arr[] = { 1,2,4,-1,-1,5,-1,-1,3,6,-1,-1,7,-1,-1 };
	int size = sizeof(arr) / sizeof(int);
	int index = 0;
	TreeNode* tree = creatTree(arr, size, &index);

	//prePrint(tree);

	//prePrintUseStack(tree);
	//middlePrint(tree);
	backOrder(tree);
}



四 总结

1 前序打印是入栈时就进行打印,中序遍历和后序遍历在出栈时进行打印。

2 后序遍历需要增加tag来标定右子树访问状态

3 所有打印形式都是左子树先全部压栈,然后再处理左子树、根子树和右子树之间的关系,左子树的压栈顺序是主要线索。

4 最后发现,还是根据左子树与右子树之间的关系来决定当前节点是否可以打印

你可能感兴趣的:(数据结构,算法)