算法学习-L1
算法-L1
排序
假设有无序数组Q[N],现需将其升序排序。
排序的稳定性
稳定: 对于原始数组有值a,pos(a1) < pos(a2),排序后必然且仍然有 pos(a1) < pos(a2)。
快速排序(不稳定)
思想
- 选取在数组中选取一个值x,作为基准值;
x 可以选取数组中任意一个数,常选最左值、最右值或中间值。x 最终的位置也是不确定的。 - 处理数组,使得其分为左右两部分,使得其中:
- 左段所有元素 ai满足 ai<=x;
- 右段所有元素 bi满足 bi>=x;
- 递归处理左段和右段。
实现
实现 1(简易):扫描
Q[N]通过将对应值存入左右段临时数组A[N]B[N],然后再存回Q[N]。通过A中放入元素数量为依据对Q切割递归。
点击查看代码
void quick_sort(int q[], int l, int r) {
if (l >= r) return; // 边界判断
int x = q[l+r>>1]; // 选择调整值x
int A[r - l + 1], B[r - l + 1]; // 临时数组,A左段,B右段
int a = 0, b = 0; // A,B指针
for(int i = l; i <= r; i++) {
if(q[i] <= x) A[a++] = q[i];
else B[b++] = q[i];
}
// 合并A,B数组至原数组
for(int i=0; i < a; i++) q[i] = A[i];
for(int i=0; i < b; i++) q[a + i] = B[i];
// 递归处理<=x段和>x段
quick_sort(q, l, a);
quick_sort(q, a + 1, r);
}
实现 2(推荐):双指针两端扫描,遇到不符合要求的数据暂停,然后交换数据。当扫描一遍后分为 A, B 段,然后进行递归。
点击查看代码
void quick_sort(int q[], int l, int r) {
if (l >= r) return; // 边界判断
int x = q[l+r>>1], i = l - 1, j = r + 1; // 选择调整值x,初始化i,j为左右哨兵
while(i < j) { // 当q[i]>=x && q[j] <=x时,交换q[i],q[j]
do ++i; while (q[i] < x); // i自增,直至x<=q[i]
do --j; while (x < q[j]); // j自减,直至q[j]<=x
if (i < j) swap(q[i], q[j]); // 如果此时i,j未交错(即未完全扫描),交换q[i],q[j]
// 单次交换后:q[l,i-1]此区间内所有值<=x,q[j+1,r]此区间内所有值>=x
} // 扫描结束后 q[l,j]<=x q[j+1,r]>=x
// 递归处理<=x的A段和>=x的B段
quick_sort(q, l, j);
quick_sort(q, j+1, r);
}
归并排序(有序)
思想
- 将数组
Q从中间位置一分为二,划分出LR两段; - 递归排序
LR两段; - 将排序好的
LR两段,归并放回原数组Q中。
实现
点击查看代码
void merge_sort(int q[], int l, int r) {
if (l >= r) return; // 递归边界
int mid = (l + r) >> 1; // 取中间递归分段点
merge_sort(q, l ,mid), merge_sort(q, mid + 1, r); // 递归排序左段,右段
// 将排序后的左右两段归并
int k=0, i = l, j = mid + 1; // k为已排序数(临时数组指针), i为左段指针,j为右段指针
while(i <= mid && j <= r) { // 当两段均未完全放入临时数组时
if(q[i] <= q[j]) tmp[k++] = q[i++]; // 将q[i],q[j]中较小者放入tmp[k],并后移对应指针
else tmp[k++] = q[j++];
}
// 一段已全部放入临时数组,将另一段接续放入临时数组
while(i <= mid) tmp[k++] = q[i++];
while(j <= r) tmp[k++] = q[j++];
// 将排序好的数组重新放入q中
for(i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
STL 之 sort(),stable_sort()
STL! STL! STL!
二分查找
任务:对已经有序的数组Q[N],Q[0, X],Q[X+1, N-1]或Q[0, X-1],Q[X, N-1]为性质不同的两个部分,查找这个位置X。
有单调性一定可以二分,二分不一定需要单调性
整体思想
性质 A:Q[0, X], 不符合性质 A:Q[X+1, N-1]
- 初始化左右边界及中间位置,
l = 0, r = N-1,mid; - 是否满足
l < r,满足继续,不满足退出; - 判断
Q[mid]是否符合性质A;- 如果符合,
0<=mid<=X,GOTO 2 - 如果不符,
X+1<=mid<=N-1,GOTO 3
- 如果符合,
- 调整左边界
l = mid,右边界不动,更新mid值,GOTO 1 - 调整右边界
r = mid-1,左边界不动,更新mid值,GOTO 1
整数二分
模板!模板!模板!
点击查看代码
// 查找右边界点,[l, mid - 1], [mid, r]
int binary_search1(int q[], int l, int r, int x) {
while(l < r) {
int mid = (l + r) >> 1;
if(x <= q[mid]) r = mid;
else l = mid + 1;
}
if(q[l] == x) return l;
else return -1;
}
// 查找左边界点,[l, mid], [mid + 1, r]
int binary_search2(int q[], int l, int r, int x) {
while(l < r) {
int mid = (l + r + 1) >> 1; // +1防止死递归
if(q[mid] <= x) l = mid;
else r = mid - 1;
}
if(q[l] == x) return l;
else return -1;
}
浮点二分
相较于整数二分,浮点二分不需要考虑边界问题.
点击查看代码
// 精度控制
const double PRECISION = 1e-8; // 经验值:精度调整为 1e-(要求有效位数+2)
while((r - l) > PRECISION) { // 未达到精度要求,继续查找
double mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid;
}
// 循环次数控制
const int N = 100
for(int i=0;i<N;i++) { // 无脑二分100次
double mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid;
}
STL 之二分查找
STL! STL! STL!
cppreference binary_search()
cppreference lower_bound()
cppreference upper_bound()
cppreference equal_range()
本文来自博客园,作者:全无青年,转载请注明原文链接:https://www.cnblogs.com/qwqn/p/learning-algorithm-L1.html

浙公网安备 33010602011771号