1. 程式人生 > >二叉樹四種遍歷方式的速度差異

二叉樹四種遍歷方式的速度差異

同學阿里三面面試官的一道面試題是:二叉樹每個節點都儲存一個整數,想要求所有節點數值之和,哪種遍歷方式最快?

首先定義二叉樹

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;
}