二叉树总结

本文详细介绍了二叉树的概念,包括前序、中序、后序、分层等遍历方法,以及二叉树的翻转、深度、宽度、叶子节点数等操作。同时,探讨了求树的直径、节点到根节点的路径、最近公共祖先和两节点间路径等问题,提供递归和非递归的解决方案,并讨论了完全和平衡二叉树的判断条件。
摘要由CSDN通过智能技术生成

什么是二叉树?

引用自百度百科
在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree),同样的左右子树也都是二叉树.

前言

本文代码实现为 C/C++,因为不是一个完整的讲解文章,只是个人思路,所以说思路讲解可能有不足之处,有错误请指出.

节点定义

使用单向链表的形式,只保存当前节点的子节点和权值,不保存父节点.

typedef struct Node{
    int value;//权值,根据实际情况而定,这里用数字
    Node *lChild;//左儿子节点
    Node *rChild;//右儿子节点
}BNode;

二叉树的遍历

二叉树的遍历分为三种:

  • 前序遍历
    先遍历当前节点(根节点),然后遍历左儿子节点,再遍历右儿子节点
  • 中序遍历
    先遍历左儿子节点,然后遍历当前节点(根节点),再遍历右儿子节点
  • 后序遍历
    先遍历左儿子节点,然后遍历右儿子节点,再遍历当前节点(根节点)

我们创建二叉树的时候一般用的是递归的方法,但是二叉树的遍历可以有递归和非递归两种方式.下面会分别给出二叉树遍历递归和非递归的思路和代码.

PS:建议看懂思路后再看代码实现,然后手动模拟一下,效果更佳.

前序遍历

递归版:
先遍历根节点,然后遍历左子树,再遍历右子树
非常经典的递归思想,理解二叉树的性质和递归思想即可得出.

void preorderTraversal(BNode *rootNode) {
    if(rootNode != NULL) {
        printf("%d ", rootNode->value);
        preorderTraversal(rootNode->lChild);
        preorderTraversal(rootNode->rChild);
    }
}

非递归版:

使用栈来实现,因为 C++ 中有现成的库,所以说就不手动模拟栈了,当遍历到一个节点的时候,先将此节点输出,然后一直遍历其左儿子,依次循环,直到碰到叶子节点,然后开始弹,也就是递归过程中的回溯。

对于任一结点node:

  • 访问结点node,并将结点node入栈;
  • 判断结点node的左孩子是否为空
    • 若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点node,循环至1
    • 若不为空,则将node的左孩子置为当前的结点node;
  • 直到node为NULL并且栈为空,则遍历结束。
void preorderTraversalNonrecursive(BNode *rootNode) {
    stack<BNode *>s;
    if (rootNode == NULL ) {
        return ;
    }
    BNode *tempNode = rootNode;
    while(!s.empty() || tempNode != NULL) {
        while(tempNode != NULL) {
            printf("%d ", tempNode->value);
            s.push(tempNode);
            tempNode = tempNode->lChild;
        }
        if (!s.empty()) {
            tempNode = s.top();
            s.pop();
            tempNode = tempNode->rChild;
        }
    }
}

中序遍历

递归版:
先遍历左子树,然后遍历根节点,再遍历右子树

void inorderTraversal(BNode *rootNode) {
    if(rootNode != NULL) {
        inorderTraversal(rootNode->lChild);
        printf("%d ", rootNode->value);
        inorderTraversal(rootNode->rChild);
    }
}

非递归版:
和先序遍历一样的思想,因为要先访问左子树,然后再访问根节点(当前节点),那么在栈弹出的时候进行输出即为所求.

对于任一结点node:

  • 若其左孩子不为空,则将node入栈并将node的左孩子置为当前的node,然后对当前结点P再进行相同的处理;
  • 若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的node置为栈顶结点的右孩子;
  • 直到node为NULL并且栈为空则遍历结束
void inorderTraversalNonrecursive(BNode *rootNode) {
    stack<BNode *>s;
    if (rootNode == NULL ) {
        return ;
    }
    BNode *tempNode = rootNode;
    while(!s.empty() || tempNode != NULL) {
        while(tempNode != NULL) {
            s.push(tempNode);
            tempNode = tempNode->lChild;
        }
        if (!s.empty()) {
            tempNode = s.top();
            s.pop();
            printf("%d ", tempNode->value);
            tempNode = tempNode->rChild;
        }
    }
}

后序遍历

递归版:
先遍历左子树,然后遍历右子树,再遍历根节点

void postorderTraversal(BNode *rootNode) {
    if(rootNode != NULL) {
        postorderTraversal(rootNode->lChild);
        postorderTraversal(rootNode->rChild);
        printf("%d ", rootNode->value);
    }
}

非递归版:
后序遍历的非递归版和先序遍历、中序遍历不一样,这里我用两个栈来实现

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值