作業三題解 === ###### tags: `成大演算法春季課程` ## [Sort the array!](https://judge.ccns.io/problems/8) - Task Provider: 參考 [Leetcode : 912. Sort an Array](https://leetcode.com/problems/sort-an-array/) - Task Setter: joey3639570 ### 解法說明 根據題目要求: > 順帶一提,老師在這裡要的排序方程式有以下要求: > - 大多數的時候希望有 $O(n\log(n))$ 的時間複雜度, > - 在 Worst Case 的時候不該是 $n^2$ 的時間複雜度 按題目所敘述的話是盡量不要使用 **Quicksort** 即可, 提到 sort 很多人的第一反應是直接使用 `std::sort` 在此希望各位去看看 `std::sort` 的細節, 至少要知道自己所用的函式內容為何,及其於各 Case 的表現。 >**Algorithms used by sort()** The algorithm used by sort() is **IntroSort**. Introsort being a **hybrid sorting algorithm** uses three sorting algorithm to minimize the running time, **Quicksort, Heapsort and Insertion Sort**. Simply putting, it is the best sorting algorithm around. It is a hybrid sorting algorithm, which means that it uses more than one sorting algorithms as a routine. 參考連結: [Internal details of std::sort() in C++](https://www.geeksforgeeks.org/internal-details-of-stdsort-in-c/) ### 參考解答 在此我使用 HeapSort 解決此排序問題。 | Case | Complexity | |:-------:|:----------:| | Worst | $O(nlogn)$ | | Best | $O(nlogn)$ | | Average | $O(nlogn)$ | ```cpp= #include <iostream> #define SIZE 50000 using namespace std; int n, arr[SIZE]; // To heapify a subtree rooted with node i which is // an index in arr[]. n is size of heap void heapify(int arr[], int n, int i) { int largest = i; // Initialize largest as root int l = 2 * i + 1; // left = 2*i + 1 int r = 2 * i + 2; // right = 2*i + 2 // If left child is larger than root if (l < n && arr[l] > arr[largest]) largest = l; // If right child is larger than largest so far if (r < n && arr[r] > arr[largest]) largest = r; // If largest is not root if (largest != i) { swap(arr[i], arr[largest]); // Recursively heapify the affected sub-tree heapify(arr, n, largest); } } // main function to do heap sort void heapSort(int arr[], int n) { // Build heap (rearrange array) for (int i = n / 2 - 1; i >= 0; i--) heapify(arr, n, i); // One by one extract an element from heap for (int i = n - 1; i > 0; i--) { // Move current root to end swap(arr[0], arr[i]); // call max heapify on the reduced heap heapify(arr, i, 0); } } /* A utility function to print array of size n */ void printArray(int arr[], int n) { for (int i = 0; i < n; ++i) cout << arr[i] << " "; cout << "\n"; } int main() { cin >> n; for(int i=0; i<n; i++){ cin >> arr[i]; } heapSort(arr, n); for (int i = 0; i < n; i++) { cout << arr[i] << " "; } return 0; } ``` ## [min-heapify](https://judge.ccns.io/problems/8) - Task Provider: 課本講義 - Task Setter: joey3639570 ### 解法說明 此題目的只是為了看各位對於課本講義上的理解, 講義上提供的例子為 Max-heapify , Pseudo Code 如下: ![](https://i.imgur.com/NxSRmPi.png) ![](https://i.imgur.com/GsfOetD.png) 此題須注意的是 level order 的部分, 如同同學一開始指出的,**一個一個放入樹,且保持 min-heap** 與 **都先放入樹,再將樹建立成 min-heap** 是不同的。 本題要求的是後者。 ### 參考解答 ```cpp= #include <iostream> #define SIZE 1025 int n, arr[SIZE]; using namespace std; void min_heapify(int *a,int i,int N) { int j, temp; temp = a[i]; j = 2 * i; while (j <= N) { if (j < n && a[j+1] < a[j]) j = j + 1; if (temp < a[j]) break; else if (temp >= a[j]) { a[j/2] = a[j]; j = 2 * j; } } a[j/2] = temp; return; } void build_minheap(int *a, int N) { int i; for(i = N/2; i >= 1; i--) { min_heapify(a,i,N); } } int main() { int i; cin>>n; for (i = 1; i <= n; i++) { cin>>arr[i]; } build_minheap(arr, n); for (i = 1; i <= n; i++) { cout << arr[i] << " "; } } ``` ## [Smallest Range Covering Elements from k Lists](https://judge.ccns.io/problems/10) - Task Provider: 參考 [Leetcode : 632. Smallest Range Covering Elements from K Lists](https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/) - Task Setter: joey3639570 ### 解法說明 此題為 heap 的應用延伸, 當然其他作法跟解法很多,但這裡以會以 min-heap 解法為主, 使用 min-heap 的原因是避免多次搜尋最小值影響時間, 在這邊助教沒設定好測資跟時間的大小,可能會導致有時間內但 TLE 的狀況發生,但其實可能是 SE 或其他錯誤。 解題邏輯: - 由於我們知道每個 list 都是非遞減排列,list 的開頭第一個元素應該是該 list 的最小元素。 - 既然要的是每個 List 都要 cover 到的範圍,從 list 內取出的值會是最貼合範圍的取法。 - 在這裡我們使用取最小範圍的方法是 - 利用 min_heap 紀錄當前最小元素。 - 以比較的方式隨時知道當前最大元素。 - 範圍為**最大元素 - min_heap 最前端的元素 + 1** ### 參考解答 在這裡我使用 `priority_queue` 協助我建立一個好操作的 min-heap ,存放於內的元素為 `pair<int,pair<int,int>>` ,詳細使用說明請參考以下: - [std::priority_queue](https://www.cplusplus.com/reference/queue/priority_queue/) - [How to implement Min Heap using STL?](https://www.geeksforgeeks.org/implement-min-heap-using-stl/) ```cpp= #include <iostream> #include <queue> #include <vector> #include <climits> using namespace std; vector<int> smallestRange(vector<vector<int>>& nums) { priority_queue<pair<int,pair<int,int>>,vector<pair<int,pair<int,int>>>,greater<pair<int,pair<int,int>>>>pq; int mx=INT_MIN; int cans=INT_MAX; int lo=INT_MIN,hi=INT_MAX; for(unsigned int i=0;i<nums.size();i++) { mx=max(mx,nums[i][0]); pq.push({nums[i][0],{i,0}}); } while(!pq.empty()) { auto x=pq.top(); pq.pop(); int na=mx-x.first+1; if(cans>na) { cans=na; lo=x.first; hi=mx; } if(x.second.second>=nums[x.second.first].size()-1) break; pq.push({nums[x.second.first][x.second.second+1],{x.second.first,x.second.second+1}}); mx=max(mx,nums[x.second.first][x.second.second+1]); } return {lo,hi}; } int main () { int n, k, a; cin >> n; std::vector<vector<int>> wholevector; for (int i=0;i<n;i++) { cin >> k; vector<int> tmpvector; for (int j=0;j<k;j++) { cin >> a; tmpvector.push_back(a); } wholevector.push_back(tmpvector); } vector<int> result = smallestRange(wholevector); for (size_t i = 0; i < result.size(); ++i) { cout << result[i] << " "; } return 0; } ```