voidmerge_sort(vector<int>& a, int l, int r){ if (r - l < 1){ return; } // 分治 int mid = l + ((r - l) >> 1); merge_sort(a, l, mid), merge_sort(a, mid + 1, r); // 合并 merge(a, l, mid, r); }
voidmerge(vector<int>& a, int l, int mid, int r){ vector<int> tmp; int i = l, j = mid + 1; while (i <= mid && j <= r) { if (a[j] < a[i]) { tmp.push_back(a[j]); j++; } else{ tmp.push_back(a[i]); i++; } }
// 此时一个数组已空,另一个数组非空,将非空的数组并入 tmp 中 for (; i <= mid; i++) tmp.push_back(a[i]); for (; j <= r; j++) tmp.push_back(a[j]);
// 将存储在临时数组中的结果拷贝回原数组 for(int k = 0; k < tmp.size(); k++) a[k + l] = tmp[k]; return; }
voidmerge(vector<int>& a, int l, int mid, int r, int& cnt){ // cnt 记录逆序对的个数 vector<int> tmp; int i = l, j = mid + 1; while (i <= mid && j <= r) { if (a[j] < a[i]) { tmp.push_back(a[j]); cnt += mid - i + 1; // a[i~mid] 都可以和 a[j] 构成逆序对 j++; } else{ tmp.push_back(a[i]); i++; } }
for (; i <= mid; i++) tmp.push_back(a[i]); for (; j <= r; j++) tmp.push_back(a[j]);
for(int k = 0; k < tmp.size(); k++) a[k + l] = tmp[k]; return; }
快速排序
快速排序,又称分区交换排序,简称「快排」,是一种被广泛运用的排序算法。
工作原理包括三个过程:
将数列划分为两部分(要求保证相对大小关系)。
递归到两个子序列中分别进行快速排序。
不用合并,因为此时数列已经完全有序。
快速排序的最优时间复杂度和平均时间复杂度为 O(nlogn),最坏时间复杂度为 O(n2)。
第一步并不是直接分成前后两个序列,而是在分的过程中要保证相对大小关系。具体来说,第一步要是要把数列分成两个部分,然后保证前一个子数列中的数都小于后一个子数列中的数。为了保证平均时间复杂度,一般是随机选择序列中一个数 m 来当做两个子数列的分界。之后,维护一前一后两个指针 p 和 q,依次考虑当前的数是否放在了应该放的位置(前一个子序列还是后一个子序列)。如果当前的数没放对,比如说如果后面的指针 q 遇到了一个比 m 小的数,那么可以交换 p 和 q 位置上的数,再把 p 向后移一位。当前的数的位置全放对后,再移动指针继续处理,直到两个指针相遇。
intfind_kth_largest(vector<int>& a, int k){ int len = a.size(); int l = 0, r = len - 1; int tmp; while(1){ tmp = partition(a, l, r); if(tmp == len - k) break; elseif(tmp < len - k){ l = tmp + 1; } else{ r = tmp - 1; } } return a[tmp]; }
intpartition(vector<int>& nums, int l, int r){ // 快速选择 int p = rand() % (r - l + 1) + l; // 随机选择一个作为分界 swap(nums[p], nums[r]); int q = l - 1; for(int i = l; i < r; i++){ if(nums[i] < nums[r]){ swap(nums[++q], nums[i]); } } swap(nums[++q], nums[r]); return q; }
分类
稳定性
稳定性是指相等的元素经过排序之后相对顺序是否发生了改变。
拥有稳定性这一特性的算法会让原本有相等键值的元素维持相对次序,即如果一个排序算法是稳定的,当有两个相等键值的元素 R 和 S,且在原本的序列中 R 出现在 S 之前,在排序过的序列中 R 也将会是在 S 之前。