快排以及BFPRT算法的学习日记

快排:

之前一直久仰快排大名,今日终于决定学习一下(主要是c++直接sort多爽(滑稽))

直接原因是接触到一个叫BFPRT的算法,快排作为应该是它的前导知识,于是今天就手打了一个(假)快排,在打的过程中遇到了一些问题,于是写下了这篇日记(滑稽)

关于快排我再赘述一下

就是一个数列中选取一个基准点,然后从左右向中间遍历,将大于它的放右边,小于它的放左边。

具体做法:

1.确定 l,r指针,以及flag基准点

2.r指针先动,r--,直到遇到一个数小于flag停下

3.l指针再动,l++,直到遇到一个数大于flag停下

4.交换l,r指针

5.重复上述操作直到l==r

6.交换基准点和l,r相遇那个位置的指针

7.递归调用上述过程。

 

疑问1:      l,r移动过程中若是遇到一个数的值等于基准点怎么办,用不用带上它一起移到最后l,r相等的位置?

答:不用管,继续移动。因为处理好之后基准点两边的数分别是小于等于基准点,大于等于基准点,当下一次递归的时候等于基准点的值还是会被移动接近该基准点。

 

疑问2:     为什么要r先动,l后动?

答:应该和基准数的选择有关,在升序排序下,选左边那个,假如l先动,那么我们考虑这种情况: 5 4 3 6 7 8 ,l会在6那里停下来,然后r再动,在6那里被l逼停下来,交换6 和 5 ,结果: 6 4 3 5 7 8 ,就出错了。

因此应该选基准数相对的另外一边作为开始。

放代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
int num[maxn]; 
void q_sort(int *beg,int *end) { //升序快排,含头不含尾
	if (beg >= end) return;
	int *l = beg, *r = end - 1;
	int sto;
	int flag = *beg; //基准点
	while (l < r) {
		while (*r >= flag&&r > l) r--; //降序快排的化这里符号变一下
		while (*l <= flag&&l < r) l++;
		sto = *r; //swap
		*r = *l;
		*l = sto;
	}
	sto = *beg;
	*beg = *l;
	*l = sto;
	q_sort(beg, l); //含前不含后啊鸽鸽
	q_sort(l + 1, end);
}
int main() {
	int n; cin >> n;
	for (int i = 1; i <= n; i++) cin >> num[i];
	q_sort(num + 1, num + n + 1);
	for (int i = 1; i <= n; i++) cout << num[i] << " ";
	cout << endl;
}

 

BFPRT算法:

好的施工完毕

我发现用这个算法过不了我们学校oj上面一道用nth_element能过的题,感觉这个还是不够快啊!!!

这个算法可以解决无序数组中第k大的问题,因为使用partition的思想做得话,因为其基准点默认第一个数,因此存在一定情况会退化成O(n^2)的算法。

这个算法就解决了这个不稳定的问题,其通过自选基准点解决。

做法:

1.分组,每5个为一组

2.组内排序,组外不需要管

3.将每个组的中位数拿出来组另外一组,名字为qq

5.得到qq的中位数,用那个作划分

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e7 + 7;
int num[maxn], cnt[maxn];
inline int read() {
	int x = 0, f = 1; register char ch = getchar();
	for (; ch<'0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * f;
}
int get_mid(int l,int r) {//含前不含后
	sort(num + l, num + r);
	return num[(l + r) / 2];
}
int part_sort(int l,int r) { //分成五组然后sort
	int flag = (r - l ) / 5 + (r - l ) % 5 == 0 ? 0 : 1;
	// flag:究竟要分几组
	int middle[maxn]; //中位数组成的数组
	for (int i = 0; i < flag; i++) {
		middle[i] = get_mid(l + i * 5, min(l+ i * 5 + 5, r));
	}
	sort(middle, middle + flag);
	return middle[flag / 2]; //返回中位数数组的中位数
}
inline void Swap(int a, int b) {
	int temp = num[a];
	num[a] = num[b];
	num[b] = temp;
}
void partation(int l,int r,int div) { //以devide为基准点的快排
	int ll = l-1, rr = r , cur = l;
	while (cur < rr) {
		if (num[cur] < div) {
			ll++;
			Swap(ll, cur);
			cur++;
		}
		else if (num[cur]==div) {
			cur++;
		}
		else {
			rr--;
			Swap(rr, cur);
		}
	}
	cnt[0] = ll + 1;
	cnt[1] = rr - 1; //表示中间区域的坐标
}
int bfprt(int l,int r,int k) { //依然含前不含后,这里的k从0开始
	if (l == r-1) { //只有一个数就返回
		return num[l];
	}
	int divide = part_sort(l, r);
	partation(l, r, divide);
	if (k >= cnt[0] && k <= cnt[1]) return num[k];
	else if (k < cnt[0]) return bfprt(l, cnt[0] , k);
	else return bfprt(cnt[1] + 1, r, k);
}
int main() {
	int n, k;
	cin >> n;
	for (int i = 0; i < n; i++) num[i] = read();
	cin >> k;
	cout << bfprt(0, n, k - 1) << endl; // 这里的k从1开始
	return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值