C++使用递归和非递归的方式实现二叉树的遍历

二叉树是常见的数据结构,常见的有三种遍历方式:前序遍历(根→左→右)、中序遍历(左→根→右)和后序遍历(左→右→根)。使用递归的方式实现遍历比较简单,书上看到一个不使用递归的方法比较复杂,记录一下。

二叉树节点定义

typedef struct node {
    int value;
    struct node *left;
    struct node *right;
}Node;

前序遍历(递归)

递归方式实现前序遍历,先打印根节点,然后依次把左节点和右节点当做根节点来打印。

void Tree::preOrderRecur(Node *tree) {
    if (!tree) return;

    std::cout << tree->value << " ";
    preOrderRecur(tree->left);
    preOrderRecur(tree->right);
}

前序遍历(非递归)

非递归遍历使用栈结构(先进先出)来暂存节点,先打印根节点,同时依次把右节点和左节点压到栈中。接着取栈顶节点作为根节点进行打印,然后再依次把右节点和左节点压到栈中。。。这样可以始终保持先打印的是根节点,且左节点比右节点先打印。

void Tree::preOrderUnRecur(Node *tree) {
    if (!tree) return;

    std::stack<Node*> nodeStack;
    nodeStack.push(tree);
    Node *root = nullptr;
    while (!nodeStack.empty()) {
        root = nodeStack.top();
        nodeStack.pop();
        std::cout << root->value << " ";

        if (root->right)
            nodeStack.push(root->right);

        if (root->left)
            nodeStack.push(root->left);
    }
}

中序遍历(递归)

递归方式依次打印左、根、右节点。

void Tree::inOrderRecur(Node *tree) {
    if (!tree) return;

    inOrderRecur(tree->left);
    std::cout << tree->value << " ";
    inOrderRecur(tree->right);
}

中序遍历(非递归)

先依次把最左边的节点压到栈中,(这样弹出节点打印的时候可以保证先打印左节点再打印根节点)。一旦检测到左节点为空,就弹出栈顶的节点并打印该节点(此时可以理解为根节点)。接着把其右节点作为根节点循环处理。

void Tree::inOrderUnRecur(Node *tree) {
    if (!tree) return;

    std::stack<Node*> nodeStack;
    Node *root = tree;

    while (!nodeStack.empty() || root) {
       if (root) {
           nodeStack.push(root);
           root = root->left;
       } else {
           root = nodeStack.top();
           std::cout << root->value << " ";

           nodeStack.pop();
           root = root->right;
       }
    }
}

后序遍历(递归)

依次处理左右节点再打印根节点。

void Tree::posOrderRecur(Node *tree) {
    if (!tree) return;

    posOrderRecur(tree->left);
    posOrderRecur(tree->right);
    std::cout << tree->value << " ";
}

后序遍历(非递归)

把根节点压入栈中,记录栈顶元素为current当前节点。如果左节点不为空则压入栈中,否则把右节点压入栈中(不为空时)。如果当前节点的左右节点都为空则打印当前节点,(可以保证根节点最后打印)。
压入右节点的时候要保证上一次没有打印过右节点,避免根节点和右节点打印后再压栈变成死循环。压入左节点的时候也要保证上次打印不是做节点,且右节点不存在或者已经处理过了,(可以保证左节点始终在右节点的前面被打印)。

void Tree::posOrderUnRecur(Node *tree) {
    if (!tree) return;

    std::stack<Node*> nodeStack;
    nodeStack.push(tree);

    Node *last = nullptr;
    Node *current = nullptr;

    while (!nodeStack.empty()) {
        current = nodeStack.top();

        if ((current->left) && (last != current->left) 
            && ((last != current->right) || !current->right)) {
            nodeStack.push(current->left);
        } else if(current->right && (last != current->right)) {
            nodeStack.push(current->right);
        } else {
            std::cout << current->value << " ";
            last = current;
            nodeStack.pop();
        }
    }
}

二叉树的创建和销毁

这里我使用一个类似二叉树层序遍历的数组的形式创建二叉树(避免歧义使用-1代表空节点),销毁二叉树要释放所有申请空间的节点。

// 创建
Node *Tree::createBiTree(const int *arr, int size, int index) {
    if (index >= size || arr[index] == -1){
        return nullptr;
    }

    Node *node = new Node;

    if (index != size) {
        node->value = arr[index];
        node->left = createBiTree(arr, size, 2*(index+1)-1);
        node->right = createBiTree(arr, size, 2*(index+1));
    } else {
        node->left = NULL;
        node->right = NULL;
    }
    return node;
}

// 销毁
void Tree::destoryBiTree(Node *tree) {
    if (tree == NULL)
        return;

    destoryBiTree(tree->left);
    if (tree->left)
        tree->left = NULL;

    destoryBiTree(tree->right);
    if (tree->right)
        tree->right = NULL;

    delete tree;
}

测试

int main() 
{

     /*  
     * 使用如下二叉树
     *             1
     *         2       3
     *      4    5   6   7
     *    8  x x  x x x 9  10
     * /
    int array[15] = {1,2,3,4,5,6,7,8,-1,-1,-1,-1,-1,9,10};
    Tree *ins = new Tree;
    Node *tree = ins->createBiTree(array, 15, 0);

    std::cout << "\n前序遍历(递归):" << std::endl;
    ins->preOrderRecur(tree);

    std::cout << "\n前序遍历(非递归):" << std::endl;
    ins->preOrderUnRecur(tree);

    std::cout << "\n中序遍历(递归):" << std::endl;
    ins->inOrderRecur(tree);

    std::cout << "\n中序遍历(非递归):" << std::endl;
    ins->inOrderUnRecur(tree);

    std::cout << "\n后序遍历(递归):" << std::endl;
    ins->posOrderRecur(tree);

    std::cout << "\n后序遍历(非递归):" << std::endl;
    ins->posOrderUnRecur(tree);

    ins->destoryBiTree(tree);
}

程序输出:

前序遍历(递归):
1 2 4 8 5 3 6 7 9 10 
前序遍历(非递归):
1 2 4 8 5 3 6 7 9 10 
中序遍历(递归):
8 4 2 5 1 6 3 9 7 10 
中序遍历(非递归):
8 4 2 5 1 6 3 9 7 10 
后序遍历(递归):
8 4 5 2 6 9 10 7 3 1 
后序遍历(非递归):
8 4 5 2 6 9 10 7 3 1 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值