树的遍历

来源:互联网 发布:西蒙网络面板 有问题 编辑:程序博客网 时间:2024/05/18 10:57

概述

树的遍历分为深度优先遍历和广度优先遍历。深度优先遍历又分为前序遍历、中序遍历、后序遍历。前序遍历的顺序为:父节点、左子节点、右子节点,中序遍历的顺序为:左子节点、父节点、右子节点,后序遍历的顺序为:左子节点、右子节点、父节点。广度优先遍历即从根节点从上往下逐层遍历,每层从左到右遍历。可以通过递归的方式或者使用栈来实现遍历,递归方式和使用栈的方式都需要遍历n个节点,所以时间复杂度为o(n);采用递归方式,需要n次递归调用,所以空间复杂度为o(n),使用栈的方式,栈需要存储空间,栈的大小为n,所以空间复杂度为o(n)

前序遍历

●递归版
思路:按照遍历的顺序进行递归调用即可;退出递归调用的条件:节点为空

#include<iostream>#include<vector>using namespace std;struct TreeNode{    int val;    TreeNode *left;    TreeNode *right;    TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}};void preorder(TreeNode *root,vector<int>&result) {    if (!root)        return;    result.push_back(root->val);    preorder(root->left,result);    preorder(root->right,result);}

●非递归版
思路:从根节点开始依次将节点入栈,栈顶节点即为本次需要访问的节点,访问过栈顶节点后,然后将此节点弹出,分别将右子节点入栈、左子节点入栈,然后进行下一次访问,直到栈为空,即访问完所有节点。

#include<iostream>#include<vector>#include<stack>using namespace std;struct TreeNode{    int val;    TreeNode *left;    TreeNode *right;    TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}};vector<int>preorder(TreeNode *root){    vector<int> result;    stack<TreeNode *> sta;    TreeNode *p ;    if (!root) return result;    else sta.push(root);    while(!sta.empty())    {        p = sta.top();        sta.pop();        result.push_back(p->val);        if (p->right)            sta.push(p->right);        if (p->left)            sta.push(p->left);    }    return result;}

后序遍历

●递归版
思路:按照遍历的顺序进行递归调用即可;退出递归调用的条件:节点为空

#include<iostream>#include<vector>using namespace std;struct TreeNode{    int val;    TreeNode *left;    TreeNode *right;    TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}};void postorder(TreeNode *root, vector<int>&result) {    if (!root)        return;    postorder(root->left);    postorder(root->right);    result.push_back(root->val);}

●非递归版
思路:后序遍历的顺序是左字节点、右子节点、父节点。可以先访问父节点,将父节点保存到动态数组中,然后访问右子节点,将右子节点保存到动态数组的第一个元素处,最后访问左子节点,并保存到动态数组的一个元素处,最后动态数组中元素的顺序为左字节点、右子节点、父节点。思路与前序遍历基本相同,只是将正在访问的元素存入动态数组vector.begin()处,而前序遍历是存入vector.end()处

#include<iostream>#include<vector>#include<stack>using namespace std;struct TreeNode{    int val;    TreeNode *left;    TreeNode *right;    TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}};vector<int> postorder(TreeNode* root) {    vector<int> result;    stack<TreeNode *> sta;    TreeNode *p;    if (!root) return result;    else sta.push(root);    while (!sta.empty())     {        p = sta.top();        sta.pop();        result.insert(result.begin(), p->val);        if (p->left) sta.push(p->left);        if (p->right) sta.push(p->right);    }    return result;}

中序遍历

●递归版
思路:按照遍历的顺序进行递归调用即可;退出递归调用的条件:节点为空

#include<iostream>#include<vector>using namespace std;struct TreeNode{    int val;    TreeNode *left;    TreeNode *right;    TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}};void inorder(TreeNode *root,vector<int>&result) {    if (root == NULL)        return;    inorder(root->left);    result.push_back(root->val);    inorder(root->right);}

●非递归版
思路:与之前前序遍历和后序遍历不同的是,父节点的访问顺序介于左子节点和右子节点之间,需要先访问左子节点,然后再回退访问父节点,最后访问右子节点,另外访问结束的条件与前序和后续遍历也不同,只有当栈为空且节点为空才代表访问完了所有节点,栈为空说明访问完了根节点的左子树,节点为空说明当前节点的父节点不存在左子节点,不用访问左子节点。

#include<iostream>#include<vector>#include<stack>using namespace std;struct TreeNode{    int val;    TreeNode *left;    TreeNode *right;    TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}};vector<int>inorder(TreeNode *root){    vector<int> result;    stack<TreeNode *> sta;    TreeNode *p = root;    if (root == nullptr) return result;    while (p != nullptr||!sta.empty())    {        if (p != nullptr)        {            sta.push(p);            p = p->left;        }        else        {            p = sta.top();            sta.pop();            result.push_back(p->val);            p = p->right;        }    }    return result;}

层序遍历
思路:层序遍历即从根节点所在层从上往下逐层遍历,每一层从左到右进行遍历,可以使用二维数组作为存储结构,将相同层的节点存入同一个数组中。

#include<iostream>#include<vector>#include<stack>using namespace std;struct TreeNode{    int val;    TreeNode *left;    TreeNode *right;    TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}};void travel(TreeNode *p, int level, vector<vector<int>>&result){    if (!p)  return ;    while (level > result.size())       result.push_back(vector<int>());    result[level - 1].push_back(p->val);    travel(p->left, level + 1, result);    travel(p->right, level + 1, result);}vector<vector<int>>level(TreeNode *root){    vector<vector<int>>result;    travel(root, 1, result);    return result;}

相关问题
打印出二叉树中和为某一值的所有路径,从树的根节点到叶子节点算作一条路径

思路:直到访问到叶子节点时才能判断路径是否符合要求,因此需要变量存储已访问节点的路径和。在访问到叶子节点之前,需要将之前访问过的节点保存起来,从而在路径满足要求时,可以打印路径。可以通过递归的方式,访问各个节点,先访问父节点,然后访问左字节点和右子节点,访问到叶子节点时,退出递归函数。每访问过一个节点,需要将节点从路径中删除,从而访问由此节点的兄弟节点构成的路径。

#include<iostream>#include<vector>using namespace std;struct TreeNode{    int val;    TreeNode *left;    TreeNode *right;    TreeNode(int value) :val(value), left(nullptr), right(nullptr){}};void findpath(TreeNode *root, int tar,int cursum, vector<int>&result){    TreeNode *p = root;    cursum+=root->val;    result.push_back(root->val);    if (p->left == nullptr&&p->right == nullptr)    {        if (cursum == tar)        {            for (int i = 0; i < result.size(); i++)                cout << result[i] << " ";            cout << endl;        }        result.pop_back();        return;    }    if (p->left)        findpath(p->left, tar, cursum, result);    if (p->right)        findpath(p->right, tar, cursum, result);    result.pop_back();}void findpath(TreeNode *root, int tar){    vector<int> result;    if (root == nullptr)        return ;    int cursum = 0;    findpath(root, tar,cursum ,result);}
0 0