快速排序简洁写法

本文深入探讨了快速排序算法的实现细节,包括常见的错误点和优化策略。首先,介绍了基本的快速排序算法,强调了分区操作的边界处理和递归调用的正确方式。然后,提出了使用舍伍德算法进行随机枢轴选择以改善最坏情况下的时间复杂度。最后,针对含有大量重复元素导致性能退化的场景,给出了改进版的三向切分快速排序。整个讨论旨在提高快速排序算法的效率和稳定性。
摘要由CSDN通过智能技术生成

代码

int partition(vector<int> &a, int s, int e) {
  for (int i = s; i <= e; swap(a[s++], a[i++]))
    while (a[e] < a[i]) i++;
  return s - 1;
}
void quick_sort(vector<int> &a, int s, int e) {
  if (s >= e) return;
  int m = partition(a, s, e);
  quick_sort(a, s, m - 1);
  quick_sort(a, m + 1, e);
}

快速排序比较容易写错的点就两个

  • partition 里的边界判断。这里我把枢轴选在区间最后一个数,判断时又没带等号,所以while循环必然会在最后一个数字上停下。不用判断越界。i==e时顺便把最后一次交换也做了
  • 递归调用quick_sort时一定要m-1m+1这样可以保证长度减一,否者可能会死循环。

另一种受Linux内核源代码启发的写法

void quick_sort(vector<int> &a, int s, int e) {
  if (s >= e) return;
  int t = s;
  int m = ({
    for (int i = s; i <= e; swap(a[s++], a[i++]))
      while (a[e] < a[i]) i++;
    s - 1;
  });
  quick_sort(a, t, m - 1);
  quick_sort(a, m + 1, e);
}

优化

为了优化快速排序最坏复杂度。
用了舍伍德算法(Sherwood)随机选取枢轴。
代码如下

std::random_device rd;
int partition(vector<int> &a, int s, int e) {
  for (int i = s; i <= e; swap(a[s++], a[i++]))
    while (a[e] < a[i]) i++;
  return s - 1;
}
void quick_sort(vector<int> &a, int s, int e) {
  if (s >= e) return;
  swap(a[rd() % (e - s + 1) + s], a[e]);
  int m = partition(a, s, e);
  quick_sort(a, s, m - 1);
  quick_sort(a, m + 1, e);
}

另外提一句,取模获得某范围的随机数其实不靠谱。这里为了方便直接就取模了。
举个例子,假设原来随机数的范围是0,1,2 1 3 \frac13 31的概率。如果你模2,那么0的概率就变成了 2 3 \frac23 321的概率还是 1 3 \frac13 31,就不是等概率了。

后记

上面的属于快慢指针法的快排。遭到洛谷的毒瘤数据暴打。如果数组中有很多相同元素。就会退化为 O ( n 2 ) O(n^2) O(n2)
附上改进写法。

void quick_sort(int a[], int s, int e) {
  if (s >= e) return;
  int k = a[(s + e) / 2], i = s, j = e;
  while (i <= j) {
    while (a[i] < k) i++;
    while (a[j] > k) j--;
    if (i <= j) swap(a[i++], a[j--]);
  }
  quick_sort(a, s, j);
  quick_sort(a, i, e);
}

有以下注意点

  • 退出循环时要保证i>j,否则当i==j时可能会死循环。比如i=j=s时,会递归调用quick_sort(i, e);quick_sort(s, e);
  • swap前的 if (i <= j)不能少,否则当i>j时会导致乱序。
  • if (i <= j)也不能改成if (i < j),否则i==j时会死循环。
  • 内部while循环条件不能带等号,否则会越界。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值