【二叉树】OJ-顺序建立完全二叉树 ❀ 努力更新DAY1

数据结构对我来说真的好难>< 可能是因为我c语言基础打得不够扎实吧!ε=(´ο`*)))唉,每天告诫自己一定要自己动手写少看(chao)答案、增加思维量。
【明明应该昨天就写完的,但是被拉去入FF14坑了呜呜呜,我就是个废物】

1.题目

假设用顺序表来表示一棵完全二叉树。从根到叶结点按层次从 开始编号,同一层次从左到右编号,数据存储在对应的数组元素中。试编写算法由此顺序存储结构建立该二叉树的二叉链表。

【输入格式】
输入只有一行,为顺序输入的从根节点开始的各个节点元素,它们之间用空格隔开,整个输入以 $ 结束。

【输出格式】
输出有一行,为二叉树结构的广义表达式。
在这里插入图片描述

2.完整代码

避免查错的朋友要翻到最后才看到完整代码,先附上完整代码~

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
 
typedef struct BT {
    char data;
    struct BT *lc, *rc;
} BT;
 
BT* init(char data) {
    BT *node = (BT *)malloc(sizeof(BT));
    node->data = data;
    node->lc = NULL;
    node->rc = NULL;
    return node;
}
 
BT* build(char list[], int len, int id) {
    BT *root = init(list[id - 1]);
    if (id * 2 <= len) {
        root->lc = build(list, len, id * 2);
    }
    if (id * 2 + 1 <= len) {
        root->rc = build(list, len, id * 2 + 1);
    }
 
    return root;
}
 

 
void clear(BT* bt) {
    if (bt->lc != NULL) {
        clear(bt->lc);
    }
    if (bt->rc != NULL) {
        clear(bt->rc);
    }
    free(bt);
}
 
void gs_print(BT* bt) {
    printf("%c", bt->data);
    if (bt->lc != NULL) {
        printf("(");
        gs_print(bt->lc);
        if (bt->rc == NULL) {
            printf(")");
        }
    }
    if (bt->rc != NULL) {
        if (bt->lc == NULL) {
            printf("(");
        }
        printf(",");
        gs_print(bt->rc);
        printf(")");
        }
}
 
void get_list(char list[]) {
    char buf[46] = "";
    scanf("%[^$\n]s", buf);
    if (strlen(buf) == 0) {
        printf("\n");
        return ;
    }/* else {
        printf("Got input:%s", buf);
    }*/
    char *alphabet;
    alphabet = strtok(buf, " ");
    char *p = &list[0];
    while (alphabet != NULL) {
        *p++ = alphabet[0];
        alphabet = strtok(NULL, " ");
    }
}
 
 
int main() {
    char s_list[26] = "";
    get_list(s_list);
    BT *root = build(s_list, (int)strlen(s_list), 1);
    gs_print(root);
    clear(root);
    return 0;
}

3.思路

1-虾米是完全二叉树?

类似的图都来自@数据与算法之美by王争
除了0这个编号点完全没有浪费的树(。
非常直观,我们的目的就是让萝卜进坑。

2- 确立要构造的部分

  • 初始化的结点
  • 提取键入值的部分入list[ ]
  • 构造完全二叉树の函数
  • 输出伪二叉树表达式の函数

3-Tree根结点初始化及clearの基操

typedef struct BT {
    char data;
    struct BT *lc, *rc;
} BT;
 
BT* init(char data) {
    BT *node = (BT *)malloc(sizeof(BT));
    node->data = data;
    node->lc = NULL;
    node->rc = NULL;
    return node;
}
void clear(BT* bt) {
    if (bt->lc != NULL) {
        clear(bt->lc);
    }
    if (bt->rc != NULL) {
        clear(bt->rc);
    }
    free(bt);
}

介个属于常规操作~必须烂熟于心的说( ̄▽ ̄)/ 再错就是大笨蛋!

4-来建立完全二叉树吧!

BT* build(char list[], int len, int id) {
    BT *root = init(list[id - 1]); // 数组都是从0开始的,所以要-1!
    if (id * 2 <= len) {
        root->lc = build(list, len, id * 2);
    }
    if (id * 2 + 1 <= len) {
        root->rc = build(list, len, id * 2 + 1);
    }
 
    return root;
}

可以将root也就是我们建立的【二叉树】理解为一个有树形的【数组】!
❀【数组】型的二叉树查找检索起来非常方便和快捷!

5-how get键入的内容呢?

基于数组的顺序存储法:
我们把根节点存储在下标 i = 1 的位置,那左子节点存储在下标 2 * i = 2 的位置,右子节点存储在 2 * i + 1 = 3 的位置。以此类推,B 节点的左子节点存储在 2 * i = 2 * 2 = 4 的位置,右子节点存储在 2 * i + 1 = 2 * 2 + 1 = 5 的位置。

// 此处的list就是存键入鬼东西的数组
void get_list(char list[]) {
    char buf[46] = "";
    scanf("%[^$\n]s", buf);
    if (strlen(buf) == 0) {
        printf("\n");
        return ;
    }/* else {
        printf("Got input:%s", buf);
    }*/

/**
strtok函数的固定用法!
**/
    char *alphabet;
    alphabet = strtok(buf, " ");
    char *p = &list[0];       //指向list首地址
        while (alphabet != NULL) {
        *p++ = alphabet[0];
        alphabet = strtok(NULL, " ");
    }
}

一些语句的注释

① scanf中的[^&xx]
scanf("%[^$\n]s", buf);
/**
scanf本身是不读空格的,加了[^$\n]就表示读入除了换行符以外的内容,
也就是说前面的空字符也会被读取!!
**/

https://blog.csdn.net/hyb612/article/details/111717216
更多用法可以看看这篇博文进行复习~

② strtok干嘛用的捏?
char *strtok(char *str, const char *delim)
  • 参数
    str – 要被分解成一组小字符串的字符串。
    delim – 包含分隔符的 C 字符串 。(人话:爸爸不读这个分隔符!)
  • 返回值
    该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。

在本文里的用法结构:

/** 获取第一个字符串 **/
alphabet = strtok(buf, " ");
    char *p = &list[0];       //指向list首地址
    /** 获取其余字符串 **/
        while (alphabet != NULL) {
        *p++ = alphabet[0]; //表示p下一个地址的值 = a的首元素
        alphabet = strtok(NULL, " ");
    }
  • 很难理解这个NULL的用法是什么,
  • 看了(https://blog.csdn.net/weibo1230123/article/details/80177898?spm=1001.2014.3001.5506)有一丶悟了!
  • 当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。
    (ps:‘\0’ 是转义字符 ,为了告诉编译器’\0’是空字符)

6- 打印伪二叉树表达式

主要还是要掌握递归思想,一些技巧:
不要去想接下来的东西是什么样的!
只要想每个未来的对象都=当前对象就可以了!

【结构】
printf最前- 递归的时候要先打印下一个递归项
算法在后- 依据左先右后的先序遍历顺序!
一些简单递归思想

void gs_print(BT* bt) {
    printf("%c", bt->data);
    if (bt->lc != NULL) {
        printf("(");
        gs_print(bt->lc);
        if (bt->rc == NULL) {
            printf(")");
        }
    }
    if (bt->rc != NULL) {
        if (bt->lc == NULL) {
            printf("(");
        }
        printf(",");
        gs_print(bt->rc);
        printf(")");
        }
}

推荐一个数据结构可视化网站:
https://visualgo.net/zh

7-main函数没什么好说的

int main() {
    char s_list[26] = "";
    get_list(s_list);
    BT *root = build(s_list, (int)strlen(s_list), 1);
    gs_print(root);
    clear(root);
    return 0;
}

完成でした!!❀❀❀

4.总结

最重要的还是掌握递归调用的思想和字符串分段提取的套路~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值