---
# System prepended metadata

title: 08. Recursion I
tags: [LeetCode筆記]

---

# Recursion I

###### tags: `LeetCode筆記`


## :memo: 一、特性

### 1. recurrence relation
一系列規則使遞迴發生到終止狀況

### 2. base case
終止狀況使遞迴不再繼續產生

### Pascal's Triangle:
* recurrence relation:

![](https://i.imgur.com/TByU3FS.png)
* base case:

![](https://i.imgur.com/wbUxnlo.png)

### 寫成code又是另一件難度的事了

## :memo: 二、基本知識
**fmax(a,b)**  // C語言函式回傳a,b中最大值
**long long** // 8bytes 值:-9223372036854775808 至 9223372036854775807


## :memo: Tips
- **When in doubt, write down the recurrence relationship.**

- **Whenever possible, apply memoization.**

- **When stack overflows, tail recursion might come to help.**

## :memo: 三、Memoization

### 利用此技術去減少重複計算(空間換時間)
重複計算會長這樣:
![](https://i.imgur.com/nFr6vBH.png)

### Top-Down Memoization
```
if (cache[n] != 0)
{
    return cache[n];
}

cache[n] = fib(n-1) + fib(n-2);

return cache[n];
```
### Bottom-Up Tabulation
```
if (n <= 1)
{
    return n;
}

for (int i = 0; i < n; i++)
{
    cache[n] = cache[n - 1] + cache[n - 2];
}

return cache[n];
```
### 更快要用數學方法(log(N))
```
class Solution {
    public int fib(int N) {
        double goldenRatio = (1 + Math.sqrt(5)) / 2;
        return (int) Math.round(Math.pow(goldenRatio, N) / Math.sqrt(5));
    }
}
```

## :memo: *Search in a Binary Search Tree (Easy)
![](https://i.imgur.com/06E68pa.png)

![](https://i.imgur.com/QDM7WsB.png)

### 作法
在function一開始初始化一個指標指向NULL，先判斷是否該node的val為要找的val，是就回傳root，不是就往左子樹遞迴(記得先檢查左子樹不等於NULL)，p = searchBST(root->left,val)，回傳p後，檢查p不等於NULL就再回傳p。左子樹是NULL，就往右子樹做同樣操作。

```
struct TreeNode* searchBST(struct TreeNode* root, int val) {
    struct TreeNode* p = NULL;
    
    if (root->val == val)
    {
        return root;
    }
    if (root->left != NULL)
    {
        p = searchBST(root->left, val);
        if (p != NULL)
        {
            return p;    
        }        
    }
    if (root->right != NULL)
    {
        p = searchBST(root->right, val);
        if (p != NULL)
        {
            return p;    
        }        
    }
    
    return p;
}
```

### 作法 三元運算子
```
class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {        
        if (root == nullptr || root->val == val) {
            return root;
        }
        
        return val < root->val ? searchBST(root->left, val) : searchBST(root->right, val);
    }
};
```

## :memo: *Maximum Depth of Binary Tree (Easy)
找樹的高度
```
int maxDepth(struct TreeNode* root){
    if (root == NULL)
    {
        return NULL;
    }
    
    return fmax(maxDepth(root->left) + 1, maxDepth(root->right) + 1);
}
```

## :memo: *Reverse String (Easy)
![](https://i.imgur.com/yckb9uZ.png)

### 作法 recursion
```
class Solution {
public:
    void reverseString(vector<char>& s) {
        helper(s, s.size() - 1, 0);
    }
    void helper(vector<char>& s, int end, int start) {
        if (start >= end) {
            return;
        }
        
        char tmp = s[start];
        s[start] = s[end];
        s[end] = tmp;
        
        helper(s, end - 1, start + 1);
    }
};
```

### 作法 iteration
**時間: O(N)**
**空間: O(1)**
```
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        
        ListNode* prev = dummy;
        
        while (head != nullptr && head->next != nullptr) {
            ListNode* firstNode = head;
            ListNode* secondNode = head->next;
            
            prev->next = secondNode;
            firstNode->next = secondNode->next;
            secondNode->next = firstNode;
            
            prev = firstNode;
            head = firstNode->next;
        }
        
        return dummy->next;
    }
};
```

## :memo: *Reverse Linked List (Easy)
![](https://i.imgur.com/xBLoPL5.png)

![](https://i.imgur.com/cBgmDVA.png)

### 作法 recursion
```
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return head;
        }
        ListNode* p = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        
        return p;
    }
};
```

### 作法 iteration
```
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr;
        ListNode* current = head;
        ListNode* next = head;

        while (current != nullptr) {
            next = current->next;
            current->next = prev;
            prev = current;
            current = next;
        }
        
        return prev;
};
```

## :memo: *Pascal's Triangle II (Easy)
![](https://i.imgur.com/pPwSQIX.png)

![](https://i.imgur.com/X0BlzS5.png)

### 作法 recursion
**時間: O(2^k)**
**空間: O(k)**
```
class Solution {
 public:
    int getNum(int row, int col) {
        if (row == 0 || col == 0 || row == col) {
            return 1;
        }
        
        return getNum(row - 1, col - 1) + getNum(row - 1, col);
    }

    vector<int> getRow(int rowIndex) {
        vector<int> ans;

        for (int i = 0; i <= rowIndex; i++) {
            ans.push_back(getNum(rowIndex, i));
        }      

        return ans;
    }
};
```

### 作法 Memory-efficient Dynamic Programming
**時間: O(k^2)**
**空間: O(k)**
```
class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<int> ans = vector<int>(rowIndex + 1, 1);

        for (int i = 1; i < rowIndex; i++) {
            for (int j = i; j > 0; j--) {
                ans[j] += ans[j - 1];  // ans[j] = ans[j - 1] + ans[j]
            }                
        }            

        return ans;
    }
};
```

### 作法 Math! (specifically, Combinatorics)
**時間: O(k)**
**空間: O(k)**
```
class Solution {
public:
    vector<int> getRow(int n) {
        vector<int> ans = {1};

        for (int k = 1; k <= n; k++) {
            ans.push_back((int)((ans.back() * (long long)(n - k + 1)) / k));      
        }     

        return ans;
    }
};
```

## :memo: *Climbing Stairs (Easy)
![](https://i.imgur.com/21f8Gdl.png)

### 作法 Dynamic Programming
**時間: O(N)**
**空間: O(N)**
```
class Solution {
public:
    int climbStairs(int n) {
        int cache[46];
        
        cache[1] = 1;
        cache[2] = 2;
        
        for (int i = 3; i <= n; i++) {
            cache[i] = cache[i - 1] + cache[i - 2];
        }
    
        return cache[n];
    }
};
```

### 作法 Fibonacci Number
**時間: O(N)**
**空間: O(1)**
```
class Solution {
public:
    int climbStairs(int n) {
        if (n == 1) {
            return 1;
        }
        
        int first = 1;
        int second = 2;
        for (int i = 3; i <= n; i++) {
            int third = first + second;
            first = second;
            second = third;
        }
        
        return second;
    }
};
```

## :memo: *Merge Two Sorted Lists (Easy)
![](https://i.imgur.com/JQ2XiHh.png)

![](https://i.imgur.com/7xpalGp.png)

### 作法 recursion 
```
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {        
        if (list1 == nullptr) {
            return list2;
        }
        else if (list2 == nullptr) {
            return list1;
        }
        else if (list1->val < list2->val) {
            list1->next = mergeTwoLists(list1->next, list2);
            return list1;
        }
        else {
            list2->next = mergeTwoLists(list1, list2->next);
            return list2;
        }
    }
};
```

## :memo: Swap Nodes in Pairs (Medium)
![](https://i.imgur.com/8Dgrh8F.png)

![](https://i.imgur.com/JNJB7SQ.png)

### 作法 recursion
**時間: O(N)**
**空間: O(N)**
```
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if (head == nullptr) {
            return nullptr;
        }
        
        if (head->next == nullptr) {
            return head;
        }
        
        ListNode* p = head;
        if (head != nullptr && head->next != nullptr) {
            int tmp = head->val;
            head->val = head->next->val;
            head->next->val = tmp;
        }
        if (head->next->next != nullptr) {
            head = head->next->next;
            swapPairs(head);
        }
        
        head = p;
        
        return p;
    }
};
```

## :memo: Pow(x, n) (Medium)
![](https://i.imgur.com/XnAXUhD.png)

### 作法 fastPow
```
double fastPow(double x, long long n) {
    if (n == 0) 
    {
        return 1.0;
    }
    double half = fastPow(x, n / 2);
    if (n % 2 == 0) 
    {
        return half * half;
    } 
    else 
    {
        return half * half * x;
    }
}

double myPow(double x, long long n){
    if (n < 0)
    {
        n = -n;
        x = 1.0 / x;
    }
    
    return fastPow(x, n);
}
```

## :memo: K-th Symbol in Grammar (Medium)
這題腦袋要靈活才可能想出來，用到XOR去表示遞迴，參考Dicuss才會寫，把圖形畫出來可以幫助理解(printf)
### 作法 範例
```
int kthGrammar(int n, int k){
    if (k <= 2)
    {
        return k - 1;
    }
    if (k % 2)
    {
        return kthGrammar(n, k / 2+1);
    }    
    else
    {
        return 1 ^ kthGrammar(n, k / 2);
    }
    
    return;
}
```

## :memo: *Unique Binary Search Trees II (Medium)
![](https://i.imgur.com/OY2qMh8.png)

![](https://i.imgur.com/QWMbUEJ.png)

這一題找出所有大小等於n的樹的形狀，完全不知道怎麼寫，就算看到範例code也要花很長時間才能去消化吸收，範例code的架構很整齊但是要弄懂一定要去trace才行。

### 作法 範例code C
```
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

struct TreeNode* push(struct TreeNode*** arr, int* returnSize, int a) {
    struct TreeNode* t = NULL;
    if (a > -1) //only valid value can be allocated
    {
        t = (struct TreeNode*) malloc(sizeof(struct TreeNode));
        t->left = t->right = NULL;
        t->val = a;
    }
    *returnSize += 1;
    *arr = (struct TreeNode**)realloc(*arr, sizeof(struct TreeNode*) * (*returnSize));
    (*arr)[*returnSize-1] = t;
    return t; //return this node for -> root
}

struct TreeNode* generate(int begin, int end, int* returnSize){
    struct TreeNode** arr = (struct TreeNode**)malloc(sizeof(struct TreeNode*));
    if (begin>=end)
    {
        if (begin > end)
        {
            push(&arr, returnSize, -1);
        }
        if (begin == end)
        {
            push(&arr, returnSize, begin);
        }
        return arr;
    }
    for (int i = begin; i <= end; i++)
    {
        int count0 = 0;
        int count1 = 0;
        struct TreeNode** arr0 = generate(begin, i-1, &count0);
        struct TreeNode** arr1 = generate(i+1, end, &count1);
        for(int j = 0; j < count0; j++)
        {
            for(int k = 0; k < count1; k++)
            {
                struct TreeNode* t = push(&arr, returnSize, i);
                t->left = arr0[j];
                t->right = arr1[k];
            }
        }
    }
    return arr;
}

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
struct TreeNode** generateTrees(int n, int* returnSize){
    *returnSize = 0;
    if (!n)
    {
        return NULL;
    }
    
    return generate(1, n, returnSize);
}
```

### 作法 C++
```
class Solution {
public:
    vector<TreeNode *> generateTrees(int n) {
        return helper(1,n);
    }
    
    vector<TreeNode*> helper(int s, int e) {
        if (s > e) {
             return vector<TreeNode*>(1, NULL);
        }
        
        vector<TreeNode*> result;
        for (int i=s; i <= e; ++i) {
            vector<TreeNode*> left, right;
                left = helper(s, i - 1);
                right = helper(i + 1, e);
                for (int j = 0; j < left.size(); ++j) {
                    for (int k = 0; k < right.size(); ++k) {
                        TreeNode* root = new TreeNode(i);
                        root->left = left[j];
                        root->right = right[k];
                        result.push_back(root);
                    }
                }
        }
        
        return result;
    }
};
```

## :memo: 刷題檢討
解決遞迴題目之前要先將數學表示方式寫出來，再利用coding去寫成程式，trace檢查有沒有錯誤，解遞迴腦袋要靈活才想得出來!