C++算法——排序算法梳理

今天来梳理一下我们常用的排序方式。只是梳理了排序方法中的一部分!!!

可以到这个网站看动画讲解:visualgo,点右下角箭头再点排序就可以看了。

一、冒泡排序

1.1 简介

冒泡排序应该是最简单的一种排序方法之一了吧,它的最坏时间复杂度是 O ( n 2 ) O(n^2) O(n2),最好是 O ( n ) O(n) O(n)。它虽然慢,但程序简单,而且是稳定的排序方式。

1.2 过程

将相邻两个数值比较,小/大的放前面,遍历 n − 1 n-1 n1次,即可得到有序数组。

1.3 原理

每一趟遍历找出当前乱序数列中最大元素并逐渐移动到末尾,从而完成排序,“冒泡”也因此得来。

1.4 程序(从小到大)

int* sort(int* a,int n){
    for(int i=0;i<n-1;++i){
        for(int j=1;j<n-i;++j){
            if(a[j]<a[j-1]){
                swap(a[j],a[j-1]);
            }
        }
    }
    return a;
}

二、选择排序

2.1 简介

选择排序是一种简单直观的排序算法。它的最好和最坏时间复杂度都是 O ( n 2 ) O(n^2) O(n2)

2.2 过程

先建立一个变量i,遍历 0 ~ n − 1 0~n-1 0n1,然后在此变量之后(包含此变量)的所有元素中找到最小/大值,与a[i]交换,遍历完即可排序完成。

2.3 原理

遍历每个元素,找到剩下序列排序完成时应在第一个的元素放在当前位置。

2.4 程序(从小到大)

int* sort(int* a,int n){
    for(int i=0;i<n-1;++i){
        int minn=1E9,mini=0;
        for(int j=i;j<n;++j){
            if(minn>a[j]){
                minn=a[j];
                mini=j;
            }
        }
        swap(a[i],a[mini]);
    }
    return a;
}

三、直接插入排序

3.1 简介

直接插入排序也很简单易懂,它是遍历数组,将数据插入到合适的位置,从而完成排序。它的最好时间复杂度是 O ( n ) O(n) O(n),最坏时间复杂度是 O ( n 2 ) O(n^2) O(n2)。除了直接插入排序,还有折半插入排序、希尔排序、和表插入排序三种,他们都是插入排序中的一种,在此不再赘述。

3.2 过程

遍历数组,从当前位置的前一位开始while循环找到第一个小于/大于当前元素的位置,同时将路上的所有元素往后移动,如果没找到就在0那里停下。最终将当前元素放到while循环中遍历的变量的位置,即可完成一次遍历。遍历完成之后排序完成。

3.3 程序(从小到大)

int* sort(int* a,int n){
    for(int i=0;i<n;++i){
        int t=a[i],cur=i;
        while((cur>0)&&(a[cur-1]>=t)){
            a[cur]=a[cur-1];
            cur--;
        }
        a[cur]=t;
    }
    return a;
}

四、桶排序

4.1 简介

桶排序也比较简单,程序是非常的好写。它的时间复杂度达到了惊人的 O ( n ) O(n) O(n),但别以为它就没有缺点了,它的空间复杂度也很惊人,达到了 O ( n + k ) O(n+k) O(n+k),其他的排序不都是 O ( n ) O(n) O(n)的时间复杂度吗,它在这个基础上增加了一个k,代表数据范围,也就是说如果有10个0~100的数,它至少需要110个int的空间…

4.2 过程

其实桶排序正常返回的是一个数组, a i a_i ai是i在原数组中出现的次数。如何制造出这个数组呢?只要遍历原数组,每次都 a [ s [ i ] ] + + a[s[i]]++ a[s[i]]++就可以了。那虽然很简单,要这个数组有什么用呢?其实,我们得到了这个数组,如果我们还想要得到排序序列,就很好弄了。如下:

//s是原数组,a是排序后的数组。
int cur=0;
for(int i=0;i<1000000;++i){
    while(a[i]){
        a[i]--;
        s[cur]=i;
        cur++;
    }
}

4.3 程序(从小到大,仅为生成数组字典的部分,拆开部分见上段)

void sort(int* a,int n,int* s){
    for(int i=0;i<n;++i){
        s[a[i]]++;
    }
}
//可以看到,这段代码还是很短的

五、快速排序

5.1 简介

快速排序终于出场了!它是对冒泡排序的一种改进。它的平均时间复杂度是 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),而最坏时间复杂度是 O ( n 2 ) O(n^2) O(n2)

5.2 过程

快速排序也分为很多次遍历,一次遍历的方法如下:先选取一个基准值,通过操作使得都小于基准值的元素处于一个区间内,大于基准值的元素处于一个区间内,再用递归分别遍历左区间和右区间。遍历完成后即可完成排序。(当区间已经只剩1个数时函数直接结束,不继续递归)

5.3 程序(从小到大)

void sort(int* a,int start,int end){
    if(start>=end){
        return;
    }
    int pivot=a[start];
    int l=start+1,r=end;
    while(l<=r){
        while(a[l]<=pivot&&l<=r){
            l++;
        }
        while(a[r]>=pivot&&l<=r){
            r--;
        }
        if(l>=r){
            break;
        }
        swap(a[l],a[r]);
    }
    swap(a[start],a[r]);
    sort(a,start,r-1);
    sort(a,r+1,end);
}

六、归并排序

6.1 简介

归并排序是一种建立在归并操作上的排序算法,它将数组分成若干个小段再组合从而完成排序。它的时间复杂度在所有情况下都是 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),空间复杂度是 O ( n ) O(n) O(n)

6.2 过程

归并排序的一次遍历:先将数组分为两半,分别遍历,最后组合在一起。所以我们很容易猜到它也要用递归。那么合并在一起怎么弄呢?我们只要定义两个指针,(从小到大的话)哪个小,就放入最终数组里,同时指针自增,判断指针的范围即可。合并函数如下(从小到大):

void merge(int* s,int start,int end,int mid){
    int a[mid-start+10]={};
    int b[end-mid+10]={};
    int cur=0;
    for(int i=start;i<=mid;++i){
        a[cur++]=s[i];
    }
    cur=0;
    for(int i=mid+1;i<=end;++i){
        b[cur++]=s[i];
    }
    cur=start;
    int l=0,r=0;
    for(;l<mid-start+1&&r<end-mid;){
        if(a[l]<=b[r]){
            s[cur++]=a[l];
            l++;
        }else{
            s[cur++]=b[r];
            r++;
        }
    }
    if(l<mid-start+1){
        for(int i=l;i<mid-start+1;++i){
            s[cur++]=a[i];
        }
    }else if(r<end-mid){
        for(int i=r;i<end-mid;++i){
            s[cur++]=b[i];
        }
    }
}

6.3 程序(从小到大,仅为递归排序部分,合并部分见上段)

void sort(int* a,int start,int end){
    if(start<end){
        int mid=(start+end)>>1;
        sort(a,start,mid);
        sort(a,mid+1,end);
        merge(a,start,end,mid);
    }
}

七、结尾

排序算法很多,(再次强调)在此仅是列举了其中的一小部分。本文中的代码均为手写排序的代码,如果没有特殊需求也可用C++标准库中的sort函数,这个函数的用法在此不再赘述,可自行查阅资料。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值