C++算法基础系列(一)

基础算法(一)

本系列博客记录了算法基础中的常用C++模板以及思路。



前言

本文主要介绍各种比赛中常用的两种排序算法,分别是快速排序和归并排序。


一、快速排序(分治)

思想:

假设要排序的区间的左右两端分别为L-R,要对其进行快速排序。

  1. 确定分界点:q[L],q[(L+R)/2],q[R],将整个区间分为两段。
  2. 调整区间:通过x分为两个区间,使得第一个区间里的数都小于等于x,第二个区间里的数都大于等于x
  3. 递归处理左右两个区间。

那么如何优雅的调整区间呢?

假设有两个指针ij分别指向q的左端点和右端点,如果i指向的值小于等于x,那么i就向后移动一位,直到i指向的值大于x停止。这时候移动j,如果j指向的值大于等于j,那么j就往前移动一位,直到j指向的值小于x停止。此时,i指向的数本该放到右半边,j指向的数本该放到左半边,所以将二者进行交换,并且两个指针都往中间移动一位。接着重复上述过程继续移动ij,直到ij相遇为止。此时,整个区间被一分为二,并且两个区间内的所有数均在各自的位置,即第一个区间里的数都小于等于x,第二个区间里的数都大于等于x

模板题(AcWing785)

给定你一个长度为 n n n 的整数数列。

请你使用快速排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 n n n

第二行包含 n n n 个整数(所有整数均在 1 ∼ 1 0 9 1∼10^9 1109范围内),表示整个数列。

输出格式

输出共一行,包含 n n n 个整数,表示排好序的数列。

数据范围
1 ≤ n ≤ 100000 1≤n≤100000 1n100000

输入样例:

5
3 1 2 4 5

输出样例:

1 2 3 4 5

代码如下
注意:代码考虑到了边界问题和死循环问题,不是严格根据思想进行的,思想仅供理解。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e6 + 10;
int n;
int q[N];

void quick_sort(int q[], int l, int r)
{
	if(l>=r) return;
	
	int x = q[(l+r)/2], 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);
	quick_sort(q, j + 1, r);
}

int main()
{
	scanf("%d",&n);
	for (int i = 0; i < n; i ++ ) scanf("%d",&q[i]);
	
	quick_sort(q, 0, n-1);
	
	for (int i = 0; i < n; i ++ ) printf("%d ",q[i]);
	
	return 0;
}

二、归并排序(分治)

思想:

假设要排序的区间的左右两端分别为L-R,要对其进行归并排序。

  1. 确定分界点:q[(L+R)/2],分界点为整个数组下标的中间位置,将整个区间分为两段。
  2. 递归排序两个区间:通过分界点分为左右两个区间,两个区间内部都是有序的。
  3. 归并两个区间,合二为一。

那么如何优雅的归并区间,使其合二为一呢?

答:双指针算法!

假设通过了第二步递归排序,得到了两个有序的子序列。我们采用两个指针ij分别指向两个有序序列的第一个位置。此时,由于两个序列都是有序的,所以指针ij的位置为两个序列的最小值。比较ij指向元素的大小,将最小的那个存入结果数组中。不妨设i指向的值最小,那么将其存入结果数组。随后i后移一位。接着继续比较ij位置的元素,较小者存入结果数组中,随后该指针后移一位继续比较。不断重复此过程,直到有一序列没有元素,则另一列的元素全部加入到结果数组中,归并排序完成。

模板题(AcWing787)

给定你一个长度为 n n n 的整数数列。

请你使用归并排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 n n n

第二行包含 n n n 个整数(所有整数均在 1 ∼ 1 0 9 1∼10^9 1109范围内),表示整个数列。

输出格式

输出共一行,包含 n n n 个整数,表示排好序的数列。

数据范围
1 ≤ n ≤ 100000 1≤n≤100000 1n100000

输入样例:

5
3 1 2 4 5

输出样例:

1 2 3 4 5

代码如下

#include<iostream>

using namespace std;
const int N = 1e6 + 10;
int n;
int q[N], tmp[N];

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;
	
	while(i <= mid && j <= r)
	{
		if (q[i] <= q[j]) tmp[k ++] = q[i ++];
		else tmp[k ++] = q[j ++];
	}

	while(i <= mid) tmp[k ++] = q[i ++];
	while(j <= r) tmp[k ++] = q[j ++];
	
	for (int i = l,j = 0; i <= r; i ++, j ++) q[i] = tmp[j];
}

int main()
{
	scanf("%d",&n);
	for (int i = 0; i < n; i ++ ) scanf("%d",&q[i]);
	
	merge_sort(q, 0, n-1);
	
	for (int i = 0; i < n; i ++ ) printf("%d ",q[i]);
	
	return 0;
}

总结

本文介绍了基础算法中常用的两种排序算法,并详细解释了其中思想,给出了模板题帮助算法的背诵和理解。另外,关于两种算法的稳定性:快排是不稳定的算法,而归并排序是稳定的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值