# Leetcode刷題學習筆記-- Counting Sort, Radix Sort ## Counting Sort ### Reference ref1 : [Counting Sort from GeeksforGeeks](https://www.geeksforgeeks.org/counting-sort/) ref2 : [Counting Sort from Leetcode](https://leetcode.com/explore/learn/card/sorting/695/non-comparison-based-sorts/4437/) ### Introduction 當需要排序的數列範圍不是很大的時候,可以使用counting sort。 + non comparison-based algorithm + non In Place algorithm ### Procedure 1. 宣告一個vector來記錄所以數值可能出現的範圍。 ```cpp vector<int> nums{5, 3, -1, 2, 4}; auto [mn, mx] = minmax_element(begin(nums), end(nums)); int range = *mx - *mn + 1, shift = *mn; vector<int> count(range); ``` 2. 統計每個數字出現的次數 ``` cpp for(auto& n : nums) count[n - shift]++; ``` ![cout_the_number](https://media.geeksforgeeks.org/wp-content/cdn-uploads/scene01153.jpg) 3. 計算prefix sum得到每個數字要排序的位置。 ```cpp for(int i = 1; i < count.size(); ++i) count[i] += count[i - 1]; ``` 或是使用 ```cpp partial_sum(count.begin(), count.end(), count.begin()); ``` ![prefix_sum](https://media.geeksforgeeks.org/wp-content/cdn-uploads/scene02881.jpg) 4. 使用一個和nums一樣大的vector來記錄排序後的結果,從後面走訪來保持stable。 ```cpp vector<int> result(nums.size()); for(int i = nums.size() - 1; i >= 0; --i) { result[count[nums[i] - shift] - 1] = nums[i]; count[nums[i] - shift]--; } ``` 5. 把結果回傳回去,或是覆蓋原本的輸入vector。 ```cpp nums = result; // 會呼叫vector的copy assignment。 ``` ### Example #### [75. Sort Colors](https://leetcode.com/problems/sort-colors/) ```cpp void sortColors(vector<int>& nums) { vector<int> count(3), tmp(nums.size()); for(auto& n : nums) count[n]++; partial_sum(count.begin(), count.end(), count.begin()); for(int i = nums.size() - 1; i >= 0; --i) { tmp[count[nums[i]] - 1] = nums[i]; count[nums[i]]--; } nums = tmp; } ``` #### [1200. Minimum Absolute Difference](https://leetcode.com/problems/minimum-absolute-difference/) ```cpp vector<vector<int>> minimumAbsDifference(vector<int>& arr) { const auto [mn, mx] = minmax_element(begin(arr), end(arr)); int range = *mx - *mn + 1, shift = *mn; vector<int> count(range); for(auto& n : arr) count[n - shift]++; int left = 0, right = left + 1, mindiff = INT_MAX; vector<vector<int>> ans; while(right < count.size()) { right = left + 1; while(right < count.size() && count[right] == 0) ++right; if(right < count.size()) { int diff = right - left; if(diff < mindiff) { mindiff = diff; ans = {{left + shift, right + shift}}; } else if(diff == mindiff) ans.push_back({left + shift, right + shift}); left = right; } else break; } return ans; } ``` ## Radix Sort ### Reference ref : [Radix Sort from GeeksforGeeks](https://www.geeksforgeeks.org/radix-sort/) ref : [Radix Sort from Leetcode](https://leetcode.com/explore/learn/card/sorting/695/non-comparison-based-sorts/4438/) ### Introduction + 所有的comparsion based sorting algorithm 最快就是$O(NlogN)$ + 可以達到線性的time complxity只有counting sort和radix sort。 + 但是counting sort對數字範圍很大的比較不利。 + 從least significant digit一直排序到most significant digit + non in-place algorithm + Stable Sort ### Procedure 1. 先取得最大值或是最長的字串,因為這是要做多少次counting sort的基礎。 如果使用max_element得到的為iterator,必須在使用前先取值,不然數值會被第一次排序後改變。 ```cpp vector<int> nums; int maxv = *max_element(begin(nums), end(nums)); ``` 2. 對每個位數做counting sort exp = 1, 10, 100, 1000, ... ```cpp for(int exp = 1; maxv / exp > 0; exp *= 10) { countSort(nums, exp); } ``` 3. 對某個位數進行counting sort ```cpp void countSort(vector<int>& nums, int exp) { vector<int> output(nums.size()); vector<int> count(10); // 因為數字為0~9 for(auto& n : nums) // 統計每個數字的個數 count[(n / exp) % 10]++; // 計算每個數字的開頭index partial_sum(begin(count), end(count), begin(count)); // 產生根據這個exp的排序 for(int i = nums.size() - 1; i >= 0; --i) { output[count[(nums[i] / exp) % 10] - 1] = nums[i]; count[(nums[i] / exp) % 10]--; } // 取代原先的input vector nums = output; } ``` ### Complexity #### Time Complexity ==$O(W(N+K))$== 其中$O(N+K)$ 為counting sort, N: 要被排序vector的大小 K: size of counting vector W: 最大數值的位數。$W = log10(MaxValue)$ #### Space complexity 和counting sort一樣 $O(N+K)$ ### Problems #### [2343. Query Kth Smallest Trimmed Number](https://leetcode.com/problems/query-kth-smallest-trimmed-number/) ```cpp class Solution { void countSort(vector<string>& nums, int pos, vector<int>& idx) { vector<int> oidx(idx.size()); vector<int> count(10); for(auto& n : nums) count[n[pos] - '0']++; partial_sum(begin(count), end(count), begin(count)); for(int i = nums.size() - 1; i >= 0; --i) { string str = nums[idx[i]]; char c = str[pos]; int j = c - '0'; oidx[count[j] - 1] = idx[i]; count[j]--; } idx = oidx; } public: vector<int> smallestTrimmedNumbers(vector<string>& nums, vector<vector<int>>& queries) { unordered_map<int, vector<int>> m; vector<int> idx(nums.size()); iota(idx.begin(), idx.end(), 0); int sz = nums[0].size(); for(int i = 1; i <= sz; ++i) { countSort(nums, sz - i, idx); m[i] = idx; } vector<int> rtn; for(auto& q : queries) rtn.push_back(m[q[1]][q[0] - 1]); return rtn; } }; ``` #### [164. Maximum Gap](https://leetcode.com/problems/maximum-gap/) ```cpp class Solution { void countSort(vector<int>& nums, int exp) { vector<int> output(nums.size()); vector<int> count(10); for(auto& n : nums) count[(n / exp) % 10]++; partial_sum(count.begin(), count.end(), count.begin()); for(int i = nums.size() - 1; i >= 0; --i) { int n = (nums[i] / exp) % 10; output[count[n] - 1] = nums[i]; count[n]--; } for(int i = 0; i < nums.size(); ++i) nums[i] = output[i]; } public: int maximumGap(vector<int>& nums) { int maxval = *max_element(nums.begin(), nums.end()); for(int exp = 1; maxval / exp > 0; exp *= 10) countSort(nums, exp); int ans = 0; for(int i = 1; i < nums.size(); ++i) ans = max(ans, nums[i] - nums[i - 1]); return ans; } }; ``` ###### tags: `leetcode` `刷題`