# 快速排序
![1692192746100]()
![1692193001985]()
![1692193017150]()
![1692193032132]()
![1692193065121]()
![1692193108614]()
![1692193116747]()
![1692193127291]()
![1692193149324]()
![1692193166398]()
![1692193196177]()
![1692193216071]()
| int partition(vector<int>& nums, int l, int r) { |
| |
| int i = l, j = r; |
| while (i < j) { |
| while (i < j && nums[j] >= nums[l]) j--; |
| while (i < j && nums[i] <= nums[l]) i++; |
| swap(nums[i], nums[j]); |
| } |
| swap(nums[i], nums[l]); |
| return i; |
| } |
| |
| void quickSort(vector<int>& nums, int l, int r) { |
| |
| if (l >= r) return; |
| |
| int i = partition(nums, l, r); |
| |
| quickSort(nums, l, i - 1); |
| quickSort(nums, i + 1, r); |
| } |
| |
| |
| vector<int> nums = { 4, 1, 3, 2, 5, 1 }; |
| quickSort(nums, 0, nums.size() - 1); |
l 和 r 分别是待排序数组的闭区间的左下标和右下标
在第一个对 i 的 while 循环中,必定会使 i 增 1。
外层 while 循环终止的时刻必定有 i=j,
i 和 j 到达同一个位置
i 和 j 到达相邻位置呢?这样会再次进入 while 循环,j 再次自减
两个内层 while 循环可以互换吗? 可以
在循环最后一个有一个 swap,
里面两个 while 小循环要加上 i<j
的条件,否则,如果遇到剩余的未遍历元素的值等于基准值时,在执行完两个 while 小循环后,会出现 i>j 的情况。
最后 i 和 j 停止的位置的数组值比基准值小,
两个内层 while 循环用的是大于等于和小于等于号,因为关键是找到首个小于或首个大于。
# 归并排序
![1692194249611]()
| |
| void mergeSort(vector<int>& nums, int l, int r) { |
| |
| if (l >= r) return; |
| |
| int m = (l + r) / 2; |
| mergeSort(nums, l, m); |
| mergeSort(nums, m + 1, r); |
| |
| int tmp[r - l + 1]; |
| for (int k = l; k <= r; k++) |
| tmp[k - l] = nums[k]; |
| int i = 0, j = m - l + 1; |
| for (int k = l; k <= r; k++) { |
| if (i == m - l + 1) |
| nums[k] = tmp[j++]; |
| else if (j == r - l + 1 || tmp[i] <= tmp[j]) |
| nums[k] = tmp[i++]; |
| else { |
| nums[k] = tmp[j++]; |
| } |
| } |
| } |
| |
| |
| vector<int> nums = { 4, 1, 3, 2, 5, 1 }; |
| mergeSort(nums, 0, nums.size() - 1); |
![1692195248013]()
vector 没有切片方法,所以要传入待排序的左右下标
| void merge_sort(vector<int> &nums, int l, int r) { |
| if (l >= r) |
| return; |
| |
| int m = (l + r) / 2; |
| |
| merge_sort(nums, l, m); |
| merge_sort(nums, m + 1, r); |
| |
| |
| vector<int> temp; |
| |
| for (int i = 0; i < r - l + 1; i++) { |
| |
| temp.push_back(nums[l + i]); |
| } |
| |
| int i = 0, j = m - l + 1; |
| |
| for (int k = l; k <= r; k++) { |
| if (j >= r - l + 1 || temp[i] <= temp[j]) { |
| nums[k] = temp[i++]; |
| } |
| else { |
| nums[k] = temp[j++]; |
| } |
| } |
| } |
# 剑指 Offer 61. 扑克牌中的顺子
![1692201901262]()
| class Solution { |
| public: |
| bool isStraight(vector<int>& nums) { |
| sort(nums.begin(), nums.end()); |
| |
| int zero_count = 0; |
| int index = 0; |
| |
| |
| for (; index < nums.size(); index++) { |
| if (nums[index] == 0) { |
| zero_count++; |
| } |
| else { |
| break; |
| } |
| } |
| |
| int gap_count = 0; |
| for (; index < nums.size() - 1; index++) { |
| |
| |
| if (nums[index + 1] == nums[index]) { |
| return false; |
| } |
| |
| gap_count += nums[index + 1] - nums[index] - 1; |
| } |
| |
| if (zero_count >= gap_count) { |
| return true; |
| } |
| else { |
| return false; |
| } |
| |
| } |
| }; |
![1692201931376]()
| class Solution { |
| public: |
| bool isStraight(vector<int>& nums) { |
| unordered_set<int> repeat; |
| int ma = 0, mi = 14; |
| for(int num : nums) { |
| if(num == 0) continue; |
| ma = max(ma, num); |
| mi = min(mi, num); |
| if(repeat.find(num) != repeat.end()) return false; |
| repeat.insert(num); |
| } |
| return ma - mi < 5; |
| } |
| }; |
| class Solution { |
| public: |
| bool isStraight(vector<int>& nums) { |
| |
| set<int> st; |
| int ma = 0, mi = 0; |
| |
| bool is_first = true; |
| |
| for (int num : nums) { |
| if (num != 0) { |
| if (st.find(num) == st.end()) { |
| st.insert(num); |
| |
| if (is_first) { |
| ma = mi = num; |
| is_first = false; |
| } |
| else { |
| ma = max(ma, num); |
| mi = min(mi, num); |
| } |
| } |
| else { |
| return false; |
| } |
| } |
| } |
| |
| return ma - mi < 5; |
| |
| } |
| }; |
![1692202017170]()
| class Solution { |
| public: |
| bool isStraight(vector<int>& nums) { |
| int joker = 0; |
| sort(nums.begin(), nums.end()); |
| for(int i = 0; i < 4; i++) { |
| if(nums[i] == 0) joker++; |
| else if(nums[i] == nums[i + 1]) return false; |
| } |
| return nums[4] - nums[joker] < 5; |
| } |
| }; |
# 剑指 Offer 45. 把数组排成最小的数
![1692203229613]()
![1692203328379]()
| class Solution { |
| public: |
| string minNumber(vector<int>& nums) { |
| vector<string> strs; |
| string res; |
| for(int i = 0; i < nums.size(); i++) |
| strs.push_back(to_string(nums[i])); |
| sort(strs.begin(), strs.end(), [](string& x, string& y){ return x + y < y + x; }); |
| for(int i = 0; i < strs.size(); i++) |
| res.append(strs[i]); |
| return res; |
| } |
| }; |
| class Solution { |
| public: |
| string minNumber(vector<int> &nums) { |
| vector<string> strs; |
| for (int num: nums) { |
| strs.push_back(to_string(num)); |
| } |
| |
| sort(strs.begin(), strs.end(), cmp); |
| |
| string res = ""; |
| |
| for(string item:strs){ |
| res += item; |
| } |
| |
| return res; |
| } |
| |
| private: |
| static bool cmp(string x, string y) { |
| return x + y < y + x; |
| } |
| |
| }; |
# 剑指 Offer 41. 数据流中的中位数
![1692260266579]()
| class MedianFinder { |
| public: |
| |
| MedianFinder() { |
| |
| } |
| |
| void addNum(int num) { |
| if (ve.size() == 0) { |
| ve.push_back(num); |
| } else { |
| vector<int>::iterator it = ve.begin(); |
| |
| bool is_insert = false; |
| |
| for (; it != ve.end(); it++) { |
| |
| if (*it > num) { |
| ve.insert(it, num); |
| is_insert = true; |
| break; |
| } |
| } |
| |
| if (!is_insert) { |
| ve.push_back(num); |
| } |
| } |
| } |
| |
| double findMedian() { |
| double res = 0; |
| if (ve.size() == 0) { |
| return res; |
| } else if (ve.size() % 2 == 0) { |
| int i = ve.size() / 2 - 1; |
| int j = i + 1; |
| res = ve[i] + ve[j]; |
| res = res / 2; |
| return res; |
| }else { |
| int i = ve.size() / 2; |
| res = ve[i]; |
| return res; |
| } |
| } |
| |
| private: |
| vector<int> ve; |
| }; |
| |
| |
| * Your MedianFinder object will be instantiated and called as such: |
| * MedianFinder* obj = new MedianFinder(); |
| * obj->addNum(num); |
| * double param_2 = obj->findMedian(); |
| */ |
![1692262038801]()
![1692262070556]()
![1692262079245]()
| class MedianFinder { |
| public: |
| priority_queue<int, vector<int>, greater<int>> A; |
| priority_queue<int, vector<int>, less<int>> B; |
| MedianFinder() { } |
| void addNum(int num) { |
| if(A.size() != B.size()) { |
| A.push(num); |
| B.push(A.top()); |
| A.pop(); |
| } else { |
| B.push(num); |
| A.push(B.top()); |
| B.pop(); |
| } |
| } |
| double findMedian() { |
| return A.size() != B.size() ? A.top() : (A.top() + B.top()) / 2.0; |
| } |
| }; |
第一次 addNum 的时候,A.size () 和 B.size () 相等,所以最终 A 入队,然后不等,B 入队,导致又相等,如此循环,要么 A.size () 比 B.size () 多 1,要么相等。
- 怎么保证小顶堆和大顶堆分别是对半排序、且组成的正好是全部元素,而不会重复或其他错误呢?
- 归纳法,假设小顶堆 A 和大顶堆 B 的 size 相同且已经排好序,并不是将新元素 num 直接压入 A 中,而是将 num 先压入 B 中排序好后将 B 堆顶元素(最大值,必定属于 A)压入 A 中并出队。
# 剑指 Offer 40. 最小的 k 个数
![1692262234303]()
| class Solution { |
| public: |
| vector<int> getLeastNumbers(vector<int>& arr, int k) { |
| if (k >= arr.size()) return arr; |
| return quickSort(arr, k, 0, arr.size() - 1); |
| } |
| private: |
| vector<int> quickSort(vector<int>& arr, int k, int l, int r) { |
| int i = l, j = r; |
| while (i < j) { |
| while (i < j && arr[j] >= arr[l]) j--; |
| while (i < j && arr[i] <= arr[l]) i++; |
| swap(arr[i], arr[j]); |
| } |
| swap(arr[i], arr[l]); |
| if (i > k) return quickSort(arr, k, l, i - 1); |
| if (i < k) return quickSort(arr, k, i + 1, r); |
| vector<int> res; |
| res.assign(arr.begin(), arr.begin() + k); |
| return res; |
| } |
| }; |
注意上面 vector 的 assign 函数。