算法导论的C实现——画出二叉树

算法导论的C实现——画出二叉树

最近在复现算法导论第二部分的内容,一上来就是和二叉树相关的堆排序(heapsort)算法。我的导师和我说过二叉树是人类想象出来的东西,只是心中有树。在做这些算法的时候不能直接观察到二叉树是一件很郁闷的事。。。实际上网上已有很多类似的画法,如使用linux工具,或者使用其他比较复杂的。但是算法导论书中往往用一个数组作为二叉树的容器,上述工具就会显得有点繁琐。所以我自己写了一个函数,基本的思想就是根据一个数组,然后用空格和数字来构成树。比如我有一个数组testArray[14] = {27, 17, 3, 16, 13, 10, 1, 5, 7, 12, 4, 8, 9, 0};,最后函数的输出是这样:

The height of this tree is: 4
                                   27
               17                                      13
     16                  12                  10                  1
5         7         3         4         8         9         0

输入其他长度的数组也可以画出:

                                             27
                     17                                              13
         16                      12                      10                      1
   5           7           3           4           8           9           0            56
5

树的高度

首先需要计算一下树的高度,为 c e i l ( log ⁡ 2 A . l e n g t h ) ceil(\log_2 A.length) ceil(log2A.length)。其中ceil()为上取整函数。C语言中没有相应的函数,但是根据数学变化 log ⁡ a b = log ⁡ n b / log ⁡ n a \log_a b = \log_n b/ \log_n a logab=lognb/logna,我们可以用C语言自带的 log ⁡ e x \log_e x logex函数来进行计算:

//myUtil.h
#define lg2(x) static_cast<int>(ceil(log(x+1)/log(2)))

这里我直接把它写成头文件的定义,并且加上static_cast<int>()函数把计算结果转化为int类型。

元素间隔

我采用自上而下的方法进行计数,就是树的根行树为n=0,子目录向下发展时n递增。在头文件中定义了两个宏定义:

//myUtil.h
#define baseInterval 7
#define elementInterval 1

其中,baseInterval指的是最底层行元素间距。elementInterval指的是数据长度,我推荐设为1。对于因为数据长度造成的树的偏移,后面还会有处理。baseInterval最好设为奇数,这样上层的元素可以坐在底层两元素的正上方。

interval

假设此时树有 h e i g h t height height层,选取第 h h h层,也就是 n = h e i g h t − h − 1 n=height - h -1 n=heighth1。这时我们可以从图中看出元素间隔和左侧间隔的数目,找不出规律可以先列举一下。当树的高度 h e i g h t = 4 height=4 height=4时:
n = 1 , i n t e r v a l = 7 ∗ e l e + 4 ∗ ( b a s e − 1 ) n=1,interval=7*ele+4*(base-1) n=1,interval=7ele+4(base1)
n = 2 , i n t e r v a l = 3 ∗ e l e + 1 ∗ ( b a s e − 1 ) n=2,interval=3*ele+1*(base-1) n=2,interval=3ele+1(base1)
… … ……
其实已经可以很直接的看出每一行的元素间的间隔了,公式表示如下:
i n t e r v a l = ( 2 h e i g h t − n − 1 ) ∗ e l e + b a s e ∗ 2 h e i g h t − n − 1 interval = (2^{height-n}-1)*ele+base*2^{height-n-1} interval=(2heightn1)ele+base2heightn1

startblank

对于第一行前的空格元素数目,也可以通过观察统计出来:
n = 1 , i n t e r v a l = 3 ∗ e l e + 3 / 2 ∗ ( b a s e − 1 ) n=1,interval=3*ele+3/2*(base-1) n=1,interval=3ele+3/2(base1)
n = 2 , i n t e r v a l = 1 ∗ e l e + 1 / 2 ∗ ( b a s e − 1 ) n=2,interval=1*ele+1/2*(base-1) n=2,interval=1ele+1/2(base1)
… … ……
规律也是很明显的,为了计算简便,实际上可以用已经算出的元素间间隔来表示:
s t a r t B l a n k = ( i n t e r v a l B l a n k − e l e m e n t I n t e r v a l − b a s e I n t e r v a l + 1 ) / 2 ; startBlank = (intervalBlank-elementInterval-baseInterval+1)/2; startBlank=(intervalBlankelementIntervalbaseInterval+1)/2;

计算的代码如下:

//myUtil.cpp
	k_ele = static_cast<int>(pow(2, height-iDraw))-1;
	k_base = static_cast<int>(pow(2, height-iDraw-1));
	//元素间间隔
	intervalBlank = k_ele*elementInterval+k_base*(baseInterval-1);
	//左侧间隔
	startBlank = (intervalBlank-elementInterval-baseInterval+1)/2;

空格数目微调

按照上面的计算画出来的图有点小瑕疵,就是当元素的位数超过1的时候,相应的行会向后移动:

The height of this tree is: 4
                     27
         17                        13
   16            12            10            1
5      7      3      4      8      9      0      56

可以看到后面的数字向前攒动了,原因就是我设置的元素单元只能容纳一个长度的整形。所以我这里加了点微调,就是当数字位数超过1的时候,将空格相应的增加:

//judge digit of an INT
int get_digit(int x)
{
    int length=0;
    while(x)
    {
        x/=10;
        length++;
    }
    return length;
}

源码

最后整体的代码如下:

//
// Created by king98 on 19-4-29.
// myUtil.c

#include <math.h>
#include <cstdio>
#include "myUtil.h"

//画二叉树的测试函数
void test_drawBINTREE()
{
    uint32_t heap_size = 15;
    int testArray[15] = {27, 17, 3, 16, 13, 10, 1, 5, 7, 12, 4, 8, 9, 0, 5};
    drawBINTREE(testArray, heap_size);
}
void drawBINTREE(int A[], int size)
{
    int height = lg2(size);
    printf("The height of this tree is: %d\n", height);
    int numRow;
    int startBlank = 0;
    int intervalBlank = 0;
    int subscript = 0;
    int tempLength;
    int k_ele;
    int k_base;
    //自上而下
    for(int iDraw=0; iDraw<height; iDraw++)
    {
        numRow = static_cast<int>(pow(2, iDraw));
        k_ele = static_cast<int>(pow(2, height-iDraw))-1;
        k_base = static_cast<int>(pow(2, height-iDraw-1));
        if(height-iDraw>1)
        {
            intervalBlank = k_ele*elementInterval+k_base*(baseInterval-1);
            startBlank = (intervalBlank-elementInterval-baseInterval+1)/2;
        }
        else
        {
            startBlank = 0;
            intervalBlank = baseInterval;
        }
        //画此行第一个元素前面的空格
        for(int iBlank=0; iBlank<startBlank; iBlank++){printf(" ");}
        //输出此行第一个元素
        printf("%d", A[subscript]);
        subscript++;
        //画接下来的空格组+元素
        for(int iCouple=0; iCouple<numRow-1;iCouple++)
        {
            tempLength = get_digit(A[subscript-1]);
            if(subscript<size)
            {
                for (int iBlank=0; iBlank<intervalBlank-tempLength+1; iBlank++){printf(" ");}
                printf("%d", A[subscript]);
                subscript++;
            }
        }
        //换行
        printf("\n");

    }
}
//judge digit of an INT
int get_digit(int x)
{
    int length=0;
    while(x)
    {
        x/=10;
        length++;
    }
    return length;
}
//
// Created by king98 on 19-4-29.
// myUtil.h

#ifndef TEST_MYUTIL_H
#define TEST_MYUTIL_H

#define lg2(x) static_cast<int>(ceil(log(x+1)/log(2)))
#define baseInterval 5
#define elementInterval 1

void test_drawBINTREE();
void drawBINTREE(int A[], int size);
int get_digit(int x);

#endif //TEST_MYUTIL_H

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值