快速排序简洁写法

代码

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循环条件不能带等号,否则会越界。

猜你喜欢

转载自blog.csdn.net/qq_45256489/article/details/122267067