同学阿里三面面试官的一道面试题是:二叉树每个节点都保存一个整数,想要求所有节点数值之和,哪种遍历方式最快?
首先定义二叉树
struct Tree
{
int val;
Tree *left;
Tree *right;
Tree(){val = 0;left = NULL;right = NULL;};
};
Tree* constructTree(vector<int> &vect, int i)
{
if(i >= vect.size())
return NULL;
Tree *root = new Tree();
root->val = vect[i];
root->left = constructTree(vect, 2*i+1);
root->right = constructTree(vect, 2*i+2);
}
void releaseTree(Tree *root)
{
if(root == NULL)
return;
release(root->left);
release(root->right);
delete root;
}
二叉树有四种遍历方式,先序遍历、中序遍历、后序遍历、层次遍历。分别利用这些遍历方式进行二叉树所有元素求和:
一、 递归方式
层次遍历只能通过非递归的方式实现,下面只给出先序遍历、中序遍历、后续遍历的三种递归实现方式。
//递归的先序遍历求和
void frontTraverseSum(Tree *root, int &sum)
{
if(root == NULL)
return;
sum += root->val;
frontTraverseSum(root->left, sum);
frontTraverseSum(root->right, sum);
}
//递归的中序遍历求和
void middleTraverseSum(Tree *root, int &sum)
{
if(root == NULL)
return;
middleTraverseSum(root->left, sum);
sum += root->val;
middleTraverseSum(root->right, sum);
}
//递归的后续遍历求和
void backTraverseSum(Tree *root, int &sum)
{
if(root == NULL)
return;
backTraverseSum(root->left, sum);
backTraverseSum(root->right, sum);
sum += root->val;
}
二、 非递归方式
//非递归的先序遍历求和,并使用count统计入栈次数
void preTraverseSum(Tree *root, int &sum)
{
stack<Tree*> st;
Tree *p = root;
int count = 0;
while(p != NULL || !st.empty()) //当出栈到root的时候会出现栈为空的中间状态,所以p不为空的条件不能省略
{
while(p != NULL)
{
sum += p->val;
st.push(p);
count++;
p = p->left;
}
if(!st.empty())
{
p = st.top();
st.pop();
p = p->right;
}
}
cout << "pre st.push count: " << count << endl;
}
//非递归的中序遍历求和
void inTraverseSum(Tree *root, int &sum)
{
stack<Tree*> st;
Tree *p = root;
int count = 0;
while(p != NULL || !st.empty())
{
while(p != NULL)
{
st.push(p);
count++;
p = p->left;
}
if(!st.empty())
{
p = st.top();
st.pop();
sum += p->val;
p = p->right;
}
}
cout << "in st.push count: " << count << endl;
}
//非递归的后序遍历求和, 这个比先序遍历和中序遍历复杂,需要判断出栈条件:左右孩子均为空或者左右孩子被访问过
void postTraverseSum(Tree *root, int &sum)
{
stack<Tree*> st;
Tree *cur = root;
Tree *pre = NULL;
st.push(root);
int count = 1;
while(!st.empty())
{
cur = st.top();
if((cur->left == NULL && cur->right == NULL) || (pre != NULL && (pre == cur->left || pre == cur->right)))
{
sum += cur->val;
st.pop();
pre = cur;
} else {
if(cur->right)
{
st.push(cur->right);
count++;
}
if(cur->left)
{
st.push(cur->left);
count++;
}
}
}
cout << "post st.push count: " << count << endl;
}
//只能非递归的层次遍历
void levelTraverseSum(Tree*root, int &sum)
{
queue<Tree*> qu;
Tree *p = root;
qu.push(root);
while(!qu.empty())
{
p = qu.front();
sum += p->val;
qu.pop();
if(p->left)
qu.push(p->left);
if(p->right)
qu.push(p->right);
}
}
三、 测试每种遍历方式的时间
全都采用相同的模板:
void test(Tree *root)
{
int sum = 0;
clock_t beg = clock();
traverseSum(root, sum);
clock_t end = clock();
cout << "time: " << end - beg << endl;
cout << "sum: " << sum << endl;
}
int main()
{
vector<int> vect;
for(int i = 0; i < 7000000; i++)
{
vect.push(i);
}
Tree *root;
root = construct(vect, 0);
test();
releaseTree(root);
return 0;
}
四、 运行结论
在规模小的二叉树上没有太大差异,对于大的二叉树,递归遍历都快于非递归遍历,但是每次运行的结果都有差异,一般来讲中序遍历是执行比较快的,后序遍历和层次遍历最慢:
一次运行结果:
7000000
front time: 50000
front sum: 1503043616
middle time: 40000
middle sum: 1503043616
back time: 40000
back sum: 1503043616
level time: 510000
level sum: 1503043616
pre st.push count: 7000000
pre time: 470000
pre sum: 1503043616
in st.push count: 7000000
in time: 460000
in sum: 1503043616
post st.push count: 7000000
post time: 530000
post sum: 1503043616
重复运行一次结果:
7000000
front time: 50000
front sum: 1503043616
middle time: 40000
middle sum: 1503043616
back time: 50000
back sum: 1503043616
level time: 500000
level sum: 1503043616
pre st.push count: 7000000
pre time: 480000
pre sum: 1503043616
in st.push count: 7000000
in time: 450000
in sum: 1503043616
post st.push count: 7000000
post time: 540000
post sum: 1503043616
五、 完整代码
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <ctime>
using namespace std;
struct Tree
{
int val;
Tree *left;
Tree *right;
Tree(){val = 0; left = NULL; right = NULL;};
};
void frontTraverseSum(Tree *root, int &sum)
{
if(root == NULL)
return;
sum += root->val;
frontTraverseSum(root->left, sum);
frontTraverseSum(root->right, sum);
}
void middleTraverseSum(Tree *root, int &sum)
{
if(root == NULL)
return;
middleTraverseSum(root->left, sum);
sum += root->val;
middleTraverseSum(root->right, sum);
}
void backTraverseSum(Tree *root, int &sum)
{
if(root == NULL)
return;
backTraverseSum(root->left, sum);
backTraverseSum(root->right, sum);
sum += root->val;
}
void test1(Tree *root)
{
int sum = 0;
clock_t beg = clock();
frontTraverseSum(root, sum);
clock_t end = clock();
cout << "front time: " << end - beg << endl;
cout << "front sum: " << sum << endl;
}
void test2(Tree *root)
{
int sum = 0;
clock_t beg = clock();
middleTraverseSum(root, sum);
clock_t end = clock();
cout << "middle time: " << end - beg << endl;
cout << "middle sum: " << sum << endl;
}
void test3(Tree *root)
{
int sum = 0;
clock_t beg = clock();
backTraverseSum(root, sum);
clock_t end = clock();
cout << "back time: " << end - beg << endl;
cout << "back sum: " << sum << endl;
}
Tree* constructTree(vector<int> &vect, int k)
{
if(k >= vect.size())
return NULL;
Tree *root = new Tree();
root->val = vect[k];
//cout << root->val << " ";
root->left = constructTree(vect, 2*k+1);
root->right = constructTree(vect, 2*k+2);
return root;
}
void levelTraverseSum(Tree *root, int &sum)
{
queue<Tree*> qu;
qu.push(root);
while(!qu.empty())
{
Tree *father = qu.front();
qu.pop();
sum += father->val;
if(father->left)
qu.push(father->left);
if(father->right)
qu.push(father->right);
}
}
void test4(Tree *root)
{
int sum = 0;
clock_t beg = clock();
levelTraverseSum(root, sum);
clock_t end = clock();
cout << "level time: " << end - beg << endl;
cout << "level sum: " << sum << endl;
}
void levelTraverse(Tree *root)
{
queue<Tree*> qu;
qu.push(root);
while(!qu.empty())
{
Tree *father = qu.front();
qu.pop();
cout << father->val << " ";
if(father->left)
qu.push(father->left);
if(father->right)
qu.push(father->right);
}
cout << endl;
}
void releaseTree(Tree *root)
{
if(root == NULL)
return;
releaseTree(root->left);
releaseTree(root->right);
delete root;
}
void preTraverse(Tree *root)
{
stack<Tree*> st;
Tree *p = root;
while(p != NULL || !st.empty())
{
while(p != NULL)
{
cout << p->val << " ";
st.push(p);
p = p->left;
}
if(!st.empty())
{
p = st.top();
st.pop();
p = p->right;
}
}
cout << endl;
}
void preTraverseSum(Tree *root, int &sum)
{
stack<Tree*> st;
Tree *p = root;
int count = 0;
while(p != NULL || !st.empty())
{
while(p != NULL)
{
sum += p->val;
st.push(p);
count++;
p = p->left;
}
if(!st.empty())
{
p = st.top();
st.pop();
p = p->right;
}
}
cout << "pre st.push count: " << count << endl;
}
void test5(Tree *root)
{
int sum = 0;
clock_t beg = clock();
preTraverseSum(root, sum);
clock_t end = clock();
cout << "pre time: " << end - beg << endl;
cout << "pre sum: " << sum << endl;
}
void inTraverse(Tree *root)
{
stack<Tree*> st;
Tree *p = root;
while(p != NULL || !st.empty())
{
while(p != NULL)
{
st.push(p);
p = p->left;
}
if(!st.empty())
{
p = st.top();
cout << p->val << " ";
st.pop();
p = p->right;
}
}
cout << endl;
}
void inTraverseSum(Tree *root, int &sum)
{
stack<Tree*> st;
Tree *p = root;
int count = 0;
while(p != NULL || !st.empty())
{
while(p != NULL)
{
st.push(p);
count++;
p = p->left;
}
if(!st.empty())
{
p = st.top();
sum += p->val;
st.pop();
p = p->right;
}
}
cout << "in st.push count: " << count << endl;
}
void test6(Tree *root)
{
int sum = 0;
clock_t beg = clock();
inTraverseSum(root, sum);
clock_t end = clock();
cout << "in time: " << end - beg << endl;
cout << "in sum: " << sum << endl;
}
void postTraverse(Tree *root)
{
stack<Tree*> st;
Tree *cur = root;
Tree *p = NULL;
st.push(root);
while(!st.empty())
{
cur = st.top();
if((cur->left == NULL && cur->right == NULL) || (p != NULL &&(p == cur->left || p == cur->right)))
{
cout << cur->val << " ";
st.pop();
p = cur;
} else {
if(cur->right)
st.push(cur->right);
if(cur->left)
st.push(cur->left);
}
}
cout << endl;
}
void postTraverseSum(Tree *root, int &sum)
{
stack<Tree*> st;
Tree *cur = root;
Tree *p = NULL;
st.push(root);
int count = 1;
while(!st.empty())
{
cur = st.top();
if((cur->left == NULL && cur->right == NULL) || (p != NULL && (p == cur->left || p == cur->right)))
{
sum += cur->val;
st.pop();
p = cur;
} else {
if(cur->right)
{
st.push(cur->right);
count++;
}
if(cur->left)
{
st.push(cur->left);
count++;
}
}
}
cout << "post st.push count: " << count << endl;
}
void test7(Tree *root)
{
int sum = 0;
clock_t beg = clock();
postTraverseSum(root, sum);
clock_t end = clock();
cout << "post time: " << end - beg << endl;
cout << "post sum: " << sum << endl;
}
int main()
{
vector<int> vect;
for(int i = 0; i < 7000000; i++)
{
vect.push_back(i);
}
cout << vect.size() << endl;
Tree *root;
root = constructTree(vect, 0);
/* cout << "levelTraverse: ";
levelTraverse(root);
cout << "preTraverse: ";
preTraverse(root);
cout << "inTraverse: ";
inTraverse(root);
cout << "postTraverse: ";
postTraverse(root); */
test1(root);
test2(root);
test3(root);
test4(root);
test5(root);
test6(root);
test7(root);
releaseTree(root);
return 0;
}