二叉树遍历实现及效率分析

主宰稳场 提交于 2019-11-30 01:07:33

1 理论


1、二叉树遍历方法

  • 先序遍历遍历顺序:根–左--右
  • 中序遍历遍历顺序:左–根--右
  • 后序遍历遍历顺序:左–右--根
  • 层序遍历遍历顺序:从上到下,从左到右

2、二叉树复原

  • 已知先序遍历序列中序遍历序列,可以唯一确定一颗二叉树
  • 已知中序遍历序列后序遍历序列,可以唯一确定一颗二叉树
  • 已知先序遍历序列后序遍历序列能唯一确定一颗二叉树

3、二叉树存储

  • 顺序存储:用一维数组存储二叉树中的节点,并且节点的存储位置,也就是数组的下标要能体现节点之间的逻辑关系,一般只用于完全二叉树 。
  • 链式存储:每个节点的结构具有一个存储数据的变量与两个指向孩子的指针域。

2 代码

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <ctime>
using namespace std;

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

#pragma region 先序遍历
void preorderRecursion(TreeNode* node) {
	if (node)
	{
		//cout << node->val << endl;

		preorderRecursion(node->left);
		preorderRecursion(node->right);
	}
}

void preorderIteration(TreeNode* root) {
	if (root)
	{
		stack<TreeNode*> toVisit;
		toVisit.push(root);
		while (!toVisit.empty())
		{
			TreeNode* visiting = toVisit.top();
			toVisit.pop();

			//cout << visiting->val << endl;

			if (visiting->right)
			{
				toVisit.push(visiting->right);
			}
			if (visiting->left)
			{
				toVisit.push(visiting->left);
			}
		}
	}
}
#pragma endregion

#pragma region 中序遍历
void inorderRecursion(TreeNode* node) {
	if (node)
	{
		inorderRecursion(node->left);
		//cout << node->val << endl;
		inorderRecursion(node->right);
	}
}

void inorderIteration(TreeNode* root) {
	if (root)
	{
		stack<TreeNode*> toVisit;
		toVisit.push(root);
		TreeNode* visiting = root->left;
		while (visiting || !toVisit.empty())
		{
			while (visiting)
			{
				toVisit.push(visiting);
				visiting = visiting->left;
			}
			visiting = toVisit.top();
			toVisit.pop();

			//cout << visiting->val << endl;

			visiting = visiting->right;
		}
	}
}
#pragma endregion

#pragma region 后序遍历
void postorderRecursion(TreeNode* node) {
	if (node)
	{
		postorderRecursion(node->left);
		postorderRecursion(node->right);

		//cout << node->val << endl;
	}
}

//考虑到右子节点一定是在当前父节点前一个被遍历到
//一种代替标记的有效方法
void postorderIteration(TreeNode* root) {
	if (root)
	{
		stack<TreeNode*> toVisit;
		toVisit.push(root);
		TreeNode* visiting = root->left;
		TreeNode* lastPop = NULL;
		while (visiting || !toVisit.empty())
		{
			//找到没有左子节点的节点  或  左子节点已经弹出的节点
			while (visiting)
			{
				toVisit.push(visiting);
				visiting = visiting->left;
			}
			visiting = toVisit.top();

			//如果右子节点不存在  或  右子节点已弹出
			if (!visiting->right || visiting->right == lastPop)
			{
				toVisit.pop();

				//cout << visiting->val << endl;

				lastPop = visiting;
				visiting = NULL;
			}
			//右子节点存在,且未被弹出
			else
			{
				visiting = visiting->right;
			}
		}
	}
}

//很巧妙,本质是先序遍历,结果是后序遍历
//但空间复杂度为O(n)
//void postorderIteration(TreeNode* root) {
//	vector<int> postorder;
//	if (root)
//	{
//		stack<TreeNode*> toVisit;
//		toVisit.push(root);
//		while (!toVisit.empty())
//		{
//			TreeNode* visiting = toVisit.top();
//			toVisit.pop();
//			postorder.push_back(visiting->val);
//			if (visiting->left)
//			{
//				toVisit.push(visiting->left);
//			}
//			if (visiting->right)
//			{
//				toVisit.push(visiting->right);
//			}
//		}
//		reverse(postorder.begin(), postorder.end());
//		for (auto ele : postorder)
//		{
//			//cout << ele << endl;
//		}
//	}
//}
#pragma endregion

#pragma region 层序遍历
void levelTraversal(TreeNode* root) {
	if (root)
	{
		queue<TreeNode*> toVisit;
		toVisit.push(root);
		while (!toVisit.empty()) 
		{
			TreeNode* visiting = toVisit.front();
			toVisit.pop();
			if (visiting->left)
			{
				toVisit.push(visiting->left);
			}
			if (visiting->right)
			{
				toVisit.push(visiting->right);
			}
			//cout << visiting->val << endl;
		}
	}
}
#pragma endregion

#pragma region 构造完全二叉树
TreeNode* constructTree(vector<int> &vect, int k)
{
	TreeNode *root = NULL;
	if (k < vect.size())
	{
		root = new TreeNode(vect[k]);
		root->left = constructTree(vect, 2 * k + 1);
		root->right = constructTree(vect, 2 * k + 2);
	}
	return root;
}
#pragma endregion

#pragma region 计时
void timing(TreeNode *root)
{
	clock_t beg = clock();
	preorderRecursion(root);
	clock_t end = clock();
	cout << "preorderRecursion time: " << end - beg << endl;

	beg = clock();
	preorderIteration(root);
	end = clock();
	cout << "preorderIteration time: " << end - beg << endl;

	beg = clock();
	inorderRecursion(root);
	end = clock();
	cout << "inorderRecursion time: " << end - beg << endl;

	beg = clock();
	inorderIteration(root);
	end = clock();
	cout << "inorderIteration time: " << end - beg << endl;

	beg = clock();
	postorderRecursion(root);
	end = clock();
	cout << "postorderRecursion time: " << end - beg << endl;

	beg = clock();
	postorderIteration(root);
	end = clock();
	cout << "postorderIteration time: " << end - beg << endl;

	beg = clock();
	levelTraversal(root);
	end = clock();
	cout << "levelTraversal time: " << end - beg << endl;
}
#pragma endregion

int main()
{
	vector<int> input;
	for (int i = 0; i < 9000000; i++)
	{
		input.push_back(i);
	}
	TreeNode *root = constructTree(input, 0);
	timing(root);
	
	/*TreeNode* A = new TreeNode(1);
	TreeNode* B = new TreeNode(2);
	TreeNode* C = new TreeNode(3);
	TreeNode* D = new TreeNode(4);
	TreeNode* E = new TreeNode(5);
	TreeNode* F = new TreeNode(6);
	TreeNode* G = new TreeNode(7);
	TreeNode* H = new TreeNode(8);
	TreeNode* K = new TreeNode(9);
	A->left = B;
	A->right = E;
	B->right = C;
	C->left = D;
	E->right = F;
	F->left = G;
	G->left = H;
	G->right = K;*/

	//preorderRecursion(A);
	//preorderIteration(A);

	//inorderRecursion(A);
	//inorderIteration(A);

	//postorderRecursion(A);
	//postorderIteration(A);

	//levelTraversal(A);
	system("pause");
    return 0;
}

//输出
preorderRecursion time: 371
preorderIteration time: 25562
inorderRecursion time: 378
inorderIteration time: 25416
postorderRecursion time: 372
postorderIteration time: 30535
levelTraversal time: 22109

3 效率问题


二叉树遍历效率(只讨论节点个数较多情况)

  • 递归方法的先序遍历、中序遍历、后序遍历运行时间基本相同;
  • 迭代方法的先序遍历、中序遍历、后序遍历运行时间也基本相同;
  • 递归方法效率高于迭代方法效率;
  • 层序遍历效率介于递归和迭代效率之间,更靠近迭代效率。

4 结论


推荐使用递归版(先序、中序、后序均可),实现简单且效率高。

5 参考文献


https://blog.csdn.net/wjwfighting/article/details/81670229
https://blog.csdn.net/na_beginning/article/details/62219224

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!