算法学习-L1

算法-L1

排序

假设有无序数组Q[N],现需将其升序排序。

排序的稳定性

稳定: 对于原始数组有值a,pos(a1) < pos(a2),排序后必然且仍然有 pos(a1) < pos(a2)。

快速排序(不稳定)

思想

  1. 选取在数组中选取一个值x,作为基准;
    x 可以选取数组中任意一个数,常选最左值、最右值或中间值。x 最终的位置也是不确定的。
  2. 处理数组,使得其分为左右两部分,使得其中:
    • 左段所有元素 ai满足 ai<=x;
    • 右段所有元素 bi满足 bi>=x;
  3. 递归处理左段和右段。

实现

实现 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);
}

归并排序(有序)

思想

  1. 将数组Q中间位置一分为二,划分出L R两段;
  2. 递归排序L R两段;
  3. 将排序好的L R两段,归并放回原数组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!

cppreference sort()
cppreference stable_sort()

二分查找

任务:对已经有序的数组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]

  1. 初始化左右边界及中间位置,l = 0, r = N-1mid
  2. 是否满足l < r,满足继续,不满足退出;
  3. 判断Q[mid]是否符合性质A
    • 如果符合,0<=mid<=XGOTO 2
    • 如果不符,X+1<=mid<=N-1GOTO 3
  4. 调整左边界l = mid,右边界不动,更新mid值,GOTO 1
  5. 调整右边界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()

posted @ 2022-03-30 15:31  全无青年  阅读(61)  评论(0)    收藏  举报