2 minute read

96. Unique Binary Search Trees

leetcode 96번 문제를 리뷰 해보도록 하겠습니다. 중요한 키포인트만 잡고 세세한 부분은 넘어가도록 하겠습니다.

Description

Given an integer n, return the number of structurally unique BST’s (binary search trees) which has exactly n nodes of unique values from 1 to n.

key point

이 문제는 DP로 풀 수 있습니다. 임의의 데이터 집합에 대한 모든 가능한 Datastructure를 구하는 문제이기 때문에 , DataStructure의 operation이나 정의를 적용하여 풀 수 없습니다. 하지만, DP를 적용할 때 symmetric인지 아닌지를 구별 안하면 좀 더 간결한 풀이가 그낭합니다. 여기서 , 수학적 지식이 더해진다면 100명중 1명의 개발자가 될 수 있습니다. (leetcode 속도 시스템은 잘 모르겠습니다.어제는 하위 10퍼엿는데 오늘 다시 제출해보니까 상위 1퍼네요..머죠?)

Solution 1

class Solution:
    dp = []
    def cal_dp(start , end )  :
        sum = 0
        if start >= len(Solution.dp) or end <0 :
            return 1
        if Solution.dp[start][end] != -1:
            return Solution.dp[start][end]
        if start > end :
            return 1
        for i in range(start,end+1):
            sum += Solution.cal_dp(start,i-1) * Solution.cal_dp(i+1,end)
        Solution.dp[start][end]=sum       
        return sum
    def numTrees(self, n: int) -> int:
        Solution.dp  = [  [-1 for _ in range(n)] for _ in range(n)]
        for i in range(n) :
            Solution.dp[i][i] = 1
        Solution.cal_dp(0,n-1)
        return Solution.dp[0][n-1]

다. DP하면 f(n) = f(n-1) + f(n-2) 같은 유형을 단순히 외우고만 있엇기에 아무 고민 없이 2차원 배열을 정의 했습니다. Dp 풀이의 정석처럼 풀었습니다.

Solution 2

class Solution:
    def numTrees(self, n):
        """
        :type n: int
        :rtype: int
        """
        G = [0]*(n+1)
        G[0], G[1] = 1, 1

        for i in range(2, n+1):
            for j in range(1, i+1):
                G[i] += G[j-1] * G[i-j]

        return G[n]

image

G(n) 을 길이 n인 sequence로 만들 수 있는 BST의 개수라 정의하겠습니다. 저희가 풀려는 문제의 답입니다.

F(i,n) 을 i가 root 인 길이 n인 sequence로 만들 수 있는 BST의 개수라 정의하겠습니다.

이는 \(G(n) = \sum_{k=1}^n F(i,n)\) 로 정의된다고 볼 수 있습니다. \(F(i,n) = G(i-1) * G(n-i )\)가 됩니다.

\(G(n) = \sum_{k=1}^n G(i-1) G(n-i )\) 가 됩니다. 즉 , 순서대로 G(1) ,G(2) ,…G(n) 을 구하면 해를 구할수 있게 됩니다.

Solution 1 , Solution2 의 차이

저는 F(0, n-1) 이라는 문제를 정의해서 풀었습니다. F(0, n-1) 을 1…. n 사이의 숫자로 만든 모든 ? 개수라고 정의했습니다. 하지만, 숫자의 범위가 어디든 간에 n개의 연속된 숫자에 대한 BST의 개수는 항상 같습니다. 즉, 대칭성(symmetric)을 알아내지 못했기에 비효율적인 풀이를 작성하게 된 것입니다.

Solution 3

이 G(n)이라는 것은 Catalan number 라는 것으로 정의되어 있습니다.

\(C_(n+1) = \frac{2(2n+1)}{n+2} C_n\) 으로 정의가 됩니다.

class Solution(object):
    def numTrees(self, n):
        """
        :type n: int
        :rtype: int
        """
        C = 1
        for i in range(0, n):
            C = C * 2*(2*i+1)/(i+2)
        return int(C)

이렇게 풀 수 있습니다.

Leave a comment