二叉树最大宽度_深度优先方式_20230520

文章介绍了如何使用深度优先遍历方法来求解二叉树的最大宽度。在深度优先遍历过程中,通过比较根节点、左孩子和右孩子的宽度,不断回溯以更新最大宽度。关键在于记录每层最左侧非空节点的位置,并在递归中追踪节点的深度,以计算当前节点的最大宽度。程序实现包括递归函数,初始化最小索引数组,并在遍历二叉树结构时动态更新最大宽度。
摘要由CSDN通过智能技术生成

二叉树最大宽度_深度优先方式_20230520

  1. 前言

给定一颗二叉树,求解其最大宽度。定义每层宽度为该层最左和最右之间的长度,也即左右两个端点之间的所跨越的长度,两个端点直接可能会包含一些延伸到本层的空节点,这些空节点的长度由于占据了宽度符,所以也计入长度范围内。

此问题的常规解法包含广度优先和深度优先两种方式,每种方式都需要对二叉树结点重新进行线性化编号,类似Heap结点的编号规则,定义根节点的编号为index, 那么其直接左孩子结点的编号为2*index,同理其直接右孩子结点编号为2*index+1。

在这里插入图片描述

  1. 深度优先遍历

深度优先遍历,顾名思义,访问节点过程中,先从深度方向上对节点进行遍历,通过不断递归,最终访问所有的节点,要用深度优先方式求解此题,先看一个最基础的例子。以三个节点二叉树为例进行思路的阐述。

在这里插入图片描述

要求解树的宽度,先访问节点③,节点③的宽度为1;然后访问节点⑤,节点⑤的宽度也为1;最后对节点⑦进行访问,节点7的宽度为2. 在深度优先遍历过程中,需要比较根节点,左孩子节点以及右子树节点的宽度,选择三个中的最大值,赋予根节点,然后再不断的递归回退。
r o o t _ w i d t h = M A X ( r o o t _ w i d t h , l e f t _ c h i l d _ w i d t h , r i g h t _ c h i l d _ w i d t h ) root\_width=MAX(root\_width,left\_child\_width,right\_child\_width) root_width=MAX(root_width,left_child_width,right_child_width)
最后求得根结点代表的子树的最大宽度值为2。

如果把上述例子作为求解的最基础的实例,那么我们再将上述公式进行一般化处理,再看一个具体的例子。

在这里插入图片描述

如果采用递归遍历,按照前面的例子,完成结点③代表的左子树的最大宽度求解后,结点③代表的左子树的最大宽度值为2。

然后再对结点②代表的右子树的最大宽度进行求解,结点②本身的宽度为2,其左子树为空,那么默认其宽度为0,其右子树⑨的宽度为4,利用上述公式,以结点②为基础的右子树的最大宽度值为4。

再接着回退,对根节点①,其左结点③和右结点②进行宽度最大值的求解,最终根节点最大宽度更新为4。

在深度优先遍历过程中,面临的挑战之一为如何求解层中每个结点相对于左端点的最大宽度,深度优先遍历对每层中结点的访问次序并不是连续的,无法在遍历过程中,直接利用其前置结点的值。这种情况下,就需要对每次的最左侧的非空结点的位置进行储存记忆,同时在递归函数中需要追踪目前处理的结点在哪一层上,方便查询并求出宽度。

如果以上面的二叉树为例,那么我们需要记录第一层结点的①min_depth[1]的值为1,第二层结点③的min_depth[2]为2,第三层结点⑤的min_depth[3]为4,这三个值一旦确定,后续遍历过程中便不再进行更新,需要通过当前深度进行查询,然后和当前结点位置进行比较,最终求出其最大的宽度值。

以结点⑨为例,它本身的计算结点编号为7(2*3+1),它所在的层数 (深度)为3,通过数组查询min_depth[3]为4,那么结点⑨的最大宽度值:
M a x _ w i d t h   o f   n o d e  ⑨ = 7 − 4 + 1 = 4 Max\_width\ of\ node\ ⑨ = 7-4+1=4 Max_width of node =74+1=4
如果二叉树的深度比较小,那么可以采用普通的数组进行储存与查询,规定数组中的初值为-1,当深度遍历第一次访问到左节点的时候,更新其值为正确的值,后续对于相同的深度的节点则不再进行更新。

  1. 程序实现

基于上面的分析,则程序的实现就比较简单,主要是在递归回退过程中,对左子树、右子树和根的宽度进行比较,返回最大值即可,这种递归过程可以直观理解为层层返回,根据条件对值进行更改,最终在总的根节点上求出问题的答案。

由于需要追踪访问过程中的每个节点所在的层数(深度)和其线性化的编号,所以递归函数的设计,不仅仅需要节点,还需要记录层数的变量depth和线性化编号的变量index。

为了简化递归函数,我们定义记忆数组min_depth[]为全局变量,并在递归第一次到达当前层的时候,对数组的内容给予更新,同时利用init_array函数对其所有元素赋初值为-1。

3.1 头文件定义

/**
 * @file max_width.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-05-19
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef MAX_WIDTH_H
#define MAX_WIDTH_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define MAX_DEPTH 10
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int     min_index[MAX_DEPTH];


typedef struct BiTNode
{
    int val;
    struct BiTNode *lchild;
    struct BiTNode *rchild;
} BiTNode, *BiTree;

/*
利用先序遍历建立二叉树,如果值为-1,则默认为NULL
*/
void create_bitree(BiTree *T, FILE *fp);

/*
初始化min_depth数组所有元素为-1
*/
void init_array(int *arr,int n);

/**
 * @brief 
 * By using DFS to find the solution to maximum width of binary tree
 * it will return the maximum widhth of (root_width, left_child_width,right_child_widht)
 * @param root   Root node 
 * @param depth  Indidctor of depth of current node
 * @param index  Linear sequence index of current node(similar to heap number)
 * @return int   Return the max width of whole tree
 */
int max_width_of_binary_tree(BiTree root, int depth, int index);

#endif

3.2 关键函数的实现

/**
 * @file max_width.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-05-19
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef MAX_WIDTH_C
#define MAX_WIDTH_C
#include "max_width.h"

int max_width_of_binary_tree(BiTree root, int depth, int index)
{
    if(root==NULL)
    {
        return 0;
    }

    int root_width;
    int l_width=0;
    int r_width=0;

    if (min_index[depth]==-1)
    {
        min_index[depth]=index;
    }

    root_width = index - min_index[depth] + 1;

    if(root->lchild)
    {
        l_width=max_width_of_binary_tree(root->lchild,depth+1,index*2);
    }
    if(root->rchild)
    {
        r_width=max_width_of_binary_tree(root->rchild,depth+1,index*2+1);
    }

    root_width=MAX(MAX(root_width,l_width),r_width);

    return root_width;

}

void create_bitree(BiTree *T, FILE *fp)
{
    int val;

    // printf("Please input the integer\n");
    fscanf(fp, "%d", &val); 

    if (val == -1)
    {
        *T = NULL;
    }
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        (*T)->val = val;
        create_bitree(&((*T)->lchild), fp);
        create_bitree(&((*T)->rchild), fp);
    }

    return;
}

void init_array(int *arr, int n)
{
    int i;

    for(i=0;i<n;i++)
    {
        *(arr+i)=-1;
    }
}

#endif

3.3 测试函数

/**
 * @file max_width_main.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2023-05-19
 *
 * @copyright Copyright (c) 2023
 *
 */

#ifndef MAX_WIDTH_MAIN_C
#define MAX_WIDTH_MAIN_C
#include "max_width.c"

int main(void)
{
    BiTree root;
    int max_width;
    FILE *fp;

    fp = fopen("data.txt", "r");

    create_bitree(&root, fp);
    init_array(min_index,MAX_DEPTH);

    max_width=max_width_of_binary_tree(root,1,1);

    printf("The max_width is %d\n the action had been done\n",max_width);
    getchar();
    fclose(fp);
    return EXIT_SUCCESS;
}

#endif

测试数据data.txt

1
3
5
-1
-1
7
-1
-1
2
-1
9
-1
-1
  1. 小结

利用深度优先遍历求解二叉树的最大宽度过程是不断对当前根、左孩子及有孩子的宽度求最大值的过程,求解过程部分属于后续遍历,此时左、右两个孩子的宽度都已经被函数栈弹回值l_width和r_width两个变量中,它们需要和根节点的宽度(root_width)进行比较,然后把最大值赋给给节点。

这个过程和线段树求解最大值的过程有点类似,有时候可以形象理解为,函数栈的值在弹出过程中,根据条件进行计算,然后再把新的值传递给上一层根节点,直至求解出最终的结果。

参考资料:

662. 二叉树最大宽度 - 力扣(Leetcode)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值