【算法1-2】快速排序

>这次的排版有了改进,用到了markdown排版,这里感谢某位杨同学,我第一次知道了还有这种文本标记语言来帮助排版

看了看之前的推文,感觉有点太啰嗦了,我尽量用最简单的语句来表达算法的**大概**内容,嘿嘿!

现在有一个需要排序的数组`int s[n]`,我们先不管它里面含有什么元素,只知道它是一个**n**长度的的整型数组即可,简单的升序排序如下:
```
for (int i = 0; i < n; i++)
{
  int low = i;
  for (int j = i+1; j < n; j++)
  {
    if (s[low] > s[j])
    low = j;
  }
  int temp = s[i];
  s[i] = s[low];
  s[low] = temp;
}
```
这个简单的排序算法的时间复杂度是t(n^2),一般超过或等于n^2的时间复杂度在算法比赛上都过不了,因为一般n会给的特别大。

比较快的算法其中就有所谓的**快速排序**了,有多快呢?~~就一句话:~~ `sort(s, s+n)` 这是c++给出STL库中的快速排序函数,时间复杂度大概是t(nlog2(n))。

`sort(a, b, c)`函数有三个参数:
1. 第一个是要排序的数组的开头的地址,即`s.begin()`。
2. 第二个是要排序的数组的最后一位元素的**后一位**的地址,即`s.end()`。
3. 第三个是一个bool函数,可自定义如:
```
bool cmd(int a, int b)
{ //代表了比较的是整型数组且前面的大于后面的,降序排序
  return a > b;
}
```

当然我们不能就满足于背下`sort`这个函数就好,我们的目的是学习它的内在,嘿嘿。

这里就要讲到一个经典的思想——**分治法**, 字面意思就是分而治之,就是把一个复杂的问题分成两个或多个相同或类似的问题,再把子问题按类似的方法再一直分下去直到最后的子问题可以简单地直接求解,**原问题的解就是子问题的解的合并**,也是快速排序算法的基础。

快速排序使用了递归思想,我们看下这段代码,它实现了以数组元素的中间数为基准数,把大于这个基准数的元素都移到数组的左边,小于它的元素都移到数组的右边。
```
void qsort(int a[], int l,int r)//应用二分思想
{
  int mid=a[(l+r)/2];//中间数
  int i=l,j=r;
  do{
  while(a[i]>mid) i++;//查找左半部分比中间数小的数
  while(a[j]<mid) j--;//查找右半部分比中间数大的数
  if(i<=j)//如果有一组不满足排序条件(左大右小)的数
  {
    swap(a[i],a[j]);//交换
    i++;
    j--;
  }
  }while(i<=j);//注意有=
}
```
在这个基础上将已经进行一次左大右小的数组**一分为二**,左边分成一组,右边分成一组,再每一组进行左大右小,一直这样下去直到最后的子数组变成一个元素,就不需要排序了。由此完成了快速排序了。

完整代码如下:
```
void qsort(int a[], int l,int r)
{
  int mid=a[(l+r)/2];
  int i=l,j=r;
  do{
    while(a[i]>mid) i++;
    while(a[j]<mid) j--;
    if(i<=j)
    {
      swap(a[i],a[j]);
      i++;
      j--;
    }
  }while(i<=j);//有=是因为没有=号的话会导致可能出现i=j的情况,递归会出错
  if(l<j) qsort(l,j);//递归左半部分
  if(i<r) qsort(i,r);//递归右半部分
}
```
好啦,快排的简单推送就要结束了,这里贴个题目,不是很难,但又不是能直接`sort`的,感兴趣的同学可以点开看看:洛谷上的[P1923 【深基9.例4】求第 k 小的数](https://www.luogu.com.cn/problem/P1923)。

注释:
1. 代码是我手写的,所以应该不会错**吧**。
2. 这是以中间元素为基准数的快排,其实也可以以其他位置的元素为基准数进行快排,但是中间好像快一点,我一开始用第一个当基准数没有过。

posted @ 2021-04-21 14:58  白缺  阅读(98)  评论(0编辑  收藏  举报