1、快速排序
选取一个数 x,使得x左边小于x,右边大于x,
将一条线段分为多段, 一段一段的排序, 递归实现。
为什么x取中间?
x可以取任何数,但此处为方便取 l + r >> 1。
void quick_sort(int q[],int l, int r){
if(l >= r) return;
int i = l, int j = r + 1, x = q[l + r >> 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(q, l, j),quick_sort(q, j + 1, r); //继续递归左右两段序列
}
2、归并排序
1、取一个位置mid,在此断开
2、在断开位置,逐个比较序列大小,小了 i + +, 大了 j + +,直到某一个序列遍历结束
3、将两个序列合并为一个序列
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 + 1, j = r;
//排序
while(i <= mid && j >= r){
if(q[i] <= q[j]) int temp[k ++] = q[i ++];
else temp[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 ++ ) q[i] = tmp[j]; //合并为一个序列
}
3、二分查找
前提:有序序列
1、需要一个“check”来判断mid是否符合某条件
2、分开区间, 可分为[l, mid],[mid+1,r] 或 [l, mid -1],[mid , r]
3、大于mid就在右边查找,小于mid就在左边查找
1) 整数二分
int binary_seacrh(int l, int r){
while(l < r)
{
int mid = l + r >> 1;
if(...) r = mid;
else l = mid + 1;
}
return l;
}
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (...) l = mid;
else r = mid - 1;
}
return l;
}
2) 浮点数二分
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
4、高精度算法
1、思想是模拟竖式算法
2、注意一点要倒序存储各个字符串!!
3、前导0处理!!
1)、高精度加法
1、规定正数
2、处理好进位t,C存放t的个位,进位取t的十位
3、存在计算完,t还有剩余的情况,将多的t放到C中
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);//十位储存到C中
t /= 10;//进位取个位
}
if(t) C.push_back(t);
return C;
}
2)、高精度减法
vector<int> sub(vector<int> &A, vector<int>)
{
vector<int> C;
for(int i = 0, t = 0; i < A.size(); i ++){
t = A[i] - t;
if(i < B.size()) t-=B[i];
C.push_back((t + 10) % 10);
if(t < 0) t = 1;
else t = 0;
}
while(C.size() > 1 && C.back() == 0) C.pop_back();//处理前导0
return C;
}
3)、高精度乘法(只有一个是高精度数)
vector<int> mul(vector<int> &A, int b){
vector<int> C;
int t = 0;
for(int i = 0 ; i < A.size()|| t; i ++){
if(i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
while(C.size() > 1 && C.back()==0) C.pop_back;
return C;
}
4)、高精度除法
除法需要注意保存余数
算完需要将整个C reverse,处理前导0
vector<int> div(vector<int> &A, int b, int &r){
vector<int> C;
r = 0;
for(int i = A.size()-1; i >= 0 ; i --){
r = r * 10 +A[i];
C.push_back(r/b);
r %= b;
}
reverse(C.begin(), C.end());
while(C.size>1&&C.back()==0) C.pop_back();
return C;
}
5、前缀和
前缀和就是计算1~i的和
此算法有利于计算区间和
1)一维版
S[i] = a[1] + a[2]+...+a[i]
a[l]+...+a[r] = S[r] - S[l - 1]//区间和
2)二维
可以想象成计算矩阵某阴影面积,利用大减小,最后加上重复减去的面积
(x1,y1)左上角,(x2,y2)右下角
子矩阵的和: S[x2 , y2] - S[x1 - 1 , y2] -S[x2, y1 - 1] + S[x1 - 1, y1 -1]
6、差分
差分是前缀和的逆推
1)一维差分
初始状态下,a[]全为0,向
给[l,r]中每个数加上c: B[l] += c, B[r + 1] -= c
2)二维差分
给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c
7、位运算
求n的第k位数字: n >> k & 1
返回n的最后一位1:lowbit(n) = n & -n
8、离散化
是一种特殊的哈希表
1、存在重复元素需要去重
2、关键在于,如何算出x,离散化后的值(二分查找)
3、需要保序,单调的
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
int find(int x) // 找到第一个大于等于x的位置
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1; //从0开始就r,从1开始就r+1
}
9、区间合并
利用pair来将绑定左右端点
1、 按区间左端点排序
2、维护区间[st,ed]
3、first存放左端点,second存放右端点
void merge(vector<PII> &segs)
{
vector<PII> res;//存放合并后的结果
sort(segs.begin(), segs.end());//先将区间排序,pair优先以左端点排序
int st = -2e9, ed = -2e9;//开始区间是负无穷到正无穷
for (auto seg : segs)
if (ed < seg.first)//维护区间,严格在枚举区间的左边,无交集
{
if (st != -2e9) res.push_back({st, ed});
st = seg.first, ed = seg.second;
}
else ed = max(ed, seg.second);//有交集,求并集,右端点更新值最大值
if (st != -2e9) res.push_back({st, ed});//防止数组为空
segs = res;
}