--- title: Algorithm homework II --- # Problem 1 - Bubble sort: 基於 bubble sort 會前項比後項, 因此會直接比較兩個相等值的元素 - Insertion sort: Insertion sort 會將比較的值與上一項的值作比較, 若大於比較值上一項的值會寫入到當前比較項的後項, 因此遇到相同值則可以直接 Insert 至相同值的後面 - Quick sort: ## bubble sort 穩定,基於 bubble sort 會前項比後項, 因此會直接比較兩個相等值的元素,這時候不會交換,如果兩個相等的元素沒有相鄰,也能通過前面的兩兩交換把兩個相鄰起來,所以氣泡排序是一種穩定的排序演算法。 in place,因為是兩個元素比較,不會用到額外的空間。 ## insertion sort 穩定,將資料分成已排序、未排序兩部份,比較是從已排序序列的末尾開始,也就是想要插入的元素和已經已排序的最大者開始比起,如果比它大則直接插入在其後面,否則一直往前找直到找到它該插入的位置。如果碰見一個和插入元素相等的,那麼插入元素把想插入的元素放在相等元素的後面。所以,相等元素的前後順序沒有改變,所以插入排序是穩定的。 in place,將資料分成已排序、未排序兩部份,不會用到額外的空間 ## quick sort 不穩定,假設第一個數為基準值,從將比基準值(Pivot)小的數值移到左邊,比基準值大的數值移到右邊, 如果序列為 7 4 5 4 8 9 ,將7和4交換便會將穩定性打亂。 not in place,因為遞迴需要額外的堆疊空間 ## merge sort 穩定,merge sort會先不斷拆成1個或2個,2個元素如果大小相等也沒有人故意交換,之後合併過程中我們可以保證如果兩個當前元素相等時,我們把處在前面的序列的元素儲存在結果序列的前面,這樣就保證了穩定性。所以,歸併排序也是穩定的排序演算法。 not in place,需要暫時性的暫列存放每回合Merge後的結果 ## heap-sort. 不穩定, 如果a序列為 7 4 5 4 8 9 4 第一步將a[2],a[5],a[6]比大小,變為 7 4 9 4 8 5 4 第二步將a[1],a[3],a[4]比大小,變為 7 8 9 4 4 5 4 這時相同的元素之間的穩定性就被破壞了。所以,堆排序不是穩定的排序演算法。 in place,不會用到額外的空間 # Problem 2 (solved) 只能算到92681,超過就會卡 ```C++ #include <iostream> using namespace std; int main(){ int n, l = 1, r, m; cin >> n; m = n/2; r = n/2; while(!(m*m < n && (m + 1)*(m + 1) > n)){ if(m*m == n){ cout << m << ' ' << m*m << endl; return 0; } else if(m*m > n) r = m; else l = m; m = (l + r)/2; } cout << m << ' ' << m*m << endl; } ``` # Problem 3 如何將Merge Sort在額外空間的占用優化至僅使用n/2 (以下方式使用遞迴) ``` 第一步:先將原本n個元素組成的數列切割成左右對半,先取左半邊 第二步:繼續切割至數列僅剩一個元素 第三步:開始合併直到取的半邊完全排序好 (在此步驟裡合併好的數列暫存在額外空間中,假設為temp,而它的尺寸為n/2) 第四步:將temp中排序好的數列直接覆蓋原本數列的左半邊 第五步:接著取右半邊,重複執行第二步與第三步 第六步:用排好的原數列左半邊與temp做最後的排序,再將排序好的temp覆蓋原數列的右半邊 ``` # Problem 4 (solved) Data structure ```=c++ #include <iostream> #include <unordered_set> #include <vector> #include <sstream> using namespace std; struct Set{ unordered_set<int> got; vector<int> elem; Set(){ stringstream ss; string s; int n; getline(cin, s); ss << s; while(ss >> n){ if(got.find(n) == got.end()){ got.insert(n); elem.push_back(n); } } } }; void Union(){ cin.ignore(); Set s1; Set s2; unordered_set<int>::iterator i; vector<int>::iterator j; for(j = s2.elem.begin();j != s2.elem.end();j++){ if(s1.got.find(*j) == s1.got.end()) cout << *j << ' '; } for(i = s1.got.begin();i != s1.got.end();i++) cout << *i << ' '; cout << endl; return; } void Intersection(){ cin.ignore(); Set s1; Set s2; vector<int>::iterator i; for(i = s2.elem.begin();i != s2.elem.end();i++){ if(s1.got.find(*i) != s1.got.end()) cout << *i << ' '; } cout << endl; return; } void elem(){ int t, n; stringstream ss; string s; cin >> t; cin.ignore(); getline(cin, s); ss << s; while(ss >> n){ if(n == t){ cout << "Yes" << endl; return; } } cout << "No" << endl; return; } int main(){ int n; while(1){ cout << "1.union\n2.intersection\n3.in\n4.quit\ninput #:"; cin >> n; switch (n){ case 1: Union(); break; case 2: Intersection(); break; case 3: elem(); break; case 4: return 0; default: cout << "No this option" << endl; } cout << "--------------------------------------" << endl; } } ``` > 幹麻一直偷我要寫的題目 [name=ZoneTwelve][time=Wed, Mar 18, 2020 10:21AM][color=#0084ff] >> 你就慢[name=vic0103520] ```=c++ // 這還沒寫完 #include <iostream> #include <vector> class SET{ public: std::vector<int> array; int find(int el); void add(int el); void print(); SET union(SET B); SET intersection(SET B); }; int SET::find(int el){ int l = 0, r = array.size(), c = -1; while(l<r){ c = l + ( ( r - l ) / 2 ); if(el>array[c]){ l = c; }else if(el<array[c]){ r = c; }else{ return c; } std::cout<<l<<" - "<<c<<" - "<<r<<"\n"; } } void SET::add(int el){ int i = 0; for(i;i<array.size()&&array[i]<el;i++){ if(array[i]==el) return; } array.insert(array.begin()+i, el); } SET SET::union(SET B){ int i; while(i<array.size()||i<B.array.size()){ std::cout<<(i++)<<" "; } } void SET::print(){for(int i=0;i<array.size();i++)std::cout<<array[i]<<" ";std::cout<<"\n";} int main(){ SET A; std::cout<<"Hello\n"; A.add(3); A.add(1); A.add(4); A.add(2); A.print(); std::cout<<"Program end;\n"; } ``` # Problem 5 (solved) ## 解法1: 演算法概念: 透過已排序的兩個陣列去做 binary search 來搜尋X中的每項相對最接近的值 要求: min i, j (|X[i] - Y[j]|) 項 X[m], Y[n] binary search => O(M$\log_{2}{n}$) ```C++ #include <iostream> #include <vector> std::vector<int> X; // 3, 5, 7 std::vector<int> Y; // 4 , 7 int abs(int a){return a>0?a:-a;} void minimum(std::vector<int> X, std::vector<int> Y){ int min = 99999; int ri, rj; for(int x=0;x<X.size();x++){ int l = 0, r = Y.size(); int c; while(l<r){ c = l + (r - l) / 2; if(X[x]>Y[c]){ l+=c; }else if(X[x]<Y[c]){ r-=c+1; }else{ break; } } if(min>abs(X[x]-Y[c])){ min = abs(X[x]-Y[c]); ri = x; rj = c; } } std::cout<<"result: "<<min<<" - "<<ri<<", "<<rj<<"\n"; } int main(){ int xlen = 5, ylen = 5; //int x[xlen] = {3, 5, 7}; //int y[ylen] = { 4 , 7 }; int x[xlen] = {1, 4, 8, 9, 10}; int y[ylen] = {2, 3 ,7, 8, 10}; for(int i=0;i<xlen;i++) X.push_back(x[i]); for(int i=0;i<ylen;i++) Y.push_back(y[i]); //for(int i=0;i<Y.size();i++) // std::cout<<Y[i]<<" "; minimum(X, Y); } ``` > 有 bug [name=ZoneTwelve][time=Wed, Mar 18, 2020 10:32 AM][color=#0084ff] ## 解法2: >修是修好了,但index可能會倒過來:)[name=中央第一網管] >> 臭87 [name=ZoneTelve][color=#0084ff] > 我發現Y的尾巴大就不會出問題,所以先比較最後一項再決定怎麼call > 原本 > 1 2 10 > 5 6 9 > 會出問題,j在X比較大的時候會跑出input,Y[j]會變成零,所以簡單的解決辦法就是先比較再call > [name=中央最強網管] > > Time complexity O(M+N) ```C++ #include <iostream> #include <vector> using namespace std; int abs(int x){return x >= 0 ? x : -x;} void minimum(vector<int> x, vector<int> y){ int i, j = 0, min = 999999, a, b; for(i = 0;i < x.size();i++){ for(;j < y.size() && y[j] < x[i];j++){} if(y[j] == x[i]){ cout << i << ' ' << j << ' ' << 0 << endl; return; } else if(y[j] - x[i] < min || j > 0 && x[i] - y[--j] < min){ min = abs(y[j] - x[i]); a = i; b = j; } } cout << a << ' ' << b << ' ' << min << endl; return; } int main(){ vector<int> X, Y; int n; while(cin >> n){ if(n == 0) break; X.push_back(n); } while(cin >> n){ if(n == 0) break; Y.push_back(n); } X.back() > Y.back() ? minimum(Y, X) : minimum(X, Y); } ``` # Problem 6 (solved) T(n) = 2T(n/2)+n-1 = 4T(n/4)+2n-(1+2) = 8T(n/8)+3n-(1+2+4) . . . = 2^(k-1)*T(2)+(k-1)n-(2^(k-1)-1) = cn/2+n(lgn-1)-n/2+1 = nlgn+(c/2-3/2)n+1 # Problem 7 ## Solution: ```=c++ #include <bits/stdc++.h> #include <sstream> std::vector<int> keys; class SET{ public: int length; std::vector<int> keys; std::map<int, int> set; SET(){length = 0;} void pushme(int); }; void SET::pushme(int el){ keys.push_back(el); set[el] = el; length++; } void solution(SET mySet, int x, std::string com = "", int n = 0, int result = 0){ std::stringstream ss; std::string tmp; for(int i=n+1;i<mySet.length+1;i++){ ss<<mySet.keys[n]; ss>>tmp; if(i<mySet.length) solution(mySet, x, com+tmp+" ", i, result+mySet.keys[n]); } if(result==x) std::cout<<result<<"\t"<<com<<"\n"; } int main(){ int x, tmp; SET mySet; std::cin>>x; while(std::cin>>tmp) mySet.pushme(tmp); std::cout<<"---Program run---\n"; solution(mySet, x); std::cout<<"---Program end---\n"; } ``` ## 描述拉幹: 題目:判斷可否從n個數字中找出k個數和為M 作法:從n個數中取一數稱t,再從剩下n-1個中找是否有k-1個數和為M-t,持續此動作直到k=1,若M'=k則true else false 分析:此作法等同找出任意大小為k,和為M之組合,每個動作都找出一個樣本,則動作總數為樣本空間之大小C(n, k)=>時間複雜度為min(O(n^k), O(n^(n-k))).