二叉树详解及二叉树的遍历(递归与非递归C++算法实现)

二叉树详解及二叉树的遍历(递归与非递归C++算法实现)

二叉树简介

树(Tree)

是一种由多个节点组成的有限集合T,有且仅有一个节点称为根(root),其余结点分为m(大于等于0)个互不相交的有限集合T1,T2,T3…;每个集合本身又是棵树,被称为这个根的子树。

在树的定义中规定了树含有结点数必须大于0,这表明空集不可以称为树;他又规定结点可以为1,该结点就是根节点。

节点、根节点、父节点、子节点、兄弟节点
② 一棵树可以没有任何节点,成为空树
③ 一棵树可以只有1个节点,也就是只有根节点
④ 子树、左子树、右子树
⑤ 节点的度(degree):子树的个数
⑥ 树的度:所有节点度中的最大值
⑦ 叶子节点(leaf): 度为0的节点
⑧ 非叶子节点:度不为0的节点

树的存储结构

  1. 结点异构型

根据结点的子树数设置相应的指针域,同时在结点的数据中再增加一个表示结点的度的域,以了解这个结点具有的指针域。

  1. 结点同构型

以这棵树的度作为结点的指针域数目,每个结点的长度一样,但是树中很多结点的度小于树的度,导致存储浪费。

二叉树(Binary Tree)

二叉树的特点

  • 每个节点的度最大为2(最多拥有2棵子树)
  • 左子树和右子树是有顺序的
  • 即使某节点只有一颗子树,也要区分左右子树。

二叉树具有五种基本形态:

  • 空二叉树。
  • 只有一个根节点
  • 根节点只有左子树
  • 根节点只有右子树
  • 根节点既有左子树又有右子树。

满二叉树

在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
满二叉树的特点有:

  • 叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
  • 非叶子结点的度一定是2。
  • 在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多

在这里插入图片描述

完全二叉树

对一颗具有n个结点的二叉树按层编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。

在这里插入图片描述

二叉树的基本性质

  1. 二叉树的第i层至多有2^(n-1)个结点;

  2. 深度为k的二叉树至多有2^k - 1个结点

  3. 对任意一颗二叉树,若2度结点数为N2 ,则叶子数为 N0 = N2 + 1;

完全二叉树的重要性质:

  1. 在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[x]是向下取整。
  2. 若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:
(1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。

二叉树的遍历(递归与非递归C++算法实现)

先序遍历

先访问根结点A,然后访问左子树B,以左子树结点B为根结点,继续,先访问左子树根结点B,再访问B的左子树结点C,如果没有左子树,则访问右子树结点D,然后继续照此遍历树中所有结点。

遍历顺序:

  • 先访问根结点
  • 先序遍历左子树
  • 先序遍历右子树

在这里插入图片描述

在这里插入图片描述

/**
 * 非递归实现先序遍历
 * 先访问根结点,再访问左子树,再访问右子树
 * @param root 根结点
 */
void pre_traversal(BTreeNode* root) {
    stack<BTreeNode*> node_stack;       //用来暂存节点的栈
    while (root != nullptr || !node_stack.empty()) {
        if (root != nullptr) {                     //若当前的节点非空,
            cout << root->val << " ";         //则输出该节点的值
            node_stack.push(root);                 //该节点压入栈中。
            root = root->leftchild;                // 我们继续向左子树前进
        }
        else {
            root = node_stack.top();
            node_stack.pop();
            root = root->rightchild;
        }
    }
}


/**
 * 递归实现先序遍历
 * 先访问根结点,再访问左子树,再访问右子树
 * @param root 根结点
 */
void PreOrder(BTreeNode* root){
    if(root!=NULL){
        visit(root);
        PreOrder(root->leftchild);
        PreOrder(root->rightchild);
    }
}

中序遍历

同先序遍历类似,只不过这里先访问的左子树结点,在访问根结点

按照以下顺序进行遍历

  • 中序遍历左子树
  • 访问根结点
  • 中序遍历右子树

在这里插入图片描述

/**
 * 非递归实现中序遍历
 * 先访问左子树,再访问根结点,再访问右子树
 * 通过栈来存储二叉树结点
 * @param root 根结点
 */
void in_traversal(BTreeNode* root) {
    stack<BTreeNode*> stack_node;
    while (root != nullptr || !stack_node.empty()) {
        if (root != nullptr) {
            stack_node.push(root);
            root = root->leftchild;
        }
        else {
            root = stack_node.top();
            cout << root->val << " ";
            stack_node.pop();
            root = root->rightchild;
        }
    }
}

/**
 * 递归实现中序遍历
 * 先访问左子树,再访问根结点,再访问右子树
 * @param root 根结点
 */
void InOrder(BTreeNode* root){
    if(root!=NULL){
        InOrder(root->leftchild);
        visit(root);
        InOrder(root->rightchild);
    }
}

后序遍历

按照以下顺序进行遍历

  • 后序遍历左子树
  • 后序遍历右子树
  • 访问根节点

在这里插入图片描述

/**
 * 非递归实现后序遍历
 * 先访问左子树,再访问右子树,再访问根结点
 * 通过栈来存储二叉树结点
 * @param root 根结点
 */
void post_traversal(BTreeNode* root) {
    stack<BTreeNode*> stack_node;
    BTreeNode* lastvisit = root;
    while (root != nullptr || !stack_node.empty()) {
        if (root != nullptr) {
            stack_node.push(root);
            root = root->leftchild;
        }
        else {
            root = stack_node.top();
            if (root->rightchild == nullptr || root->rightchild == lastvisit) {

                cout << root->val << " ";
                stack_node.pop();
                lastvisit = root;
                root = nullptr;
            }
            else {
                root = root->rightchild;
            }
        }
    }
}

/**
 * 递归实现后序遍历
 * 先访问左子树,再访问右子树,再访问根结点
 * @param root 根结点
 */
void PostOrder(BTreeNode* root){
    if(root!=NULL){
        PostOrder(root->leftchild);
        PostOrder(root->rightchild);
        visit(root);
    }
}

层次遍历

从根节点开始,一层一层,从上到下,每层从左到右,依次遍历下去。

void LevelOrder(BTreeNode* root){
    BTreeNode* p; // 创建辅助队列
    queue<BTreeNode*> q;
    q.push(root); //根结点入队
    while(!q.empty()){
        p = q.front();
        visit(p);//访问结点
        q.pop(); //对头结点出队
        if(p->leftchild!=NULL){
            q.push(p->leftchild);
        }
        if(p->rightchild!=NULL){
            q.push(p->rightchild);
        }
    }
}

遍历总结

结果:

通过二叉树遍历规律得出的结果如下图所示:

在这里插入图片描述

练习:

这里给出另外一个二叉树及其先中后序遍历及层次遍历的结果,可以试着自己遍历试试看,是否得到同样的结果。
在这里插入图片描述

代码实现

通过运行代码实现效果如下所示:

在这里插入图片描述

完整代码

//
// Created by caifl on 12/7/2021.
//
/**
 * 二叉树的先序、中序、后序、层序遍历思路及算法实现
 */

#include "iostream"
#include "vector"
#include "stack"
#include "queue"
using namespace std;

struct BTreeNode { //二叉树结点
    int val;
    BTreeNode* leftchild; //左子树结点
    BTreeNode* rightchild; //右子树结点
    // 二叉树结点结构体构造函数
    BTreeNode(char const& _val, BTreeNode* _leftchild=NULL, BTreeNode* _rightchild=NULL) :
            val(_val), leftchild(_leftchild), rightchild(_rightchild) {}
};

/**
 * 访问结点值
 * @param node 树结点
 */
void visit(BTreeNode* node){
    cout<<char(node->val)<<" ";
}

/**
 * 非递归实现先序遍历
 * 先访问根结点,再访问左子树,再访问右子树
 * @param root 根结点
 */
void pre_traversal(BTreeNode* root) {
    stack<BTreeNode*> node_stack;       //用来暂存节点的栈
    while (root != nullptr || !node_stack.empty()) {
        if (root != nullptr) {                     //若当前的节点非空,
            visit(root);      //则输出该节点的值
            node_stack.push(root);                 //该节点压入栈中。
            root = root->leftchild;                // 我们继续向左子树前进
        }
        else {
            root = node_stack.top();
            node_stack.pop();
            root = root->rightchild;
        }
    }
}

/**
 * 非递归实现中序遍历
 * 先访问左子树,再访问根结点,再访问右子树
 * 通过栈来存储二叉树结点
 * @param root 根结点
 */
void in_traversal(BTreeNode* root) {
    stack<BTreeNode*> stack_node;
    while (root != nullptr || !stack_node.empty()) {
        if (root != nullptr) {
            stack_node.push(root);
            root = root->leftchild;
        }
        else {
            root = stack_node.top();
            visit(root);
            stack_node.pop();
            root = root->rightchild;
        }
    }
}

/**
 * 非递归实现后序遍历
 * 先访问左子树,再访问右子树,再访问根结点
 * 通过栈来存储二叉树结点
 * @param root 根结点
 */
void post_traversal(BTreeNode* root) {
    stack<BTreeNode*> stack_node;
    BTreeNode* lastvisit = root;
    while (root != nullptr || !stack_node.empty()) {
        if (root != nullptr) {
            stack_node.push(root);
            root = root->leftchild;
        }
        else {
            root = stack_node.top();
            if (root->rightchild == nullptr || root->rightchild == lastvisit) {

                visit(root);
                stack_node.pop();
                lastvisit = root;
                root = nullptr;
            }
            else {
                root = root->rightchild;
            }
        }
    }
}



/**
 * 递归实现先序遍历
 * 先访问根结点,再访问左子树,再访问右子树
 * @param root 根结点
 */
void PreOrder(BTreeNode* root){
    if(root!=NULL){
        visit(root);
        PreOrder(root->leftchild);
        PreOrder(root->rightchild);
    }
}

/**
 * 递归实现中序遍历
 * 先访问左子树,再访问根结点,再访问右子树
 * @param root 根结点
 */
void InOrder(BTreeNode* root){
    if(root!=NULL){
        InOrder(root->leftchild);
        visit(root);
        InOrder(root->rightchild);
    }
}

/**
 * 递归实现后序遍历
 * 先访问左子树,再访问右子树,再访问根结点
 * @param root 根结点
 */
void PostOrder(BTreeNode* root){
    if(root!=NULL){
        PostOrder(root->leftchild);
        PostOrder(root->rightchild);
        visit(root);
    }
}

/**
 * 层次遍历二叉树
 */
void LevelOrder(BTreeNode* root){
    BTreeNode* p; // 创建辅助队列
    queue<BTreeNode*> q;
    q.push(root); //根结点入队
    while(!q.empty()){
        p = q.front();
        visit(p);//访问结点
        q.pop(); //对头结点出队
        if(p->leftchild!=NULL){
            q.push(p->leftchild);
        }
        if(p->rightchild!=NULL){
            q.push(p->rightchild);
        }
    }
}


/**
 * 测试方法
 */
void test(){
    //构建二叉树
    BTreeNode* A = new BTreeNode('A');
    BTreeNode* B = new BTreeNode('B');
    BTreeNode* C = new BTreeNode('C');
    BTreeNode* D = new BTreeNode('D');
    BTreeNode* E = new BTreeNode('E');
    BTreeNode* F = new BTreeNode('F');
    BTreeNode* G = new BTreeNode('G');
    BTreeNode* H = new BTreeNode('H');
    BTreeNode* I = new BTreeNode('I');
    BTreeNode* J = new BTreeNode('J');
    BTreeNode* K = new BTreeNode('K');
    A->leftchild=B;
    A->rightchild=C;
    B->leftchild=D;
    B->rightchild=E;
    C->leftchild=F;
    C->rightchild=G;
    D->leftchild=H;
    D->rightchild=I;
    E->rightchild=J;
    F->rightchild=K;

    /**
     * 非递归实现
     */
	cout<<"非递归实现:"<<endl << "Pre order traversal:" << endl;
	pre_traversal(A); //先序遍历
	cout << endl<< "In order traversal:" << endl;
	in_traversal(A);  //中序遍历
	cout << endl << "Post order traversal:" << endl;
	post_traversal(A); //后序遍历

    /**
     * 递归实现
     */
    cout<<endl<<"递归实现"<<endl;
    cout<<"先序遍历:"<<endl;
    PreOrder(A);
    cout<<endl<<"中序遍历:"<<endl;
    InOrder(A);
    cout<<endl<<"后序遍历:"<<endl;
    PostOrder(A);

    //层次遍历
    cout<<endl<<"层次遍历:"<<endl;
    LevelOrder(A);

}

Reference

https://blog.csdn.net/chinesekobe/article/details/110874773

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小枫学IT

如果觉得有用的话,可以支持一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值