NOIP-C++大神培养计划Step1.2.4快速排序

简介:

之前我们用到的排序的时间复杂度都是O(n^2)的,并不乐观,但是我们依然有更快的算法,它就是快速排序,时间复杂度O(n log n)。


我们还是先看看快速排序的原理:

0115a5312f9769ac3af29ba959726ba65c475b93


可能这个排序有些人看不懂,就让我们来讲一下它的基本原理吧。


比如我们来排序6  1  2  7  9  3  4  5  10  8


方法其实很简单:分别从初始序列“6  1  2 7  9  3  4  5 10  8”两端开始“探测”。先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们把这两个变量叫i和j。刚开始的时候让i指向序列的最左边(即i=1),指向数字6。让j指向序列的最右边(即=10),指向数字。


然后j开始移动。因为此处设置的基准数是最左边的数,所以需要让j先移动,这一点非常重要(请自己想一想为什么)。j一步一步地向左挪动(即j--),直到找到一个小于6的数停下来。接下来i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后j停在了数字5面前,i停在了数字7面前。


现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下:

6  1  2  5  9 3  4  7  10  8


到此,第一次交换结束。接下来开始j继续向左挪动(每次必须是j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列如下:

6  1  2 5  4  3  9  7 10  8


第二次交换结束,扫描继续。j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。i继续向右移动,此时i和j相遇了,i和j都走到3面前。说明此时扫描结束。我们将基准数6和3进行交换。交换之后的序列如下:

3  1 2  5  4  6  9 7  10  8


到此第一轮扫描真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。回顾一下刚才的过程,其实j就是要找小于基准数的数,而i就是要找大于基准数的数,直到i和j碰头为止。


之后,不断重复这个动作,将范围不断缩小,最终成为有序序列。


我们不妨来看看代码。


int a[101],n;
void quicksort(int left,int right) 
{ 
    int i,j,t,temp; 
    if(left>right) 
       return; 
                                
    temp=a[left]; //temp中存的就是基准数 
    i=left; 
    j=right; 
    while(i!=j) 
    { 
        //顺序很重要,要先从右边开始找 
        while(a[j]>=temp && i<j) j--; 
        while(a[i]<=temp && i<j) i++; 
        //交换两个数
        if(i<j) 
        { 
            t=a[i]; 
            a[i]=a[j]; 
            a[j]=t; 
        } 
    } 
    //最终将基准数归位 
    a[left]=a[i]; 
    a[i]=temp;            
    quicksort(left,i-1);//处理左边的
    quicksort(i+1,right);//处理右边的 
	//这里是一个递归的过程 ,就是不断缩小范围,最终得解 
} 

这就是快排。我们想一想为什么他会“快”。


快速排序之所比较快,因为相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样每次只能在相邻的数之间进行交换,交换的距离就大的多了。因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的都是O(n^2),它的平均时间复杂度为O(n log n)。其实快速排序是基于一种叫做“二分”的思想。至于二分思想,我们以后会介绍


手写快排会了吧?现在,关上门,我们单独说话。


有一个神器,叫sort,也就是排序的意思,他能干什么呢?

3e2ada97e04085017e3e573a6ab9786389fb5867


是的,他可以排序。


来看看两个人的代码:

1.


#include<iostream>
using namespace std;
int a[101],n;
void quicksort(int left,int right) 
{ 
    int i,j,t,temp; 
    if(left>right) 
       return; 
                                
    temp=a[left]; //temp中存的就是基准数 
    i=left; 
    j=right; 
    while(i!=j) 
    { 
        //顺序很重要,要先从右边开始找 
        while(a[j]>=temp && i<j) j--; 
        while(a[i]<=temp && i<j) i++; 
        //交换两个数
        if(i<j) 
        { 
            t=a[i]; 
            a[i]=a[j]; 
            a[j]=t; 
        } 
    } 
    //最终将基准数归位 
    a[left]=a[i]; 
    a[i]=temp;            
    quicksort(left,i-1);//处理左边的
    quicksort(i+1,right);//处理右边的 
	//这里是一个递归的过程 ,就是不断缩小范围,最终得解 
} 
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    quicksort(1,n);
    for(int i=1;i<=n;i++)
        cout<<a[i]<<endl;
    return 0;
}

这是你


2.


#include<iostream>
#include<algorithm>
using namespace std;
int n,a[10001];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
        cout<<a[i]<<endl;
    return 0;
}

这是大佬

OhhhhOhhhhhOhhhOhhhh


那么,大佬用到了sort,他是排序,包含在algorithm(算法)库中,有着极大的功能。


与我们的quicksort不同的是,sort函数的两个参数是由 一个字符“+”一个数字,就像 a+1,a+n+1一样。我们看到,a是我们待排序数组的数组名,1和n+1是待排序数组的元素编号,注意,这里是下表从0开始,我们用a[1]存,就要写a+1,我们用a[n+1],就要写a+n+1。怎么样,不错吧?


不过,我们测试之后发现,sort是默认升序排序,也就是从小到大,那如果我们要求从大到小降序排列怎么办呢?

没事,C++给了我们一个极大的权力:控制sort的排序规则。我们来看看如何做到。

bool cmp(int a,int b)//这里最好是bool
{
    return a>b;
}
int main()
{
    sort(a+1,a+n+1,cmp/*多出来的参数*/);
}

我们用bool型制造了一个函数:cmp,而sort中就加了一个参数:cmp。不错,这就是我们的规则。先让我们来看看为什么我们要用bool作为类型。


return a>b;  若a<=b,则返回0,sort的规则是,你制定的规则在一组数据中返回了0,就交换,才导致最后的序列是降序。其实,用int也行,只是bool的逻辑关系更明确。


如果你理解不了上面那一句话,那就看看通俗的讲解吧:

return a>b就是使所有的(a,b)[a在b前面] 使a>b,这就是工作原理啦!


当然,sort+cmp的用途远不止于此,更多的用途多在于结构体。


我们知道,sort适用于任何数据类型,包括结构体,比如结构体student


struct student
{
    int hight,weight,IQ;
}a[101];

我们则要将学生按身高排序,cmp函数如下:


bool cmp(student a,student b)
{
    return a.hight>b.hight;
}

sort函数如下:


sort(a+1,a+n+1,cmp);

这就解决了,是不是很方便?

c10748ed1918593ae161aeb303ba8c2ad1f3d72e

好了,今天我们的课程到这里就结束了,我们下节课精彩继续。


祝大家元旦快快乐!

相关文章
|
6月前
|
算法 C++
c++算法学习笔记 (1)快速排序
c++算法学习笔记 (1)快速排序
|
6月前
|
搜索推荐 C++
C++快速排序的实现
C++快速排序的实现
|
搜索推荐 C++
C++实现快速排序算法
快速排序算法时最常用的排序算法之一,时间复杂度为O(nlog(n))~O(n^2),最差的时候就是排序的原始数据和要求正好相反,如需要正序的结果,而原始数据恰好是逆序的过程。
144 0
|
6月前
|
Java C++
快速排序(c++,java)
快速排序(c++,java)
31 0
|
11月前
|
C++
C++快速排序
C++快速排序
57 1
|
6月前
|
搜索推荐 大数据 C++
C++系列案例-大数据减法-绘制余弦曲线-兔子数量-快速排序
C++系列案例-大数据减法-绘制余弦曲线-兔子数量-快速排序
|
算法 搜索推荐 C++
【C++】快速排序的学习和介绍
【C++】快速排序的学习和介绍
89 0
|
存储 人工智能 移动开发
【八大数据排序法】快速排序法的图形理解和案例实现 | C++
排序算法是我们在程序设计中经常见到和使用的一种算法,它主要是将一堆不规则的数据按照递增或递减的方式重新进行排序。在如今的互联网信息时代,随着大数据和人工智能的发展,大型企业的数据库中有亿级的用户数据量。因此对其进行处理,排序算法也就成为了其中必不可缺的步骤之一。
188 0
【八大数据排序法】快速排序法的图形理解和案例实现 | C++
|
C++ Python
LeetCode每日一题题解:912. 排序数组-题解-python && C++源代码-快速排序代码模板
LeetCode每日一题题解:912. 排序数组-题解-python && C++源代码-快速排序代码模板
|
存储 算法 搜索推荐
C++实现排序 - 02 归并排序、快速排序和堆排序
今天我们继续来整理平均时间复杂度为 O(nlogn) 的三个排序算法,即归并排序、堆排序和快速排序。
272 0
C++实现排序 - 02 归并排序、快速排序和堆排序