95.不同的二叉搜索树 || -----力扣每日打卡Day2

1.题目

给定一个整数 n,生成所有由 1 … n 为节点所组成的 二叉搜索树

示例

输入:3
输出:

[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]

解释
以上的输出对应以下 5 种不同结构的二叉搜索树:

   1         3     3      2      1
    \       /     /      / \      \
     3     2    1      1   3      2
    /     /       \                 \
   2     1         2                 3

提示:0 <= n <= 8

c语言函数头

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


/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
 struct TreeNode** generateTrees(int n, int* returnSize){
 }

来源:力扣(LeetCode)戳我前往题目

2.题目分析

二叉搜索树又是:二叉排序树,二叉查找树。

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树。

这里难就难在,返回的是所有排序二叉树组成的的数组,而不是单纯的创建一个二叉排序树。但是创建里面的二叉排序树,一般情况下我们都会用递归来创建二叉排序树。所以这里仍然首先考虑递归。

  • 所以不妨再创建一个 buildTree 的函数来创建排序二叉树,因为是从1到n,所以我们创建的函数可以是 struct TreeNode** buildTree(int start,int end, int *returnSize)start为1,end为n。
  • 因为二叉排序树左右子树的特殊,我们可以通过i遍历1-n为根结点(既然是所有排序二叉树,所以根结点都会把1~n遍历一遍),然后通过这个函数的递归生成左右子树的集合。左子树小于根结点,所以左子树集合就应该是starti-1,右子树比根结点,所以右子树应该是 i+1end
  • 找到i根结点的左右子树结合,就可以直接通过遍历左右子树集合,和当时i的根结点结合。就生成一个新的二叉排序树。再将这个二叉排序树加在将要返回的二叉树数组中。
  • 记得每次遍历完第i个根结点中,生成的左右子树集合要free()。不同的i的根结点生成的左右子树都是不同的,新建的。

3.代码实现

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


/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
 struct TreeNode** buildTree(int start,int end, int *returnSize)
 {
     //整数小于0,返回[[]]
    if(start > end){
        *returnSize = 1;
        struct TreeNode** ret = malloc(sizeof(struct TreeNode*));
        ret[0] = NULL;
        return ret;
    }

    //只有一个数1,只能生成一个树
    if(start == end){
        *returnSize = 1;
        struct TreeNode** ret = malloc(sizeof(struct TreeNode*));
        ret[0] = malloc(sizeof(struct TreeNode));
        ret[0][0].val = start;
        ret[0][0].left = NULL;
        ret[0][0].right = NULL;
        return ret;

    }

    *returnSize = 0;//定义一个生成树的初始值
    struct TreeNode** allTree = malloc(0);//初始化总的要返回树的初始值

    //遍历根结点
    for(int i=start;i<=end;i++){

        //获得所有可行的左子树集合,应该比根结点小
        int leftSize;
        struct TreeNode** leftTree = buildTree(start,i-1,&leftSize);

        //获得所有可行的左子树集合,应该比根结点大
        int rightSize;
        struct TreeNode** rightTree = buildTree(i+1,end,&rightSize);

        //开始创建二叉排序树,右子树集合和左子树集合中遍历,拼接在根结点上
        for(int right = 0; right < rightSize; right++){
            for(int left = 0; left < leftSize; left++){
                //创建一个二叉排序树
                struct TreeNode* ret = malloc(sizeof(struct TreeNode));
                ret->val = i;
                ret->left = leftTree[left];
                ret->right = rightTree[right];

                //创建完了一个二叉排序树后,扩展返回二叉树数组空间,把这个二叉树存放在返回树数组中
                (*returnSize)++;
                allTree = realloc(allTree, sizeof(struct TreeNode*) * (*returnSize));
                allTree[(*returnSize)-1] = ret;
            }
        }
        //遍历一遍后,清空左右子树集合
        free(leftTree);
        free(rightTree);
    }
    return allTree;
 }
struct TreeNode** generateTrees(int n, int* returnSize){
    //在n为0时,输出为[]
    if(!n){
        (*returnSize) = 0;
        return NULL;
    }
    return buildTree(1,n,returnSize);
}

4.题后总结

  1. n的值为0的时候是返回[],一个空数组。而n的值为负,返回的是[[]],一个数组中的空数组。刚开始还没注意到,所以在generateTrees函数里没有加n = 0时的返回值。小小的注意一下qwq。
    在这里插入图片描述在这里插入图片描述
  2. 里面有个函数 realloc()。是为了给返回的二叉树数组扩展空间,如果空间不够,就拓展。我还去百度了一下。realloc原型是extern void *realloc(void *mem_address, unsigned int newsize); 功能:先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
  3. 总的来说,如果的二叉树首先考虑用另一个函数用递归法。

猜你喜欢

转载自blog.csdn.net/qq_46293423/article/details/107485316