Leetcode 96 Unique Binary Search Trees

来源:互联网 发布:2016淘宝详情页尺寸 编辑:程序博客网 时间:2024/06/04 18:40

原题地址:https://leetcode.com/problems/unique-binary-search-trees/

题目描述

Given n, how many structurally unique BST’s (binary search trees) that store values 1…n?
给出n,返回结构上不重复的二叉查找树的个数(每个二叉树都存储1,2,3,…,n)。

For example,
举例,

Given n = 3, there are a total of 5 unique BST’s.
给出n=3,共有5种各异的二叉查找树:

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

对于 n=3,返回 5

解题思路

这是一个动态规划问题。

由于我是先做的Leetcode 95(博客记录:Leetcode 95 Unique Binary Search Trees II),因此很自然得认为想到在Leetcode 95的基础上对程序进行修改即可轻松得到正确的代码。然而仅仅修改代码遇到了以下问题:

1. Memory Limit Exceeded 超过内存限制2. Time Limit Exceeded 超过时间限制

为了解决上述方法,我主要采取以下方法来优化代码:

1. 特别注意对于内存的管理,对于已经不再使用的内存及时进行释放。2. 有些情况下是无须重复计算的,如果重复计算的话就会有超时的可能。

对于第一点,这个没什么技巧, 对于无用的内存释放即可。

对于第二点,我们可以发现如下规律,对于两个不存在重复元素的数组arr1和arr2,当他们元素数量相同时,就算他们存储的元素不全都一样,他们可以构成的二叉查找树是一样多的。因此我们考虑使用一个数组numsTrees[n+1]来存储可以构成的异构二叉查找树的数目,这样只需要计算n+1次,其他情况下直接使用已经存储的结果即可。这是一种以空间换时间的方法。空间复杂度O(n),时间复杂度O(n)。

代码

/** * 返回由n个数字可以构成的异构二叉查找树的数目 * Input n : 最大值(即1-n共n个数) * Return : 异构的二叉查找树的数目 */int numTrees(int n) {    int numTreesFromArray(int *, int *, int);    // 如果n为0或1,直接返回1    if (n == 0 || n == 1)        return 1;    // 分配备选数组nums内存    int *nums = (int *)malloc(sizeof(int) * n);    // 分配numsTrees内存,numsTrees[i]代表用i个数可以构成的异构二叉查找树的数目    int *numsTrees = (int *)malloc(sizeof(int) * (n + 1));    // 初始化    int i;    for (i = 0; i < n; ++i) {        *(nums + i) = i + 1;  // 初始化备选数组nums,nums中存储着1-n(有序)        *(numsTrees + i) = 0; // 初始化结果集numsTrees = {0}    }    *(numsTrees + n) = 0; // 初始化numsTrees[n] = 0    *numsTrees = 1;       // 初始化numsTrees[0] = 1,空集只有一种构造方式    *(numsTrees + 1) = 1; // 初始化numsTrees[1] = 1,一个数字只有一种构造方式    // 获取使用nums可能异构的二叉查找树的数目    int count = numTreesFromArray(numsTrees, nums, n);    // 释放内存    free(nums);    free(numsTrees);    return count;}/** * 返回由nums数组中的元素可以构造出的异构的二叉查找树的数目 * Input numsTrees : 存储由i个元素可以构成的异构二叉查找树的数目 * Input nums : 存储备选元素 * Input size : 备选元素个数 * Return : 异构的二叉查找树的数目 */int numTreesFromArray(int *numsTrees, int *nums, int size) {    // 如果已经计算过,直接返回    if (*(numsTrees + size))        return *(numsTrees + size);    int *littleNums = (int *)malloc(sizeof(int) * size); // 用于存储比根节点小的数    int *bigNums = (int *)malloc(sizeof(int) * size); // 用于存储比根节点大的数    int littleNumsCount, bigNumsCount, i, j, k, count = 0;    for (i = 0; i < size; ++i) { // 遍历nums中的元素,将其作为根节点的值         littleNumsCount = 0; // 初始化littleNums的数量        bigNumsCount = 0; // 初始化bigNums的数量        // 将nums分成比根节点小的和比根节点大的两个数组        for (j = 0; j < size; ++j) {            if (*(nums + j) >= *(nums + i))                break;            *(littleNums + littleNumsCount++) = *(nums + j);        }        if (*(nums + j) == *(nums + i))            ++j;        for (; j < size; ++j)            *(bigNums + bigNumsCount++) = *(nums + j);        // 返回由littleNums可以构造出的异构的二叉查找树的数目        j = numTreesFromArray(numsTrees, littleNums, littleNumsCount);        // 返回由bigNums可以构造出的异构的二叉查找树的数目        k = numTreesFromArray(numsTrees, bigNums, bigNumsCount);        count += j * k; // 增加异构的二叉查找树的数目    }    // 释放内存    free(littleNums);    free(bigNums);    // 存储结果    *(numsTrees + size) = count;    return count;}

运行情况

Status:Accept
Time:0ms

测试数据

0   :   11   :   12   :   23   :   54   :   145   :   426   :   1327   :   4298   :   14309   :   486210  :   1679611  :   5878612  :   20801213  :   74290014  :   267444015  :   969484516  :   3535767017  :   12964479018  :   47763870019  :   1767263190

总结

运行时间竟然是0ms,之前的时候可是超时的啊,可见有些时候用空间来换时间还是非常值得的。代码优化的重要性也不言而喻。


// 个人学习记录,若有错误请指正,大神勿喷
// sfg1991@163.com
// 2015-05-26

0 0