# 演算法作業七
## 觀念題
### 第1題: 2-D Maxima Finding Problem
- 請比較此兩個問題與其解法,有何相同與相異之處。
- 相似
1. 兩個演算法都是使用 D&C(分治法)策略
2. 合併時都要先看右邊
3. 都要用到支配的概念
- 相異
| **2-D Maxima Finding Problem** | **2-D Ranking Finding Problem** |
|:------------------------------:|:-------------------------------:|
| 找出不被其他點支配的點 | 找出該點支配的數量 |
- 若將此問題,由2-D延伸到3-D,請你嘗試設計出一個解法
1. STEP1: 用x y平面切割成四塊
2. STEP2: 將這四塊遞迴的找不被支配的點,我們分四個象限
3. STEP3: 四塊進行合併
- 第三象限與第四象現尋找max(z),並各自和二、一合併
- 接下來尋找右邊之max(y),並執行合併
### 第2題: Closest Pair Problem
- 前處理
1. 以y值排列個點
- Divide
2. 如果該區域只剩下一個點,回傳無限
3. 尋找這區x的中位數,並進行切割(等等用$S_L$,$S_R$表示)
4. (GOTO2),開始遞迴
- Merge
5. 將$S_L$與$S_R$之回傳數值找最小值,以下以$d$表示
- 將中線L左右擴大d (即$[L+d,L-d$])
- For $P_i$ in 左邊的點P($P \in [L-d,L$])
- let $y$=$P_i.y$
- 計算右邊的在$y+d$到$y-d$的全部點之距離
- 若是上面產生之最小值比d還小,則更新d
### 第3題: Convex Hull Problem
- 為何不直接用sort來得到polygon,而要採用merge?
- Sort的複雜度:$O(nlogn)$
- Merge複雜度:$O(n)$
- 使用Merge更有效率
- 請說明如何將polygon,修改成convex hull?
- 刪除內角超過180的節點
## 程式題
### 第4題: 將linked list的數列排序
[leetcode 148](https://leetcode.com/problems/sort-list/description/)
- Solution 1: Convert to array
- 程式碼
:::spoiler Python
```python=
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
arr = []
while head:
arr.append(head.val)
head = head.next
arr.sort()
if len(arr) == 0: return None
newHead = ListNode(arr[0])
curr = newHead
for i in arr[1:]:
curr.next = ListNode(i)
curr = curr.next
return newHead
```
:::
- 執行結果

- 花費時間: 10分鐘
- 完成程度:靠自己
- 思路
~~好一開始偷懶的解法,我感到十分羞恥~~
1. 先將LinkList轉換成array
2. 然後直接sort
3. 最後再轉成LinlList
- Solution 2: Merge Sort
- 程式碼
:::spoiler C++
```C++=
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (!head || !head->next) return head;
typedef ListNode* nodePtr;
nodePtr slow, fast;
slow = head;
fast = head->next;
while (fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}
nodePtr mid = slow->next;
slow->next = NULL;
nodePtr left = sortList(head);
nodePtr right = sortList(mid);
nodePtr newHead = new ListNode();
nodePtr curr = newHead;
while (left && right){
if (left->val < right->val){
curr->next = left;
left = left->next;
}
else{
curr->next = right;
right = right->next;
}
curr = curr->next;
}
curr->next = left ? left : right;
return newHead->next;
}
};
```
:::
- 執行結果

- 花費時間: 1小時
- 完成程度:參考他人
- [ref 1](https://leetcode.com/problems/sort-list/solutions/1795310/python3-o-1-space-simple-and-concise-solution-with-explanation-mergesort-easy-to-understand/)
- 思路
0. 分享一下我卡的點: 如何找中間
1. 首先先找中間
- 使用龜兔賽跑法
- 當最快抵達終點最慢的將會是中心點
2. Divide
- 分成左右各自執行排序
3. Merge
- 用類似雙指針的方式
- 將較小的插入list中
- 直到其中一個null時結束
ps: 其中一個null時另一個可能還沒,我的作法是將還沒的直接接上
- 其他
- [merge sort教學](https://medium.com/appworks-school/%E5%88%9D%E5%AD%B8%E8%80%85%E5%AD%B8%E6%BC%94%E7%AE%97%E6%B3%95-%E6%8E%92%E5%BA%8F%E6%B3%95%E9%80%B2%E9%9A%8E-%E5%90%88%E4%BD%B5%E6%8E%92%E5%BA%8F%E6%B3%95-6252651c6f7e)
### 第5題: 將排序好的數列轉為二元搜尋樹
[leetcode 108](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/description/)
- 程式碼
:::spoiler C++
```C++=
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
typedef TreeNode* nodePtr;
if (nums.size() == 0) return NULL;
if (nums.size() == 1) return new TreeNode(nums[0]);
int mid = nums.size()/2;
nodePtr root = new TreeNode(nums[mid]);
vector<int> left(nums.begin(),nums.begin()+mid);
vector<int> right(nums.begin()+mid+1,nums.end());
root->left = sortedArrayToBST(left);
root->right= sortedArrayToBST(right);
return root;
}
};
```
:::
- 執行結果

- 花費時間: 30分鐘
- 完成程度: 靠自己
- 思路
0. 其實觀察一下測資可以發現輸入根本就是inorder
- Divide
1. 每次取出array的中間
- 遞迴中止條件
- 剩下一個 => 回傳該treenode
- 剩下零個 => 回傳NULLPTR
2. 接下來把array分成左右(不包含中間),並遞迴
- Merge
3. 將左子樹和右子樹接上中心點中
### 第6題: 美麗數列
[leetcode 932](https://leetcode.com/problems/beautiful-array/description/)
- 程式碼
:::spoiler C++
```C++=
class Solution {
public:
vector<int> beautifulArray(int n) {
if (n==1) return vector(1,1);
vector<int> old = beautifulArray(n-1);
vector<int> res;
for (int i:old){
if (2*i-1<=n){
res.push_back(2*i-1);
}
}
for (int i:old){
if (2*i<=n){
res.push_back(2*i);
}
}
return res;
}
};
```
:::
- 執行結果

- 花費時間: 體感2小時...
- 完成程度: 參考別人
- [ref1](https://leetcode.com/problems/beautiful-array/solutions/4673410/beautiful-array-efficient-divide-and-conquer-algo/)
- [ref2](https://leetcode.com/problems/beautiful-array/solutions/187669/share-my-o-nlogn-c-solution-with-proof-and-explanation/)
- [ref3](https://leetcode.com/problems/beautiful-array/solutions/3692187/recursive-code/)
- 思路
~~真的要抱怨這題有夠難解釋,而且很難想~~
0. 這題主要要我們產生一個沒有左右相加是自己兩倍的數列
- (odd + even = odd) => 奇數放左偶數放右,可避開
- 如果上一個是美麗數列,那我們用上一個進行映射呢? => 這樣用上一個的平移產生的也是美麗數列
- Divide
1. 先找到上一個的美麗數列
- Conquer
2. 接下來將上一個美數列進行平移處理,使他們相對關係不變
- 奇數,以$2n-1$處理 => 加入array(如果>=N)跳過
- 偶數,以$2n$處理 => 加入array(如果>=N)跳過
:::spoiler proof 平移會產生1~N之數列
給定一集合$S=\{1,2...N-1\}$
則以$2n-1$平移產生$S_1=\{1,3...2N-1\}$
則以$2n$平移產生$S_2=\{2,4...2N\}$
因此這兩個集合聯集,一定有1~N的數列
:::
## 心得
這周教了D&C,來到我最害怕的單元了,從以前就覺得遞迴好難,真是百事可樂。
BTW,我在理解遞迴上,我都使用stack這個資料結構輔助思考,這樣對某些題型我覺得會比較好思考。
我印象最深的是convex hull,之前影像處理有用過,但是是靠opencv的函數實現,我有空寫寫看leetcode 587好了,感覺很有趣。
這周作業我印象最深的是美麗數列,他根本一點都不美麗@@,anyway,我想超級久,我還看了一堆人的解題報告,想超級久,後來歸納兩個規則
1. (odd + even = odd) => 奇數放左偶數放右
2. 如果上一個是美麗數列,那我們用上一個進行映射呢? => 這樣用上一個的平移產生的也是美麗數列
3.
但我覺得這個還是好難解釋,希望檢討時有電神同學or學長能解釋Orz。