# 演算法作業04 ## 第1題: Selection Sort ### 請使用Selection sort排序,6,4,1,3,5。在四個回合中,每回合的Change of Flag數量為何?總共的Change of Flag數量為何? :::info Change of Flag的定義(個人想法): 從$a_0$開始往$a_{n-1}$走,如果遇到比較小的$a_i$就+1, 然後把$a_0$跟$a_i$交換,繼續比對 ::: :::info 初始陣列: | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 6 | 4 | 1 | 3 | 5 | ::: :::info 第一回合 初始化: `f`設為0 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | <font color="#f00">6</font> | 4 | 1 | 3 | 5 | | j | f | | --- | --- | | 0 | 0 | 遇到4,$a_k<a_f$, `Change_of_Flag++` `f=j` | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 6 | <font color="#f00">4</font> | 1 | 3 | 5 | | j | f | | --- | --- | | 0 | 1 | 遇到1,$a_k<a_f$, `Change_of_Flag++` `f=2` | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 6 | 4 | <font color="#f00">1</font> | 3 | 5 | | j | f | | --- | --- | | 0 | 2 | 遇到3,$a_k\geq a_f$,不做事 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 6 | 4 | 1 | <font color="#f00">3</font> | 5 | | j | f | | --- | --- | | 0 | 2 | 遇到5,$a_k\geq a_f$,不做事 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 6 | 4 | 1 | 3 | <font color="#f00">5</font> | | j | f | | --- | --- | | 0 | 2 | 最後將$a_j$和$a_k$交換 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | <font color="#f00">1</font> | 4 | <font color="#f00">6</font> | 3 | 5 | <font color="#f00">此回合的Change of Flag是$2$</font> ::: :::info 第二回合 初始化 `f`設為1 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | <font color="#f00">4</font> | 6 | 3 | 5 | | j | f | | --- | --- | | 1 | 1 | 遇到`6`,$a_k\geq a_f$,不做事 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | <font color="#f00">4</font> | 6 | 3 | 5 | | j | f | | --- | --- | | 1 | 1 | 遇到3,$a_j<a_f$, `Change_of_Flag++` `f=3` | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 4 | 6 | <font color="#f00">3</font> | 5 | | j | f | | --- | --- | | 1 | 3 | 遇到`5`,$a_k\geq a_f$,不做事 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 4 | 6 | <font color="#f00">3</font> | 5 | | j | f | | --- | --- | | 1 | 3 | 最後將$a_j$和$a_k$交換 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | <font color="#f00">3</font> | 6 | <font color="#f00">4</font> | 5 | <font color="#f00">此回合的Change of Flag是$1$</font> ::: :::info 第三回合 初始化 `f`設為2 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 3 | <font color="#f00">6</font> | 4 | 5 | | j | f | | --- | --- | | 2 | 2 | 遇到4,$a_k<a_f$, `Change_of_Flag++` `f=3` | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 3 | 6 | <font color="#f00">4</font> | 5 | | j | f | | --- | --- | | 2 | 3 | 遇到`5`,$a_k\geq a_f$,不做事 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 3 | 6 | <font color="#f00">4</font> | 5 | | j | f | | --- | --- | | 2 | 3 | 最後將$a_j$和$a_k$交換 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 3 | <font color="#f00">4</font> | <font color="#f00">6</font> | 5 | <font color="#f00">此回合的Change of Flag是$1$</font> ::: :::info 第四回合 初始化 `f`設為3 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 3 | 4 | <font color="#f00">6</font> | 5 | | j | f | | --- | --- | | 3 | 3 | 遇到4,$a_k<a_f$, `Change_of_Flag++` `f=4` | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 3 | 4 | 6 | <font color="#f00">5</font> | | j | f | | --- | --- | | 3 | 4 | 最後將$a_j$和$a_k$交換 | 0 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | --- | | 1 | 3 | 4 | <font color="#f00">5</font> | <font color="#f00">6</font> | <font color="#f00">此回合的Change of Flag是$1$</font> ::: :::success 第一回合:2 第二回合:1 第三回合:1 第四回合:1 總共:5 ::: ## 第2題: Quick Sort ### 使用Quick排序7個數字,1,2,3,4,5,6,7,請說明每回合的pivot如何選擇,會有best case。請參考投影片33頁,畫出二元樹表示。 ![Quick sort best case](https://hackmd.io/_uploads/BJOnN9WC6.png) :::info 每次選擇的pivot都要對半切,這樣就會是best case 紅色是第一刀,藍色是第二刀 ::: :::success 第一回合要選擇4 第二回合要選擇2 第三回合要選擇6 best case會是: 4213657 ::: ### 呈上題,請說明每回合的pivot如何選擇,會有worstcase。請參考投影片33頁,畫出二元樹表示。 ![Quick sort worst case](https://hackmd.io/_uploads/S1HUO5ZAa.png) :::info 每一刀都只把自己切掉,這樣就會跑$N$次 所以這棵二元樹會是歪斜的 ::: :::success 答案有兩個 一個左歪斜一個右歪斜 worst case: 1234567 or 7654321 ::: ## 第3題: 2-D Ranking Finding ### 平面上四個點,(1,1),(2,2),(3,3),(4,4)。請依照課本的方法,說明如何找出四個點的Ranking。(可配合畫圖表示) :::info 先把點排序好,再用中間值去切分 切到剩一個 再將左右邊合併 右邊每個點去加多少個`y`比他小的 ::: :::success 註:這邊以$(i,i)$的點以$i$表示 * 步驟一、排序 * 步驟二、遞迴切半 * $[1,2,3,4]$ * $[1,2]\ [3,4]$ * $[1]\ [2]\ [3]\ [4]$ * 步驟三、遞迴上來同時將左右邊做合併處理 0. $rank$初始狀態 | 1 | 2 | 3 | 4 | | --- | --- | --- | --- | | 0 | 0 | 0 | 0 | 1. $[1]+[2] => [1,2]$ | <font color="#00f">1</font> | <font color="#00f">2</font> | 3 | 4 | | --- | --- | --- | --- | | 0 | 1 | 0 | 0 | `y`比2小的有一個所以`rank[2]+=1` 2. $[3]+[4] => [3,4]$ | 1 | 2 | <font color="#00f">3</font> | <font color="#00f">4</font> | | --- | --- | --- | --- | | 0 | 1 | 0 | 1 | `y`比4小的有一個所以`rank[4]+=1` 3. $[1,2]+[3,4] => [1,2,3,4]$ | <font color="#00f">1</font> | <font color="#00f">2</font> | <font color="#00f">3</font> | <font color="#00f">4</font> | | --- | --- | --- | --- | | 0 | 1 | 2 | 3 | `y`比3小的有一個所以`rank[3]+=2` `y`比4小的有一個所以`rank[4]+=2` 結果就是 | dots | 1 | 2 | 3 | 4 | | ---- | --- | --- | --- | --- | | rank | 0 | 1 | 2 | 3 | ::: ![2D Ranking Finding](https://hackmd.io/_uploads/Bk8IJsZA6.gif) 我自己畫ㄉGIF ## 第4題:兩數之和 ### 解題思路1 枚舉每個$a_i$,然後設$tmp$為成為target的差 $tmp=target-a_i$ 然後因為要避免重複搜尋到自己($a_i$) 所以把二分搜的左右界先用好 1. 如果$tmp \geq a_i$,則`l=i+1,r=numbers.size()-1` 2. 如果$tmp<a_i$,則`l=0,r=i-1` 這樣就可以很輕鬆的使用二分搜 還可以避免搜尋到自身 最後再回傳就可以了 這個方法是$O(nlogn)$ ### 程式碼1 ```cpp= class Solution { public: vector<int> twoSum(vector<int>& numbers, int target) { int i,l,r,mid; vector<int> ans; for(i=0;i<numbers.size();i++) { int tmp=target-numbers[i]; if(tmp>=numbers[i]) { l=i+1; r=numbers.size()-1; } else { l=0; r=i-1; } while(l<=r) { mid=(l+r)>>1; if(numbers[mid]>=tmp) { r=mid-1; } else { l=mid+1; } } if(l<numbers.size()&&numbers[l]==tmp) { ans=vector<int>{min(i+1,l+1),max(i+1,l+1)}; break; } } return ans; } }; ``` #### 可以用也map解 時間複雜度一樣 如果改成unordered_map照理來說效果也會不錯 不過建雜湊的常數太高了,結果有點差 ```cpp= class Solution { public: vector<int> twoSum(vector<int>& numbers, int target) { map<int,int> m; for(int i=0;i<numbers.size();i++) m[numbers[i]]=i+1; for(int i=0;i<numbers.size();i++) { auto p=m.find(target-numbers[i]); if(p!=m.end()) return vector<int>{min(i+1,p->second),max(i+1,p->second)}; } return vector<int>{0,0}; } }; ``` ### 測試輸出輸入的畫面截圖1 ![image](https://hackmd.io/_uploads/SJNQiSpaa.png) 7ms ### 解題思路2 `l=0,r=numbers.size()-1` 分成4個狀況 1. `numbers[l]+numbers[r]>target` 2. `numbers[l]+numbers[r]<target` 4. `numbers[l]+numbers[r]==target` 狀況1: 要把`numbers[l]+numbers[r]`變小,所以把`r-1` 狀況2: 要把`numbers[l]+numbers[r]`變大,所以把`l+1` 狀況3: 符合`target`,所以就可以return答案了 這個方法是$O(n)$ ### 程式碼2 ```cpp= class Solution { public: vector<int> twoSum(vector<int>& numbers, int target) { int l=0,r=numbers.size()-1; int tmp=numbers[l]+numbers[r]; while(tmp!=target) { if(tmp>target) { r--; } else { l++; } tmp=numbers[l]+numbers[r]; } return vector<int>{l+1,r+1}; } }; ``` ### 測試輸出輸入的畫面截圖2 ![image](https://hackmd.io/_uploads/By5seqC6p.png) 10ms ### 解題思路3 把上面的`l+1`跟`r-1`改成用二分搜 時間一樣是$O(N)$,可是常數低很多 ### 程式碼3 ```cpp= class Solution { public: vector<int> twoSum(vector<int>& numbers, int target) { int ll,rr,l=0,r=numbers.size()-1,tmp,mid; while(numbers[l]+numbers[r]!=target&&l<=r) { cout<<l<<' '<<r<<endl; cout<<numbers[l]<<' '<<numbers[r]<<endl; if(numbers[l]+numbers[r]<target) { ll=l; rr=r-1; while(ll<=rr) { mid=(ll+rr)>>1; tmp=numbers[r]+numbers[mid]; if(tmp==target) { return vector<int>{mid+1,r+1}; } else if(target < tmp) { rr=mid-1; } else { ll=mid+1; } } l=ll; } else if(numbers[l]+numbers[r]>target) { ll=l+1; rr=r; while(ll<=rr) { mid=(ll+rr)>>1; tmp=numbers[l]+numbers[mid]; if(tmp==target) { return vector<int>{l+1,mid+1}; } else if(target < tmp) { rr=mid-1; } else { ll=mid+1; } } r=ll-1; } } return vector<int>{l+1,r+1}; } }; ``` ### 測試輸出輸入的畫面截圖3 ![image](https://hackmd.io/_uploads/rkTEx5Apa.png) 3ms超讚 ### 解題時間 12分鐘(如果不算後面幾個的時間的話) ### 完成程度 完全自己解出 ## 第5題:回文Palindrome ### 解題思路 `tolow()`是在做把大寫轉小寫,其他照樣傳 `isAaOR19()`是判斷是不是大小寫字母跟數字 一個指針放左邊 一個指針放右邊 過程中如果不符合`isAaOR19()`就跳下一個字 一直往中間跑 在跑之前先設一個`bool f=1` 如果有不同的字就把`f`改成0 最後再回傳`f`就好了 ### 程式碼 ```cpp= class Solution { public: char tolow(char ch) { return ch>='A'&&ch<='Z'?ch-'A'+'a':ch; } bool isAaOR19(char ch) { return ch>='a'&&ch<='z' || ch>='A'&&ch<='Z' || ch>='0'&&ch<='9'; } bool isPalindrome(string s) { int l=0,r=s.size()-1; bool f=1; while(l<r) { while(l<r&&!isAaOR19(s[l])) { l++; } while(l<r&&!isAaOR19(s[r])) { r--; } if(tolow(s[l])!=tolow(s[r])) { f=0; break; } l++; r--; } return f; } }; ``` ### 測試輸出輸入的畫面截圖 ![image](https://hackmd.io/_uploads/B10uxU66a.png) ### 解題時間 10分鐘 ### 完成程度 完全自己解出 ## 第6題:Linked List是否存在Cycle ### 解題思路 直覺(?) 龜兔賽跑:D,看到判圈就想到歐拉迴圈,就用上去了 簡單來說就是 `r`是兔子一次跑兩步 `t`是烏龜一次跑一步 如果有環最終一定會撞到一起 ### 程式碼 ```cpp= /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: bool hasCycle(ListNode *head) { ListNode *r=head; ListNode *t=head; while(t!=NULL&&r!=NULL&&r->next!=NULL) { r=r->next->next; t=t->next; if(r==t) { return 1; } } return 0; } }; ``` ### 測試輸出輸入的畫面截圖 ![image](https://hackmd.io/_uploads/rkdxWrp6T.png) ### 解題時間 8分鐘 ### 完成程度 完全自己解出 ## 第7題:心得 已填