【每日算法】二叉树的遍历

二叉树特点

  1. 每个节点最多有两棵子树;
  2. 二叉树是有序的,即区分左右子树的次序。

完全二叉树

  1. 叶子节点只能出现在最下两层,且最下层的叶子节点都集中在二叉树左侧连续的位置。
  2. 如果有度为1的节点,只可能有一个,且该节点只有左孩子。

二叉树实现

这里只讲二叉链表实现,使用C++。

template<class DataType>
struct BiNode
{
    DataType data;
    BiNode<DataType> *lchild, *rchild;
};

template<class DataType>
class BiTree
{
public:
    BiTree() {root = Creat(root);}
    ~BiTree() {Release(root);}
    void PreOrder() {PreOrder(root);}
    void InOrder() {InOrder(root);}
    void PostOrder() {PostOrder(root);}
    void LevelOrder();
private:
    BiNode<DataType> *root;
    BiNode<DataType> *Creat(BiNode<DataType> *bt);
    void Release(BiNode<DataType> *bt);
    void PreOrder(BiNode<DataType> *bt);
    void InOrder(BiNode<DataType> *bt);
    void PostOrder(BiNode<DataType> *bt);
};

建树:可以递归调用Creat函数来实现,注意对于参数

BiNode<DataType> *bt

bt是形参,对它的修改仅在函数体内生效,如果想修改传进来的指针,可使用引用:

BiNode<DataType> * &bt

析构函数:在释放某节点时,需要先释放其左右子树,故可使用后序遍历的方法释放。

前序遍历

前序遍历先访问根节点再访问左右子树,其递归实现:

void BiTree<DataType>::PreOrder(BiNode<DataType> *bt)
{
    if (NULL == bt)
        return ;
    cout << bt->data << ‘ ‘;
    PreOrder(bt->lchild);
    PreOrder(bt->rchild);
}

事实上,我们递归的过程如下:

访问节点->往左->访问节点->往左, 直到左边节点为NULL,之后返回该NULL节点的父节点,并访问该父节点的右儿子,继续重复访问节点->往左……的过程。

于是我们可以利用一个栈写出非递归的版本:

void BiTree<DataType>::PreOrder(BiNode<DataType> *bt)
{
    stack<BiNode<DataType> *> s;
    while (bt || !s.empty())
    {
        while (bt)
        {
            cout << bt->data << ‘ ‘;
            s.push(bt);
            bt = bt->lchild;
        }
        if (!s.empty())
        {
            bt = s.top();
            s.pop();
            bt = bt->rchild;
        }
    }
}

中序遍历

中序遍历先访问左子树,再访问根节点,最后访问右子树,其递归实现:

void BiTree<DataType>::InOrder(BiNode<DataType> *bt)
{
    if (NULL == bt)
        return ;
    InOrder(bt->lchild);
    cout << bt->data << ‘ ‘;
    InOrder(bt->rchild);
}

非递归版本的中序遍历与前序遍历非常类似:

void BiTree<DataType>::InOrder(BiNode<DataType> *bt)
{
    stack<BiNode<DataType> *> s;
    while (bt || !s.empty())
    {
        while (bt)
        {
            s.push(bt);
            bt = bt->lchild;
        }
        if (!s.empty())
        {
            bt = s.top();
            s.pop();
            cout << bt->data << ‘ ‘; //将访问移到这里
            bt = bt->rchild;
        }
    }
}

后序遍历

后序遍历先访问左子树,再访问右子树,最后访问根节点,其递归实现:

void BiTree<DataType>::PostOrder(BiNode<DataType> *bt)
{
    if (NULL == bt)
        return ;
    PostOrder(bt->lchild);
    PostOrder(bt->rchild);
    cout << bt->data << ‘ ‘;
}

后序遍历的非递归版本就有点难度了。

对于某个节点,它需要入栈两次,出栈两次:

第一次出栈:只遍历完左子树,右子树尚未遍历,利用栈顶节点找到它的右子树(相当于出栈又进栈),准备遍历它的右子树。

第二次出栈:遍历完右子树,将该节点出栈并访问它。

为区别同一个节点的两次出栈,我们设置标志flag:

flag=1,表示第一次出栈,只遍历完左子树,该节点不能访问;

flag=2,表示第二次出栈,遍历完右子树,该节点能访问;

对于根节点bt,有以下两种情况:

  1. bt不为NULL,则bt及标志(置为1)入栈,遍历其左子树;
  2. bt为NULL,若栈为空,说明整个遍历结束;若栈不为空,说明栈顶节点的左子树或右子树遍历完毕,此时若栈顶元素flag=1,表明刚遍历完左子树,所以修改flag=2,遍历右子树,若栈顶元素flag=2,表明刚遍历完右子树,输出栈顶元素。
void BiTree<DataType>::PostOrder(BiNode<DataType> *bt)
{
    stack<BiNode<DataType> *> s;
    stack<int> s_flag;
    while (bt || !s.empty())
    {
        while (bt)
        {
            s.push(bt);
            s_flag.push(1);
            bt = bt->lchild;
        }
        while (!s.empty() && 2 == s_flag.top())
        {
            bt = s.top();
            s.pop();
            s_flag.pop();
            cout << bt->data << ‘ ‘;
        }
        if (!s.empty())
        {
            s_flag.pop();
            s_flag.push(2);
            bt = s.top()->rchild;
        }
    }
}

层序遍历

层序遍历是一层一层访问的,一个节点被访问,则在下一层中,其儿子节点将被访问,对于同一层的两个节点A、B,A在B左边,则A的儿子节点先于B的儿子节点被访问。

简而言之,先访问的节点,其左右孩子也要先访问,因此我们可以使用一个队列来维护这个过程:

根节点入队,之后

1. 从队列头取出一个元素进行以下操作:

2. 访问该元素;

3. 若该节点的左右孩子非空,将其左右孩子入队;

4. 回到1,直到队列为空时结束。

void BiTree<DataType>::LevelOrder()
{
    queue<BiNode<DataType> *> q;
    if (root)
        q.push(root);
    while (!q.empty())
    {
        BiNode<DataType> *cur = q.top();
        q.pop();
        cout << cur->data << ‘ ‘;
        if (cur->lchild)
            q.push(cur->lchild);
        if (cur->rchild)
            q.push(cur->rchild);
    }
}


每天进步一点点,Come on!

(●’?’●)


本人水平有限,如文章内容有错漏之处,敬请各位读者指出,谢谢!

时间: 2024-11-06 02:25:10

【每日算法】二叉树的遍历的相关文章

[数据结构与算法] 二叉树及其遍历方式

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289830.html 一.数据结构分类 (一)按逻辑结构 集合(无辑关系) 线性结构(线性表):数组.链表.栈.队列 非线性结构:树.图.多维数组 (二)按存储结构 顺序(数组)储结构.链式储结构.索引储结构.散列储结构 二.二叉树相关性质

【LeetCode-面试算法经典-Java实现】【107-Binary Tree Level Order Traversal II(二叉树层序遍历II)】

[107-Binary Tree Level Order Traversal II(二叉树层序遍历II)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root). For example

【转】算法之二叉树各种遍历

http://blog.csdn.net/sjf0115/article/details/8645991 树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用. 二叉树是每个结点最多有两个子树的有序树.通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个结点:

【每日算法】图算法(遍历&amp;MST&amp;最短路径&amp;拓扑排序)

图有邻接矩阵和邻接表两种存储方法,邻接矩阵很简单,这里不讨论,下面我们先看看常用的邻接表表示方法. 邻接表常用表示方法 指针表示法 指针表示法一共需要两个结构体: struct ArcNode //定义边表结点 { int adjvex: //邻接点域 ArcNode* next; }; struct VertexNode //定义顶点表结点 { int vertex; ArcNode* firstedge; }; 每个节点对应一个VertexNode,其firstedge指向边表(与当前节点邻

算法之二叉树各种遍历

树形结构是一类重要的非线性数据结构,当中以树和二叉树最为经常使用. 二叉树是每一个结点最多有两个子树的有序树.通常子树的根被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每一个结点至多仅仅有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个结点:深度为k的二叉树至多有2^(k) -1个结点:对不论什么一棵二叉树

【LeetCode-面试算法经典-Java实现】【102-Binary Tree Level Order Traversal(二叉树层序遍历)】

[102-Binary Tree Level Order Traversal(二叉树层序遍历)] [LeetCode-面试算法经典-Java实现][所有题目目录索引] 原题 Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). For example: Given binary tree {3,9,20,#,#,15

一步一步写算法(之二叉树广度遍历)

原文:一步一步写算法(之二叉树广度遍历) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在二叉树的遍历当中,有一种遍历方法是不常见的,那就是广度遍历.和其他三种遍历方法不同,二叉树的广度遍历需要额外的数据结构来帮助一下?什么数据结构呢?那就是队列.因为队列具有先进先出的特点,这个特点要求我们在遍历新的一层数据之前,必须对上一次的数据全部遍历结束.暂时还没有掌握队列知识的朋友可以看一看我的这一篇博客-队列. a)下面是新添加的队列数据结构

一步一步写算法(之二叉树深度遍历)

原文:一步一步写算法(之二叉树深度遍历) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 深度遍历是软件开发中经常遇到的遍历方法.常用的遍历方法主要有下面三种:(1)前序遍历:(2)中序遍历:(3)后序遍历.按照递归的方法,这三种遍历的方法其实都不困难,前序遍历就是根-左-右,中序遍历就是左-根-右,后续遍历就是左-右-根.代码实现起来也不复杂. 1)前序遍历 void preorder_traverse(TREE_NODE* pTree

算法之二叉树各种遍历 (转)

树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用. 二叉树是每个结点最多有两个子树的有序树.通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个结点:深度为k的二叉树至多有2^(k) -1个结点:对任何一棵二叉树T,如果其终端结点数(即叶子结点数)为n0,

转 算法之二叉树各种遍历

原文:http://blog.csdn.net/sjf0115/article/details/8645991 树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常用. 二叉树是每个结点最多有两个子树的有序树.通常子树的根被称作“左子树”(left subtree)和“右子树”(right subtree).二叉树常被用作二叉查找树和二叉堆或是二叉排序树.二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2的 i -1次方个