← Home

1569. Number of Ways to Reorder Array to Get Same BST

LeetCode article · C++ solution
Website made by wuisabel-gif · Original C++ code by keineahnung2345
two pointersC++Markdown
156

The trick here is to name the state correctly, then let the implementation follow. For 1569. Number of Ways to Reorder Array to Get Same BST, the solution in this repository is mainly a two pointers solution.

Guide

What?

We want to turn the problem statement into a smaller set of decisions the computer can repeat safely. Instead of trying to be clever immediately, read the code as a sequence of questions:

  • What state are we keeping?
  • How do we move from one state to the next?
  • When do we know the answer is already determined?

For this file, the main tools are: two pointers, sliding window.

The notes already sitting in the source point us in the right direction:

  • https://stackoverflow.com/questions/17119116/how-many-ways-can-you-insert-a-series-of-values-into-a-bst-to-form-a-specific-tr

Guide

When?

This is the kind of solution you want when the problem has structure hiding inside a messy-looking input. The accepted code reduces that pressure by storing exactly the information that remains useful later.

The important function names to track are insert, dfs, fact, power, gcd.

Guide

Why?

The solution works because it narrows the problem until every update has a clear reason to exist.

  • A map keeps the lookup side cheap; the code pays a little memory to avoid repeated searching.
  • The two-dimensional vector is the memory of the solution: grid state, DP state, or adjacency shape.
  • The final return is not magic; it is the invariant after the loops or recursion have finished doing their accounting.

Guide

How?

Walk through the solution in this order:

  1. Read the setup variables first.
  2. Follow the main loop or recursive helper next.
  3. Watch where invalid states get skipped.
  4. Check which value survives to the return statement.

The most important competitive-programming habit here is to trust the invariant. Once the invariant is right, the loops become much less scary.

Guide

Complexity

  • Time: O(n) to O(n log n), depending on the dominant loop or data structure operation
  • Space: O(n) in the usual case for auxiliary containers or recursion

Guide

C++ Solution

Your submission

The accepted solution

solution.cpp
01//https://stackoverflow.com/questions/17119116/how-many-ways-can-you-insert-a-series-of-values-into-a-bst-to-form-a-specific-tr
02//Runtime: 128 ms, faster than 80.00% of C++ online submissions for Number of Ways to Reorder Array to Get Same BST.
03//Memory Usage: 28.5 MB, less than 100.00% of C++ online submissions for Number of Ways to Reorder Array to Get Same BST.
04class Node{
05public:
06    Node *left, *right;
07    int val;
08    
09    Node(int v) : val(v), left(nullptr), right(nullptr){};
10};
11
12class BST{
13public:
14    Node* root;
15    
16    void insert(int v, Node* node = nullptr){
17        if(root == nullptr){
18            root = new Node(v);
19        }else{
20            if(node == nullptr)
21                node = root;
22            if(v < node->val){
23                if(node->left == nullptr){
24                    // cout << node->val << " l-> " << v << endl;
25                    node->left = new Node(v);
26                }else{
27                    // cout << node->left->val << " l-> " << v << endl;
28                    insert(v, node->left);
29                }
30            }else{
31                if(node->right == nullptr){
32                    // cout << node->val << " r-> " << v << endl;
33                    node->right = new Node(v);
34                }else{
35                    // cout << node->right->val << " r-> " << v << endl;
36                    insert(v, node->right);
37                }
38            }
39        }
40    }
41};
42
43
44class Solution {
45public:
46    int MOD = 1e9+7;
47    unordered_map<Node*, int> counter;
48    
49    //fill "counter"
50    int dfs(Node* node){
51        if(node == nullptr) return 0;
52        counter[node] = dfs(node->left) + dfs(node->right) + 1;
53        return counter[node];
54    };
55
56    // Returns factorial of n 
57    int fact(int n) 
58    { 
59        long long res = 1; 
60        for (int i = 2; i <= n; i++) 
61            res = (res * i) % MOD;
62        return res; 
63    };
64    
65    int power(int x, unsigned int y){ 
66        if (y == 0) 
67            return 1; 
68        long long p = power(x, y/2) % MOD; 
69        p = (p * p) % MOD; 
70
71        return (y%2 == 0)? p : (x * p) % MOD; 
72    };
73
74    // Function to return gcd of a and b 
75    int gcd(int a, int b){ 
76        if (a == 0) 
77            return b; 
78        return gcd(b%a, a); 
79    };
80
81    // Function to find modular inverse of a under modulo m 
82    // Assumption: m is prime 
83    int modInverse(int a){ 
84        int g = gcd(a, MOD); 
85        if (g != 1){
86            //Fermat's little theorem only works only when m is prime!
87            return -1;
88        }else{ 
89            // If a and m are relatively prime, then modulo inverse 
90            // is a^(m-2) mod m 
91            /*
92            From Fermat's little theorem,
93            when a is not divisible by p,
94            a^(p-1) mod m = 1
95            here we choose p as m,
96            so a^(m-1) mod m = 1
97            this is equal to saying that
98            a * a^(m-2) mod m = 1
99            so we can say that a^(m-2) mod m is the inverse of a
100            */
101            return power(a, MOD-2);
102        } 
103    };
104  
105    int nCr(int n, int r) 
106    { 
107        long long fr_inverse = modInverse(fact(r));
108        long long fnr_inverse = modInverse(fact(n - r));
109        return ((fact(n) * fr_inverse) % MOD * fnr_inverse) % MOD; 
110    };
111    
112    int countInsertionOrderings(Node* node) {
113        if(!node) return 1;
114        
115       int m = counter[node->left];
116       int n = counter[node->right];
117       
118        //number of ways to permute sequence of L and sequence of R
119        long long a = nCr(m + n, n);
120        //how many possible permutations of sequence L
121        long long b = countInsertionOrderings(node->left);
122        //how many possible permutations of sequence R
123        long long c = countInsertionOrderings(node->right);
124        
125        return (((a*b) % MOD) * c) % MOD;
126    }
127    
128    int numOfWays(vector<int>& nums) {
129        int n = nums.size();
130        if(n == 0) return 0;
131        
132        BST* bst = new BST();
133        
134        for(int i = 0; i < n; ++i){
135            bst->insert(nums[i]);
136        }
137        
138        dfs(bst->root);
139        
140        //-1: deduce one, the original way to construct binary tree
141        return countInsertionOrderings(bst->root) - 1;
142    }
143};
144
145//use pascal triangle to calculate combination count
146//https://leetcode.com/problems/number-of-ways-to-reorder-array-to-get-same-bst/discuss/819369/C%2B%2B-Just-using-recursion-very-Clean-and-Easy-to-understand-O(n2)
147//https://www.geeksforgeeks.org/calculate-ncr-using-pascals-triangle/
148//Runtime: 376 ms, faster than 37.46% of C++ online submissions for Number of Ways to Reorder Array to Get Same BST.
149//Memory Usage: 105.7 MB, less than 38.77% of C++ online submissions for Number of Ways to Reorder Array to Get Same BST.
150class Solution {
151public:
152    long long MOD = 1e9+7;
153    vector<vector<int>> pascal;
154    
155    void build_pascal(int max_num){
156        pascal = vector<vector<int>>(max_num+1);
157        
158        for(int n = 0; n <= max_num; ++n){
159            //we need nC0 to nCn
160            pascal[n] = vector<int>(n+1);
161        	//cout << "pascal[" << n << "]" << endl;
162            
163            /*
164            to calculate pascal[n][n], 
165            it will use pascal[n-1][n], which doesn't exist,
166            so here we treat pascal[n][n] as a special case
167            and do not calculate it in the loop
168            */
169            pascal[n][0] = pascal[n][n] = 1;
170            for(int r = 1; r < n; ++r){
171                pascal[n][r] = (pascal[n-1][r-1] + pascal[n-1][r]) % MOD;
172            }
173        }
174    }
175    
176    int dfs(vector<int>& nums){
177        int n = nums.size();
178        if(n == 0) return 1;
179        
180        vector<int> left, right;
181        
182        //i starts from 1: exclude nums[0], which is root
183        for(int i = 1; i < n; ++i){
184            if(nums[i] < nums[0]) left.push_back(nums[i]);
185            else right.push_back(nums[i]);
186        }
187        
188        long long left_res = dfs(left);
189        long long right_res = dfs(right);
190        
191        //(n-1)C(left.size()): choose from n-1 elements, excluding the root
192        return ((pascal[n-1][left.size()] * left_res) % MOD * right_res) % MOD;
193    }
194    
195    int numOfWays(vector<int>& nums) {
196        int n = nums.size();
197        build_pascal(n);
198        //cout << "table built" << endl;
199        
200        return dfs(nums)-1;
201    }
202};

Cost

Complexity

Time
O(n) to O(n log n), depending on the dominant loop or data structure operation
Dominated by the main traversal, recursion, or data-structure operations in the code.
Space
O(n) in the usual case for auxiliary containers or recursion
Auxiliary state plus the answer structure where the problem requires one.