剑指Offer:[第6天 搜索与回溯算法(简单)]--->从上到下打印二叉树Ⅲ


一、题目描述

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

例如:
给定二叉树[3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回:

[
  [3],
  [20,9],
  [15,7]
]

提示:
节点总数 <= 1000


二、思路分析

注:思路分析中的一些内容和图片参考自力扣各位前辈的题解,感谢他们的无私奉献

思路

①按层打印:题目要求的二叉树的层序遍历又称为二叉树的广度优先搜索(BFS)。BFS通常借助队列的先入先出特性来实现。
②每层打印到一行:将本层全部结点打印到一行,并将下一层全部结点加入队列,以此类推,即可分为多行打印。
③按之字型打印:奇数行顺序打印,偶数行倒序打印。根据行数的奇偶性对打印的位置进行控制,奇数行从开头打印,偶数行则从行尾开始。
算法流程:
1、特例处理:当根结点为空,则返回空列表[]
2、初始化:定义用于返回的二维数组res,定义队列queue,将根结点入队
3、BFS循环:当队列queue为空时跳出;
----3.1 计算出队列的长度,也就是当前层的结点数。
----3.2 判断当前行是奇数行还是偶数行,对后面循环打印的起始位置进行设置
----3.3 当前层打印循环,循环次数为当前层结点数
------3.3.1 出队:队首元素出队,记为tmp
------3.3.2 打印:根据奇偶性不同,将tmp.val添加至res对应的地方
------3.3.3 添加子节点:若tmp的左(右)子结点不为空,则将左(右)子结点加入队列queue
4、返回值:返回二维数组res即可。
复杂度分析:
时间复杂度 O ( N ) \rm{O(N)} O(N)N为二叉树的结点数量,即BFS需循环N次。
空间复杂度 O ( N ) \rm{O(N)} O(N):最差情况下,即当树为平衡二叉树时,最多有N/2个树结点同时在queue中,使用O(N)大小的额外空间。


三、整体

整体代码如下

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */


/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
int** levelOrder(struct TreeNode* root, int* returnSize, int** returnColumnSizes){
    //树为空时
    if (!root) {
        *returnSize = 0;
        *returnColumnSizes = NULL;
        return NULL;
    }
    *returnSize = 0;                                         //返回数组的行数,即二叉树的高度
    int** res = (int**)malloc(sizeof(int*) * 10001);         //返回数组
    *returnColumnSizes = (int*)malloc(sizeof(int) * 10001);  //返回数组中每一行的列数
    struct TreeNode** queue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);  //新建队列
    int head = -1;  //head为队首下标,初始化为-1,代表没有元素
    int rear = 0;   //rear为队尾元素后面的位置,初始化为0,代表需要入的元素插入到这个地方 
    queue[++head] = root;  //将根结点插入,队首坐标往后挪
    rear++;                //队尾位置往后挪
    //遍历二叉树
    while (head < rear) {
        /*
        记录当前的rear,因为我们要对树的每一层进行遍历,每一层的结点数=rear-head
        而在遍历每一层过程中,要对各个结点左右孩子进行入队,此时rear又要变化
        所以定义一个preRear,根据这个preRear控制对当前层的遍历
        */
        int preRear = rear;
        res[*returnSize] = (int*)malloc(sizeof(int) * (preRear - head));   //记录当前行的各个结点的值
        (*returnColumnSizes)[*returnSize] = preRear - head;                //记录当前行的列数
        int col;  //记录当前行开始遍历的结点的下标
        
        //*returnSize代表当前是多少行,注意这里是从第0行开始,所以*returnSize为偶数时是奇数列
        if (*returnSize % 2 == 0) {
            col = 0;  //奇数列,则顺序对当前层结点进行遍历
        } 
        else{
            col = preRear - head - 1;  //偶数列,则逆序对当前层结点进行遍历
        }

        //遍历当前行
        for (; head < preRear; head++) {
            struct TreeNode* tmp = queue[head];   //出队
            if (*returnSize % 2 == 0) {
                res[*returnSize][col++] = tmp->val;
            } 
            else{
                res[*returnSize][col--] = tmp->val;
            }
            //左右孩子入队
            if (tmp->left) {
                queue[rear++] = tmp->left;
            }
            if (tmp->right) {
                queue[rear++] = tmp->right;
            }
        }
        (*returnSize)++;  //当前行处理完毕,行数+1
    }
    return res;
}

运行,测试通过
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

知初与修一

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

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

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

打赏作者

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

抵扣说明:

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

余额充值