二叉樹四種遍歷方式的速度差異
阿新 • • 發佈:2019-02-01
同學阿里三面面試官的一道面試題是:二叉樹每個節點都儲存一個整數,想要求所有節點數值之和,哪種遍歷方式最快?
首先定義二叉樹
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;
}