二叉树的递归和非递归遍历

来源:互联网 发布:桌面美化软件 编辑:程序博客网 时间:2024/06/05 16:59

前段时间瞄了一眼《剑指offer》上面有说要熟悉二叉树的6种遍历方式,说来你或许不信,我自己实现非递归遍历的时候硬是没有实现出来,看来自己的基本功还是有待加强的。话不多说,上代码加分析。

二叉树的实现:

这个基本上是轻车熟路了,平常写书的算法的时候都要求实现一遍,当然这里不是这篇文章的重点。

树的节点的定义和树的定义。

struct TreeNode {    TreeNode *lChild;  //左孩子    TreeNode *rChild; //右孩子    EleType value;       //节点中的值};struct Tree {    TreeNode *root;   //树可以直接用一个节点的指针来表示};
树的实现。

TreeNode *CreateTreeNode (EleType k) {  //这个函数的意义是分配一个树的节点并且初始化    TreeNode *temp = (TreeNode *) malloc (sizeof (TreeNode));    temp->lChild = nullptr;    temp->rChild = nullptr;    temp->value = k;    return temp;}void CreateTree (TreeNode **root) {    int t;    cin >> t;    if (t != 0) {   //为零的话代表其是叶节点        *root = CreateTreeNode (t);        CreateTree (&((*root)->lChild));//利用递归实现树        CreateTree (&((*root)->rChild));    }}
二叉树的前序遍历:

二叉树的前序遍历的递归实现。

void PreTravelTree (TreeNode *node) {    if (node != nullptr) {        cout << node->value << " ";        PreTravelTree (node->lChild);        PreTravelTree (node->rChild);    }}
二叉树的前序遍历的非递归实现。

void PreTravelTreeNonRecur (TreeNode *node) {    stack<TreeNode *> nodeStack;    TreeNode *cur = node;    while (cur != nullptr || !nodeStack.empty ()) {        while (cur != nullptr) {            cout << cur->value << " ";            nodeStack.push (cur);            cur = cur->lChild;        }        if (!nodeStack.empty ()) {            cur = nodeStack.top ();            nodeStack.pop ();            cur = cur->rChild;        }    }
二叉树的中序遍历:

二叉树的中序遍历的递归实现。

void MidTravelTree (TreeNode *node) {    if (node != nullptr) {        MidTravelTree (node->lChild);        cout << node->value << " ";        MidTravelTree (node->rChild);    }}
二叉树的中序遍历的非递归实现。

void MidTravelTreeNonRecur (TreeNode *node) {    stack<TreeNode *> nodeStack;    TreeNode *cur = node;    while (cur != nullptr || !nodeStack.empty ()) {        while (cur != nullptr) {            nodeStack.push (cur);            cur = cur->lChild;        }        if (!nodeStack.empty ()) {            cur = nodeStack.top ();            cout << cur->value << " ";            nodeStack.pop ();            cur = cur->rChild;        }    }}
二叉树的后序遍历:

二叉树的后序遍历的递归实现。

void PosTravelTree (TreeNode *node) {    if (node != nullptr) {        PosTravelTree (node->lChild);        PosTravelTree (node->rChild);        cout << node->value << " ";    }}
二叉树的后序遍历的非递归实现。

这个是这篇文章的重点了,由于二叉树的后序遍历不同于前序遍历和中序遍历,必须要使用某种方式标记出左右字节点是否已经访问过了,那么在这里其实有两种方式,一种方式是改变底层的数的节点的数据结构,加上访问标记。第二种就是使用不同的进栈方式,在父节点进栈的时候同时将两个子节点进栈。下面先介绍第一种方式,改变底层的数据结构。

(底层数据结构只是加上了visited的字段,并没有其他的不同,不再赘述)

void PosTravelTreeNonRecurSec (TreeNodePlus *node) {    stack<TreeNodePlus *> nodeStack;    TreeNodePlus *cur = node;    while (cur != nullptr || !nodeStack.empty ()) {        while (cur != nullptr) {  //在这个while循环当中,只将不是nullptr同时没有访问过的节点加入nodeStack            cur->visited++;            nodeStack.push (cur);            if (cur->lChild == nullptr) {                break;            }else if (cur->lChild->visited > 0) {                break;            }else {                cur = cur->lChild;            }        }        if (!nodeStack.empty ()) { //进入这里就说明其子节点的一个孩子或者两个孩子已经访问过了(这里有一个特殊情况            cur = nodeStack.top ();//就是子节点为nullptr也可以进入这里,但是子节点为nullptr也代表访问过了)            cur->visited++;            if (cur->visited == 3) {  //说明这个节点的左右子节点已经访问过了,那么就可以将这个节点打印出来                cout << cur->value << " ";                nodeStack.pop ();                cur = nullptr;  //将其设置为nullptr那么下个循环就可以跳过上面那么while循环,处理这个节点的父节点。            }else {                cur = cur->rChild;//说明这个节点的右节点还没有访问,那么把当前节点的值设置为右节点。            }        }    }}
第二种方式就是使用不同的进栈方式。

void PosTravelTreeNonRecur (TreeNode *node) {    stack<pair<TreeNode * , bool>> nodeStack; //这个栈的节点是一个pair    TreeNode *cur = node;    nodeStack.push (make_pair (cur , false));    bool visited;    while (!nodeStack.empty ()) {        cur = nodeStack.top().first;        visited = nodeStack.top().second;        nodeStack.pop ();        if (cur == nullptr)            continue;        if (visited)            cout << cur->value << " ";        else {  //使用精心设计的进栈方式,根节点放在最下面,然后再是右节点,再是坐节点,这样就保证取出来的时候            nodeStack.push (make_pair(cur , true));  //左节点可以最先取出来。同时,其他两种的遍历方式的非递归实现 nodeStack.push (make_pair(cur->rChild , false)); //也可以采用这种进栈方式,但是缺点是效率稍微低了一些,进栈            nodeStack.push (make_pair(cur->lChild , false)); //次数多了。        }    }}




0 0