算法笔记之基础算法(模板)

基础算法

排序

快速排序(可以是稳定的)

思路

左端为 l ,右端为 r

  • 首先确定分界点:q[l] q[r] q[(l+r)/2]都行 (用(l+r)/2 更快一点)
  • 然后调整区间:使左部分满足都是≤分界点的,右部分都是≥分界点的
  • 最后递归处理左右两端
模板代码
void quick_sort(int q[], int l, int r)
{
	if (l >= r)
		return;								//不满足条件记得return;
	int x = q[(l+r)/2];
	int i = l - 1, j = 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);					//注意这里右边界如果是j, 上面的x不能是r或者(l+r+1)/2;否则会死循环
	quick_sort(q, j + 1, r);
}

归并排序(排序是稳定的)

思路
  • 首先确定分界点 mid = (l+r)/2
  • 然后递归排序
  • 最后归并(两个有序的序列合二为一)
代码模板
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; int i = l, j = mid+1;
	while (i <= mid && j <= r)
	{
		if (a[i] <= a[j])
			tmp[++k] = a[i++];
		else
			tmp[++k] = a[j++];
	}
	while (i <= mid)
	{
		tmp[++k] = a[i++];
	}
	while (j <= r)
	{
		tmp[++k] = a[j++];
	}//每次要更新a数组
	for (i = l, j = 1; i <= r, j <= k; i++, j++)
	{
		a[i] = tmp[j];
	}
}

二分(核心是边界问题)

整数二分

1.要找第一个满足的
int bseach_1(int l,int r)
{
	while(l<r)
    {
		int mid = l+r>>1;		//下取整
        if(check(mid))
            r=mid;
        else
            l=mid+1;
    }
    return l;
}
2.找最后一个满足的
int bseach_2(int l,int r)
{
	while(l<r)
    {
        int mid = l+r+1>>1;		//注意要上取整
        if(check(mid))
            l=mid;
        else
            r=mid-1;
	}
}

浮点数二分

不需要考虑边界+1问题

//表示方式一:
for(int i = 1;i<=100;i++)
{
	double mid = (l + r) / 2;
	if (mid * mid * mid >= n)
		r = mid;
	else
		l = mid;
}

//表示方式二:
while(r-l>1e-8)			//要比题目的精度再精确两位
{
	double mid = (l + r) / 2;
	if (mid * mid * mid >= n)
		r = mid;
	else
		l = mid;
}

高精度

  • 输入用string数组来表示
  • 倒置变为数字存入vector数组
  • 结果存入vector< int >数组

高精度加法

vector<int> add(vector<int>& a, vector<int>& b)
{
	vector<int> C;
	int t = 0;			//t表示进位
	for (int i = 0; i < a.size() || i < b.size(); i++)
	{
		if (i < a.size())
			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;
}

高精度减法

//高精度减法核心函数
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];			
		C.push_back((t + 10) % 10);	//t此时就是该位上的最终结果
      								//注意+10 %10 的技巧
		if (t < 0)
			t = 1;
		else
			t = 0;
	}
	while (C.size() > 1 && C.back()==0)		//去掉前导零
	{
		C.pop_back();
	}
	return C;
}


//判断A,B大小
bool cmp(vector<int>& a, vector<int>& b)
{
	if (a.size() != b.size())
		return a.size() > b.size();
	for (int i = a.size() - 1; i >= 0; i--)
		if (a[i] != b[i])
			return a[i] > b[i];
	return true;
}

高精度乘法

  • 第i位和第j位相乘,结果在第i+j-1位
//关键步骤
//ans[0]存的是s1+s2的位数,所以一定大于等于最终结果的位数,需要去除前导零
for (int i = 1; i <= len1; i++)
{
	for (int j = 1; j <= len2; j++)
	{
		ans[i + j - 1] += a[i] * b[j];
		ans[i + j] += ans[i + j - 1] / 10;
		ans[i + j - 1] %= 10;
	}
}
//注意特判
    if(s1=="0"||s2=="0")
    {
        cout<<0<<"\n";
        return 0;
    }


前缀和

  • 一次运算算出前缀的和
一维
//求前缀和
s[i] = s[i-1]+a[i];
//求部分和
S = s[r]-s[l-1];
二维
//求前缀和
 s[i][j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];

//求部分和
S = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];

差分

  • 简单的说就是,对差分的前缀和就是原数组
  • 用O(1)的时间给连续区间加上固定的值
一维差分
//求差分(初始化)
b[i] = a[i]-a[i-1];
//或者
b[i]+=a[i];
b[i+1]-=a[i];

//应用
// l,r,c 代表在a数组中l到r区间每个元素增加c
b[l]+=c;
b[r+1]-=c;
  • 为了方便插入修改,可以写一个函数
void insert(int l,int r,int c)
{
	b[l]+=c;
    b[r+1]-=c;
}
//初始化即
insert(i,i,a[i]);
//求前缀和
b[i]+=b[i-1];
二维差分
void insert(int x1,int y1,int x2,int y2,int c)
{
	b[x1][y1]+=c;
	b[x2+1][y1]-=c;
	b[x1][y2+1]-=c;
	b[x2+1][y2+1]+=c;
}	
//初始化即
insert(i,j,i,j,a[i][j]);
//求前缀和
b[i][j] += b[i-1][j] + b[i][j-1] - b[i-1][j-1];

双指针

  • 核心思想:把O(N*N)的算法优化到O(N);

代码框架

//只是个大体框架,不一定写的一模一样
for(i = 0,j = 0;i<n;i++)
{
    while(j<i && check())
    {
        j++;
    }
    //每道题目的具体逻辑
}

位运算

运算符

& 与运算(有一个为0,即为0)
0 & 0 = 0;
1 & 0 = 0; 
0 & 1 = 0;
1 & 1 = 1;
| 或运算(有一个为1,即为1)
0 | 0 = 0;
0 | 1 = 1;
1 | 0 = 1;
1 | 1 = 1;
^ 异或(两个数不同,就为1,否则是0)
0 ^ 0 = 0;
1 ^ 1 = 0;
1 ^ 0 = 1;
0 ^ 1 = 1;

常用操作

看n用二进制表示,第k位数字是几(从个位往上数到k)

n >> k & 1 即,先右移k位再取出此时的个位

lowbit(x):返回x的最后一位1
  • 注意:lowbit(x) 中 x 是十进制数,即该函数自动把十进制转化成二进制来运算,并返回其对应的十进制数
  • 运算规则:
若 x 的二进制为 1010,     lowbit(x) 所对应的二进制为 10;
若 x 的二进制为 101000,   lowbit(x) 所对应的二进制为 1000;
  • 内部实现
//x & -x 或者 x & (~x+1);
int lowbit(int x)
{
	return x & -x;
}
  • 可以求一个数的二进制中 1 的个数
while(x)
{
    x -= lowbit(x);
    res++;		//记录二进制中1 的个数
}

离散化

  • 特指整数的离散化

概念:

在值域很大,但是元素较少的情况下,有时我们需要用到元素的值作为下标来运算,显然,由于值域很大,开一个数组并用元素值作为下标并不显示,所以我们考虑对其元素值进行映射,映射成一组连续的数值作为下标,即离散化的过程。

实现:

  • 想办法把要离散化的点用数组 alls 存起来(目的是为了后续去重后得到要进行离散化的数组大小,进而方便求前缀和),
  • 同时用适当的数据结构(such as pair)来存储题目中进行的操作(目的是为了记录 后期需要离散化的对象,进而对其离散化)
vector<int> alls;			//a表示待离散化数组
  • 然后对a数组排序、去重
sort(alls.begin(),alls.end());//排序
alls.erase(unique(alls.begin(),alls.end()),alls.end()); //删除重复元素
  • 再用二分来求需要离散化对象的映射
//即用二分来求出x对应的离散化的值
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;  //映射到1,2,3……n
}
//如何调用?(例如):
int x = find(items.first);	//x表示离散化后的坐标
a[x] += c;					//a数组表示对离散化后的坐标进行操作
  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值