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
来源:https://blog.csdn.net/qq_24309981/article/details/85275275