常用算法代码模板 (1) :基础算法

常用算法模板——基础算法:排序、二分、高精度、前缀和与差分、位运算、双指针、离散化、区间合并


常用算法代码模板 (1) :基础算法
常用算法代码模板 (2) :数据结构
常用算法代码模板 (3) :搜索与图论
常用算法代码模板 (4) :数学知识
算法选择——由数据范围反推算法时间复杂度



1 排序

std::sort(begin, end, cmp)

1.1 直接插入排序

int n;
int q[N];	// q[0 ... n-1]

void insert_sort() {
   
	for (int i = 1; i < n; i++)
		for (int j = i; j >= 1 && q[j] > q[j - 1]; j--)
			swap(q[j], q[j - 1]);
}

1.2 快速排序

  1. 确定枢轴:通常从 q[l]q[l + r >> 1]q[r]之中任选一个
  2. 划分子区间:双指针 ij初始位于待排区间两侧外,先 ij相向而行,最终使得左右子区间 q[l ... j]q[j+1 ... r]左小右大
  3. 递归排序左右子区间(该写法左子区间右端点必须为 j

快速排序

int q[N];	// q[l ... r]

void quick_sort(int l, int r) {
   
    if (l >= r) return;	// 只剩一个数或没有数了则不排序
  
    int x = q[l + r >> 1];		// 枢轴(可选 q[l]、q[l + r >> 1]、q[r])
    int i = l - 1, j = r + 1;	// 双指针初始位于两侧外(追加1偏移量)
    while (i < j) {
   	// 进行一轮划分操作
        do i++; while (q[i] < x);
        do j--; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
  
    quick_sort(l, j), quick_sort(j + 1, r);		// 左子区间右端点必须为j
}

1.3 归并排序

  1. 确定分界点:mid = l + r >> 1
  2. 递归排序左右子区间
  3. 归并左右子区间为有序子区间:挑出两者较小值,相等则优先归并 q[i],使得排序稳定

二路归并排序

int q[N];	// q[l ... r]
int tmp[N];	// 辅助数组tmp临时存放新区间

void merge_sort(int l, int r) {
   
    if (l >= r) return;		// 只剩一个数或没有数了则不排序
  
    int mid = l + r >> 1;	// 确认分界点:左[l, mid]、右[mid + 1, r]
    merge_sort(l, mid), merge_sort(mid + 1, r);
  
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)		// 归并左右子区间为有序子区间:挑出两者较小值
        if (q[i] <= q[j]) tmp[k++] = q[i++];	// 相等则优先归并q[i],否则排序不稳定
    	else tmp[k++] = q[j++];
    while (i <= mid) tmp[k++] = q[i++];		// 并入区间剩余元素
    while (j <= r) tmp[k++] = q[j++];
  
    for (i = l, j = 0; i <= r; ++i, ++j)	// 将tmp[0 ... r-l+1]复制给q[l ... r]
        q[i] = tmp[j];
}

2 二分

2.1 整数二分

AcWing 789. 数的范围

  1. 中点将区间划分出左右两子区间
  2. 判断中间点是否满足某侧区间的性质 check(mid),查找x边界,目标在x区间,检测x区间性质。易知该种写法条件检测始终为"≥"或"≤",对应下文check_ge()(greater_equal)、check_le()(less_equal),对比目标和中点的位置关系即可得出条件检测函数。
  3. 返回所检测的x区间的端点x

当查找右边界时中点应为l + r + 1 >> 1,简记:有(“右”) 加必有(“右”) 减

/* 查找左边界,即第一个满足条件的元素下标 (lower_bound) */
int bsearch_l(int l, int r) {
   
    while (l < r) {
   
        int mid = l + r >> 1;
        if (check_ge(mid, target)) r = mid;	// 目标在左,mid所指>=目标:带mid去左边[l, mid]
        else l = mid + 1;					// 否则去右边 [mid + 1, r]
    }
    return l;
}

/* 查找右边界,即最后一个满足条件的元素下标 (upper_bound的前驱) */
int bsearch_r(int l, int r) {
   
    while (l < r) {
   
        int mid = l + r + 1 >> 1;			// 有(“右”)加必有(“右”)减
        if (check_le(mid, target)) l = mid;	// 目标在右,mid所指<=目标:带mid去右边[mid, r]
        else r = mid - 1;					// 否则去左边: [l, mid - 1]
    }
    return r;
}

2.2 浮点数二分

类似整数二分的查找左边界,常写作f(mid) >= target的形式。解唯一,无需处理边界。要注意浮点精度问题。

int bsearch_f(double l, double r) {
   
    const double eps = 1e-8;		// 精度,视题目而定
    while (r - l > eps) {
   
        double mid = (l + r) / 2;
        if (check_ge(mid, target)) r = mid;	// 目标在左,mid所指>=目标。注意浮点关系运算精度问题
        else l = mid;						// 边界均无需+1或-1
    }
    return l;
}

3 高精度运算

使用变长数组vector<int>存储大整数及其属性,低位存于低位。亦可自定义结构体实现。

3.1 高精度加法

/* C = A + B, A >= 0, B >= 0 */
vector<int> add(vector<int> &A, vector<int> &B) {
   
    if (A.size() < B.size()) return add(B, A);
  
    vector<int> C;
    int t = 0;	// 进位
    for (int i = 0; i < A.size() i++) {
   
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }
  
    if (t) C.push_back(t);	// 存入最后的进位
    return C;
}

3.2 高精度减法

/* 比较两个高精度整数的大小,返回A - B的符号 */
int cmp(vector<int> &A, vector<int> &B) {
   
    if (A.size() > B.size()) return 1;	// 优先比较长度
    else if (A.size() < B.size()) return -1;
  
    for (int i = A.size() - 1; i >= 0; i--)	// 从高位起逐位比较
        if (A[i] > B[i]) return 1;
    	else if (A[i] < B[i]) return -1;
  
    return 0;
}

/* C = A - B, A >= B, A >= 0, B >= 0 */
vector<int> sub(vector<int> &A, vector<int> &B) {
   
    vector<int> C;
    int t = 0;	// 借位
    for (int i = 0; i < A.size(); i++) {
   
        t = A[i] - t;	// 成为本轮的被减数
        if (i < B.size()) t -= B[i];	// 先直接相减,t<0则说明需借位
        C.push_back((t + 10) % 10);		// 若t<0,则存的是借位后的差;否则正常存差
        if (t < 0) t 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Akira37

💰unneeded

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值