排序及相关知识点
快速排序
1、用途
排序
2、原理
选取区间中某个数(midin a[l:j])为基准,将整个序列划分成左右两个部分(S_1)和(S_2),使得(forall a[i]in S_1,a[i]le mid;forall a[j]in S_2,a[j]ge mid)。之后递归地处理左右两个子区间直至序列有序。
3、复杂度
期望(O(nlogn));最坏情况时每次选择的基准元素均不能将序列划分为长度均匀的两部分(如每次选择序列的最大值),此时复杂度为(O(n^2))。
4、模板
void qsort(int a[], int l, int r)
{
if (l == r) return;
int i = l - 1, j = r + 1, mid = a[l + r >> 1];
while (i < j)
{
do ++i; while (a[i] < mid);
do --j; while (a[j] > mid);
if (i < j) swap(a[i], a[j]);
}
qsort(a, l, j); qsort(a, j + 1, r);
}
5、备注
①基准元素并不一定要选择区间中点,选择左端点、右端点均可以。
②为避免快排达到最坏复杂度,有时在左、右和中点三者中取中间元素。
③库函数sort()很快。
④边界情况非常多,建议背模板。
快速选择
1、用途
查询区间第(k)小的元素
2、原理
基于快速排序,将区间划分成两个具有相对大小关系的子区间后询问左区间的长度。若长度大于等于(k)则说明要查询的元素一定在左区间内,否则就在右区间内。之后递归询问直至找到该元素。
3、复杂度
由于比快速排序可以少一边的递归处理,复杂度为(O(n))
4、模板
int qfind(int a[], int l, int r, int k)
{
if (l == r) return a[l];
int i = l - 1, j = r + 1, mid = a[l + r >> 1];
while (i < j)
{
do ++i; while (a[i] < mid);
do --j; while (a[j] > mid);
if (i < j) swap(a[i], a[j]);
}
int len = j - l + 1;
if (len >= k) return qfind(a, l, j, k);
else return qfind(a, j + 1, r, k - len);
}
5、备注
①边界情况非常多,建议背模板。
②可以直接使用库函数nth_element()。
归并排序
1、用途
排序
2、原理
选取区间中点(mid),将区间划分成左右两段,并递归地保证左右区间内的元素单调不减。于是区间最小值仅可能在两段区间的左端点取到。利用双指针(i=l,j=mid+1),每次从(a[i],a[j])中取较小的那一个存入备份数组中,直至两个指针将数组元素全部遍历一遍。此时备份数组已保证有序,将原数组覆盖即可。
3、复杂度
(O(nlogn))
4、模板
int temp[];
void merge_sort(int a[], int l, int r)
{
if (l == r) return;
int mid = l + r >> 1;
merge_sort(a, l, mid); merge_sort(a, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
{
if (a[i] <= a[j]) temp[++k] = a[i++];
else temp[++k] = a[j++];
}
while (i <= mid) temp[++k] = a[i++];
while (j <= r) temp[++k] = a[j++];
for (int i = l, j = 1; i <= r; ++i, ++j) a[i] = temp[j];
return;
}
逆序对
1、用途
求区间逆序对对数
2、原理
基于归并排序。将逆序对划((a[i],a[j]))分为三类:(1)(i<jle mid);(2)(mid<i<j);(3)(ile mid < j)。前两段可以在递归过程中求出,那么当前只需要考虑第三种情况。在归并两段区间时,若发现(a[i]>a[j]),则此时(forall i<kle mid,a[k]ge a[i]>a[j])。所以以(a[j])为结尾的第三类逆序对总对数为(mid-i+1)。对于所有(j),将答案全部累加即可。
3、复杂度
(O(nlogn))
4、模板
int temp[];
ll revPair(int a[], int l, int r)
{
if (l == r) return 0;
int mid = l + r >> 1;
ll ans = revPair(a, l, mid) + revPair(a, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
{
if (a[i] <= a[j]) temp[++k] = a[i++];
else temp[++k] = a[j++], ans += mid - i + 1;
}
while (i <= mid) temp[++k] = a[i++];
while (j <= r) temp[++k] = a[j++];
for (int i = l, j = 1; i <= r; ++i, ++j) a[i] = temp[j];
return ans;
}
*基数排序
1、用途
排序
2、原理
按照数位大小逐位比较。从最低位开始,将所有数分散到若干个桶中,之后按照桶的编号顺序将所有元素取出覆盖原数组。
3、复杂度
(O(nlog_k^{max{a[i]}}))
4、模板
void rsort(int a[], int n)//index = 2
{
int dig = 0;
for (int i = 1; i <= n; ++i)
{
int temp = a[i], cnt = 0;
while (temp) ++cnt, temp >>= 1;
dig = max(dig, cnt);
}
for (int k = 0; k < dig; ++k)
{
vector <int> buk[2];
for (int i = 1; i <= n; ++i)
buk[(a[i] >> k) & 1].push_back(a[i]);
int now = 0;
for (auto &i : buk[0]) a[++now] = i;
for (auto &i : buk[1]) a[++now] = i;
}
}
5、备注
①桶排序的一种。
*计数排序
1、用途
排序
2、原理
在基数排序中将(k o +infin)。将每个元素放入该元素对应的桶中,最后按桶的编号从小到大将元素取出。
3、复杂度
(O(n))
4、模板
需要用时自己手写即可。
5、备注
①桶排序的一种。
例题
暂空