二叉树四种遍历方式的速度差异

本文探讨了在二叉树中求节点数值之和的问题,分析了先序、中序、后序和层次遍历四种方法,并通过递归和非递归方式实现。实测结果显示,在大规模二叉树中,递归遍历通常更快,其中中序遍历的效率相对较高,而后序遍历和层次遍历较慢。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

同学阿里三面面试官的一道面试题是:二叉树每个节点都保存一个整数,想要求所有节点数值之和,哪种遍历方式最快?

首先定义二叉树

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值