【数据结构】【王道】【树与二叉树】二叉树层序遍历实现(可直接运行)

总目录:https://blog.csdn.net/treesorshining/article/details/125726400

1.说明

层序遍历即是一层一层从左至右访问二叉树每个结点,类似于广度优先遍历。

实现思路:借助一个辅助队列,从根节点开始,根结点入队,访问此结点后出队,并将此结点的左右孩子入队(如果有),再依次对队头结点执行上述操作,直至队列为空为止。

2.算法实现

// 层序遍历
// 即一层一层从左至右访问每个结点,即广度优先遍历
// 此操作需要一个辅助队列,从根结点开始入队,访问此结点后出队,并将此结点的左右孩子入队(如果有)
// 再对每一个入队结点重复上述操作直至遍历完成(队列空)
void LevelOrder(BiTree T) {
    LinkQueue Q;
    // 初始化辅助队列
    InitQueue(Q);
    BiTree p;
    // 根节点入队
    EnQueue(Q, T);
    // 队列不空则一直循环
    while(!IsEmpty(Q)) {
        // 队头结点出队
        DeQueue(Q, p);
        // 访问出队结点
        visit(p);
        // 访问出队结点
        if(p->lchild != NULL) {
            // 左孩子入队
            EnQueue(Q, p->lchild);
        }
        if(p->rchild != NULL) {
            // 右孩子入队
            EnQueue(Q, p->rchild);
        }
        // 释放空间
        free(p);
    }
}

3.完整代码

#include<stdio.h>
#include<stdlib.h>

typedef struct BiTNode {
    // 数据域
    char data;
    // 左、右孩子指针
    struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;

typedef struct LinkNode {   // 链式队列结点
    // 此处存的是指针而不是结点,可节省大量空间
    BiTNode* data;   // 数据域
    struct LinkNode *next;  // 指针域
} LinkNode;

typedef struct {    // 链式队列
    // 如果不设置尾指针,若要插入,遍历到队尾再插入,时间复杂度O(n)
    LinkNode *front, *rear; // 队列的队头和队尾指针
} LinkQueue;

// 初始化
void InitTree(BiTree &T) {
    // 初始化可将根节点置空,空树
    T = NULL;
    // 插入根节点
    // 分配空间
    T = (BiTree)malloc(sizeof(BiTree));
    // 赋值
    T->data = 'A';
    // 初始无左右子树
    T->lchild = NULL;
    T->rchild = NULL;
}

// 左插入结点
bool InsertLeftTreeNode(BiTNode* &T, char x) {
    // 分配空间
    BiTNode *p = (BiTNode*)malloc(sizeof(BiTNode));
    // 分配空间失败的情况
    if(p == NULL) {
        return false;
    }
    // 数据域赋值
    p->data = x;
    // 初始插入,无左右孩子,置空
    p->lchild = NULL;
    p->rchild = NULL;
    // 作为指定插入结点的左孩子
    T->lchild = p;
    return true;
}

// 右插入结点
bool InsertRightTreeNode(BiTNode* &T, char x) {
    // 分配空间
    BiTNode *p = (BiTNode*)malloc(sizeof(BiTNode));
    // 分配空间失败的情况
    if(p == NULL) {
        return false;
    }
    // 数据域赋值
    p->data = x;
    // 初始插入,无左右孩子,置空
    p->lchild = NULL;
    p->rchild = NULL;
    // 作为指定插入结点的左孩子
    T->rchild = p;
    return true;
}

// 访问结点
void visit(BiTree T) {
    printf("%c   ", T->data);
}

// 树的深度
int treeDepth(BiTree T) {
    // 根结点为空,则深度为0
    if(T == NULL) {
        return 0;
    } else {
        // 递归遍历左子树,获得左子树深度
        int l = treeDepth(T->lchild);
        // 递归遍历右子树,获得右子树深度
        int r = treeDepth(T->rchild);
        // 树的深度=Max{左子树深度,右子树深度} + 1
        // 加1相当于访问根节点,包括根结点深度+1
        return l > r ? l + 1 : r + 1;
    }
}

// 初始化(带头结点)
void InitQueue(LinkQueue &Q) {
    // 初始时为头结点分配空间,并令头指针、尾指针都指向头结点
    Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
    // 将头结点指针域置空
    Q.front->next = NULL;
}

// 判断队列是否为空
bool IsEmpty(LinkQueue Q) {
    // 头指针与尾指针指向同一地址(头结点即表示队列为空)
    // 或者也可以根据Q.front->next = NULL来进行判定
    if(Q.front == Q.rear) {
        return true;
    } else {
        return false;
    }
}

// 入队
bool EnQueue(LinkQueue &Q, BiTNode *x) {
    // 申请分配空间
    LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));
    // 内存分配失败的情况
    if(s == NULL) {
        return false;
    }
    // 将数据赋值给数据域
    s->data = x;
    // 由于队列必定在队尾插入,所以指针域要设为NULL
    s->next = NULL;
    // 入队操作
    Q.rear->next = s;
    // 修改队尾指针
    Q.rear = s;
    return true;
}

// 出队操作
bool DeQueue(LinkQueue &Q, BiTNode* &x) {
    // 空队的情况
    if(Q.front == Q.rear) {
        return false;
    }
    // 由于队列出队操作一定是在队头进行,所以以一临时指针指向队头结点(非头结点,是队列第一个结点)
    LinkNode *p = Q.front->next;
    // 将队头元素赋值给x
    x = p->data;
    // 令队头指针指向队头元素下一个元素
    Q.front->next = p->next;
    // 出队为最后一个结点的情况
    if(Q.rear == p) {
        // 修改尾指针,使其指向头结点,以免之后释放空间时尾指针指向未知地点
        // 同时,也表示此队列已是空队列
        Q.rear = Q.front;
    }
    // 释放空间
    // 由于之后还需要取分支节点的左右孩子,所以不能在此处释放空间
    // free(p);
    return true;
}

// 层序遍历
// 即一层一层从左至右访问每个结点,即广度优先遍历
// 此操作需要一个辅助队列,从根结点开始入队,访问此结点后出队,并将此结点的左右孩子入队(如果有)
// 再对每一个入队结点重复上述操作直至遍历完成(队列空)
void LevelOrder(BiTree T) {
    LinkQueue Q;
    // 初始化辅助队列
    InitQueue(Q);
    BiTree p;
    // 根节点入队
    EnQueue(Q, T);
    // 队列不空则一直循环
    while(!IsEmpty(Q)) {
        // 队头结点出队
        DeQueue(Q, p);
        // 访问出队结点
        visit(p);
        // 访问出队结点
        if(p->lchild != NULL) {
            // 左孩子入队
            EnQueue(Q, p->lchild);
        }
        if(p->rchild != NULL) {
            // 右孩子入队
            EnQueue(Q, p->rchild);
        }
        // 释放空间
        free(p);
    }
}

int main() {
    BiTree T;
    InitTree(T);
    InsertLeftTreeNode(T, 'B');
    InsertRightTreeNode(T, 'C');
    InsertLeftTreeNode(T->lchild, 'D');
    InsertRightTreeNode(T->lchild, 'E');
    InsertLeftTreeNode(T->rchild, 'F');
    InsertRightTreeNode(T->rchild, 'G');
    printf("\ntreeDepth:%d\n", treeDepth(T));
    printf("LevelOrder\n");
    LevelOrder(T);
}

4.运行结果

在这里插入图片描述

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
层序遍历是一种按照从上到下、从左到右的顺序访问二叉树节点的方法。通过层序遍历,我们可以逐层遍历二叉树的节点,并在遍历过程中进行判断,从而确定二叉树是否为完全二叉树层序遍历是一种广度优先搜索的遍历方式,适用于结构。通过利用队列实现层序遍历,我们可以按照从上到下、从左到右的顺序逐层遍历中的节点。具体实现方法如下: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def levelOrder(root: TreeNode) -> List[List[int]]: if not root: return [] res = [] queue = [root] while queue: level = [] for i in range(len(queue)): node = queue.pop(0) level.append(node.val) if node.left: queue.append(node.left) if node.right: queue.append(node.right) res.append(level) return res ``` 以上代码中,我们定义了一个`TreeNode`类来表示二叉树的节点,`levelOrder`函数用于实现二叉树层序遍历。在函数中,我们首先判断根节点是否为空,如果为空则直接返回空列表。然后定义一个`res`列表来存储遍历结果,定义一个`queue`队列来存储待遍历的节点。接下来,我们使用一个`while`循环来遍历整个二叉树。在每一层遍历中,我们定义一个`level`列表来存储当前层的节点值,然后使用一个`for`循环来遍历当前层的所有节点。在循环中,我们首先弹出队列中的第一个节点,并将其值加入到`level`列表中。然后判断该节点是否有左右子节点,如果有则将其左右子节点加入到队列中。最后将`level`列表加入到`res`列表中,表示当前层的遍历已经完成。最终返回`res`列表即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Silver Star

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值