基础算法(一)
本系列博客记录了算法基础中的常用C++模板以及思路。
文章目录
前言
本文主要介绍各种比赛中常用的两种排序算法,分别是快速排序和归并排序。
一、快速排序(分治)
思想:
假设要排序的区间的左右两端分别为L-R,要对其进行快速排序。
- 确定分界点:
q[L],q[(L+R)/2],q[R],将整个区间分为两段。 - 调整区间
★:通过x分为两个区间,使得第一个区间里的数都小于等于x,第二个区间里的数都大于等于x。 - 递归处理左右两个区间。
那么如何优雅的调整区间呢?
假设有两个指针i和j分别指向q的左端点和右端点,如果i指向的值小于等于x,那么i就向后移动一位,直到i指向的值大于x停止。这时候移动j,如果j指向的值大于等于j,那么j就往前移动一位,直到j指向的值小于x停止。此时,i指向的数本该放到右半边,j指向的数本该放到左半边,所以将二者进行交换,并且两个指针都往中间移动一位。接着重复上述过程继续移动i和j,直到i和j相遇为止。此时,整个区间被一分为二,并且两个区间内的所有数均在各自的位置,即第一个区间里的数都小于等于x,第二个区间里的数都大于等于x。
模板题(AcWing785)
给定你一个长度为 n n n 的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式
输入共两行,第一行包含整数 n n n。
第二行包含 n n n 个整数(所有整数均在 1 ∼ 1 0 9 1∼10^9 1∼109范围内),表示整个数列。
输出格式
输出共一行,包含 n n n 个整数,表示排好序的数列。
数据范围
1
≤
n
≤
100000
1≤n≤100000
1≤n≤100000
输入样例:
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,要对其进行归并排序。
- 确定分界点:
q[(L+R)/2],分界点为整个数组下标的中间位置,将整个区间分为两段。 - 递归排序两个区间:通过分界点分为左右两个区间,两个区间内部都是有序的。
★归并两个区间,合二为一。
那么如何优雅的归并区间,使其合二为一呢?
答:双指针算法!
假设通过了第二步递归排序,得到了两个有序的子序列。我们采用两个指针i和j分别指向两个有序序列的第一个位置。此时,由于两个序列都是有序的,所以指针i和j的位置为两个序列的最小值。比较i和j指向元素的大小,将最小的那个存入结果数组中。不妨设i指向的值最小,那么将其存入结果数组。随后i后移一位。接着继续比较i和j位置的元素,较小者存入结果数组中,随后该指针后移一位继续比较。不断重复此过程,直到有一序列没有元素,则另一列的元素全部加入到结果数组中,归并排序完成。
模板题(AcWing787)
给定你一个长度为 n n n 的整数数列。
请你使用归并排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式
输入共两行,第一行包含整数 n n n。
第二行包含 n n n 个整数(所有整数均在 1 ∼ 1 0 9 1∼10^9 1∼109范围内),表示整个数列。
输出格式
输出共一行,包含 n n n 个整数,表示排好序的数列。
数据范围
1
≤
n
≤
100000
1≤n≤100000
1≤n≤100000
输入样例:
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;
}
总结
本文介绍了基础算法中常用的两种排序算法,并详细解释了其中思想,给出了模板题帮助算法的背诵和理解。另外,关于两种算法的稳定性:快排是不稳定的算法,而归并排序是稳定的。
325

被折叠的 条评论
为什么被折叠?



