基础算法思想整理

一、排序

1.快排:

算法思想:利用双指针,设置界定值,从界定值左边寻找比界定值大的数,在右边寻找比界定值小的数,找到后两者进行交换,即可完成一次排序。完成一次排序后只能保证界定值左右均比界定值大或小,但是左右仍然为乱序,需将左右两边进行递归快排,从而达到排序目的

代码模板:

void  quick_sort(int q[], int l, int r)
{
	if(l > r)
		return ;
	//step1:选取界定值
	int mid = q[(l + r) / 2];
	//step2:进行第一次快排
	int i = l - 1;
	int 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]);
	}
	//step3:对两边进行递归快排
	quick_sort(q, l, i);
	quick_sort(q, j + 1, r);
}

完整模板:Acwing.785.快速排序

2.归并排序

算法思想:归并即对界定值两边分别进行排序后,合并为一个有序数组
应用方面:求逆序对

代码模板:

void merge_sort(int q[], int l, int r)
{
	if(l >= r)
		return ;
	//step1:确定分界点
	int mid = (l + r) / 2;
	//step2:对分界点两边进行归并排序
	merge_sort(q, l, mid);
	merge_sort(q, mid + 1, r);
	//step3:合并排序后的数组
	int temp[], k;
	int i = l, j = mid + 1;
	while(i <= mid && j <= r)
	{
		if(q[i] < q[j])		temp[k ++] = q[i ++];
		else				temp[k ++] = q[j ++]; 
	}
	//处理合并之后剩余未合并的
	while(i <= mid)			temp[k ++] = q[i ++];
	while(j <= r)			temp[k ++] = q[j ++];
	
	//将临时数组赋值给q
	for(int i = l, j = 0; i <= r; i ++ , j ++ )
		q[i] = temp[j];
}

完整模板:Acwing.787.归并排序

二、二分

算法思想:由于暴搜需要扫描整个数组,当数组有序时,可采用二分查找,每次将区间缩小一半,远远降低时间复杂度
应用方面:查找

代码模板:

//要查找的值为x,数组为q[]
int l = 0, r = n - 1;
//模板一:
while(l < r)
{
	int mid = (l + r + 1) / 2;
	if(q[mid] <= x)	l = mid;
	else	 		r = mid - 1;
}

//模板二
while(l < r)
{
	int mid = (l + r) / 2;
	if(q[mid] >= x)		r = mid;
	else 				l = mid + 1;
}

三、前缀和与差分

算法思想:
前缀和:数组中前i个数的和
差分:为前缀和的逆运算,即将一个数分成若干数相加

前缀和应用很广泛,求和,求子矩阵和等等都可用到
差分最明显的特征为:当需要将一个区间内的数作相同变化时,即可用差分

代码模板:

//前缀和
for(int i = 1; i <= n; i ++ )
{
	s[i] = s[i - 1] + a[i];
}
//差分:
比如需要将[l,r]区间中的所有数加c
//先求差分数组
//仅仅对l操作即可,因为差分数组求前缀和以后即可达到整个数组都操作,再对r+1进行反操作就能保证仅仅是这个区间内操作
b[l] += c;
b[r + 1] -= c;
//再对差分数组求前缀和即可达到整个区间操作的目的,且时间复杂度仅仅为O(1);
for(int i = 1; i <= n; i ++ )
	b[i] = b[i - 1] + b[i];

四、双指针

五、位运算

快速求二进制数中1的个数

代码模板:

int x,count;
while(x)
{
	x -= x & -x;
	count ++;
}

六、DFS和BFS

DFS是一个深度搜索的过程,将一条路径搜索完之和才会回溯搜索其他路径,常用于排列组合问题,DFS需要用递归。
BFS是一个广度搜索的过程,它会将一个结点所连接的路径全部搜索出来后再往下继续搜索,因此它会有一个结点搜没搜过的特性,利用这个特性可以计算最短路径问题,BFS需要用队列来实现

关于DFS什么时候需要回溯的问题:
1.当搜索方式为选或者不选的时候,即比如排列数字,走迷宫等,当前有几个可以选择,我可以选这个也可以选那个,选了一个后,另一个等回溯到这里时再选,这个时候需要回溯,即恢复现场,保证只是在当前做选择后的路径不一样,即选择分支
2.而当搜索方式为每个点各自为一种情况,比如洪水灌溉,红与黑等,我要搜索其中有多少我可以走的(计数问题),每个点独立,即每次走完一个点后需要标记这个点,不需要回溯,如果这种情况回溯的话,那么肯定会重复计数,导致陷入死循环。

代码模板:需要回溯Acwing.842.排列数字
不需要回溯Acwing.1113.红与黑

/*****DFS******/
void dfs(int x)
{
	//等于n说明一条路径已经搜索完,将这条路径输出
	if(x == n)
	{
		for(int i = 0; i < n; i ++)
			printf("%d",path[i]);
			puts("");
			return ;
	}
	//否则证明还没有搜索完该条路径,向下继续搜索
	else
	{
		for(int i = 1; i <= n; i ++ )
		{
			if(!st[i])
			{
				path[x] = i;
				st[i] = true;
				//一个结点搜索完之和,直接搜索该结点的下一个结点
				dfs(x + 1);
				//执行到这里说明一条路径搜索完了,该回溯搜索其他路径
				path[i] = 0;
				st[i] = false;
			}
		}
	}
}

BFS
代码模板:

//BFS
int bfs()
{
	//定义一个队列
	queue<int> q;
	//将初始状态压入队列中
	q.push(...);
	//while循环直到队列为空,即搜索到最后一个元素
	while(!q.empty())
	{
		//取到当前队首元素
		auto t = q.front();
		//弹出队首元素
		q.pop();
		//位置向前移动
		int x = ...., y= .....
		//判断移动后的位置是否符合条件
		if(.......)
		{
			//符合条件则更新题中所求的东西
			//接着将移动后的位置压入队列中
			q.push(...);
		}
	}
}

完整模板题:Acwing.844.走迷宫

七、并查集

算法思想:并查集是对集合进行的操作,当需要合并集合时,只需要将一个集合的根节点接在另一个集合的根节点之下即可,并查集中最重要的操作:路径压缩,将每个结点都直接连在根节点之下

代码模板:

//并查集基本都会有的查找祖宗的操作
void find(int x)
{
	if(p[x] == x)	return x;  //p[x]为一个结点的父节点,如果一个结点的父节点是自己,那么它就是根节点
	else
		p[x] = find(p[x]);  //并查集的路径压缩,即将每个结点都直接连在根节点之下 
}

int main()
{
	for(int i = 1; i <= n; i ++ )
		p[i] = i;  //并查集初始化每个结点的父节点都是自己本身
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值