9种排序算法总结

排序算法可以说是计算机专业学生要学习的最基础的算法,但其实也是最重要的,现在大部分互联网公司笔试面试也都会涉及到排序算法的知识。除了了解思想之外,还应该动手写一写,分析一些具体思路、时间复杂度、空间复杂度和稳定性等。

我们面试讨论小分队也简单讨论了一下排序算法,为了加深记忆,我自己也动手写了一些代码(Linux平台写的,自己测试是通过了),并做一些分析(由于水平较水,代码可能有误!)。

9种排序算法分别为:选择排序、冒泡排序、插入排序、希尔排序、归并排序、堆排序、快速排序、计数排序、基数排序!

1. 选择排序

基本思想:从第一个位置开始依次选择该位置的元素,第i次扫描就可以选出第i小的元素,思想很简单,现在用的较少。

特点:平均时间复杂度O(n^2),最坏时间复杂度O(n^2),额外空间O(1),不稳定排序(举例:序列5 8 5 2 9, 第一遍选择第1个元素5会和2交换,原序列中2个5的相对前后顺序就被破坏了),n较小时较好!

代码:

void select_sort(int *a, int n)
{
    for(inti = 1; i <= n; i++) {
        intmin_pos = i;
        for(intj = i+1; j <= n; j++)
            if(a[j] < a[min_pos])
                min_pos = j;

        if(min_pos != i) {
            inttemp = a[i];
            a[i] = a[min_pos];
            a[min_pos] = temp;
        }
    }
}

2. 冒泡排序

基本思想:顾名思义,每一趟都通过相邻元素两两比较,通过交换将较小的元素往前移动,一趟下来就可以将最小的元素(气泡)移动到最前面。一般会加一个标志flag,若一趟扫描没有任何元素交换,则说明序列已经有序,flag为false,直接退出。

特点:平均时间复杂度O(n^2),最坏时间复杂度O(n^2),额外空间O(1),稳定排序(因为比较和交换都是两相邻元素,相等时不交换),n较小时较好!

代码:

void bubble_sort(int *a, int n)
{
    boolflag =false;
    for(inti = 1; i <= n; i++) {
        for(intj = n; j > i; j--)
            if(a[j] < a[j-1]) {
                inttemp = a[j];
                a[j] = a[j-1];
                a[j-1] = temp;
                flag =true;
            }
        if(!flag)
            return;
    }
}

3. 插入排序

基本思想:假定一个已排好序的序列和一个元素,只需将该元素从序列末尾向前比较,找到第一个小于它的序列元素,排在其之后即可。思想类似于玩扑克牌时整理牌面。

特点:平均时间复杂度O(n^2),最坏时间复杂度O(n^2),额外空间O(1),稳定排序(比较元素和序列时,找到序列中相等元素的话,排在其之后),序列大部分已排好序时(时间复杂度可提升至O(n))较好!

代码:

void insert_sort(int *a, int n)
{
    inttemp;
    for(inti =2; i <= n; i++) {
        intj = i - 1;
        temp = a[i];
        while(j >= 1) {
            if(a[j] > temp) {
                a[j+1] = a[j];
                j--;
            }else
                break;
        }
        a[j+1] = temp;
    }
}

4. 希尔排序

基本思想:插入排序的升级版(根据其特点:序列大部分已排好序时效率很高),将数据分为不同的组,先对每一组进行排序,然后对所有元素进行一次排序(即最后步长必须为1),步长的选择是递减的,比如5、3、1,现在一般使用D.E.Knuth分组方法(n很大是,用h(n+1)=3h(n)+1来分组,即1、4、13......)。

特点:平均时间复杂度O(n*logn),最坏时间复杂度O(n^s)(1<s<2),额外空间O(1),不稳定排序(相等元素在不同组里,交换后相对顺序可能改变)!

代码:

void shell_sort(int *a, int n)
{   //我这里步长为5、3、1,仅为举例
    for(intgap = 5; gap > 0; gap -= 2)
        for(inti = gap + 1; i <=n; i++) {
            intj = i - gap;
            inttemp = a[i];
            while(j >= 1) {
                if(a[j] > temp) {
                    a[j + gap] = a[j];
                    j -= gap;
                }else
                    break;
            }
            a[j+gap] = temp;
        }
}

5. 归并排序

基本思想:分治的思想,就是用递归先将序列分解成只剩一个元素的子序列,然后逐渐向上进行合并,每次合并过程就是将两个内部已排序的子序列进行合并排序,只需O(n)时间。

特点:平均时间复杂度O(n*logn),最坏时间复杂度O(n*logn),额外空间O(n)(另外需要一个数组),稳定排序,当n较大时较好(当也不能太大,用了递归就要考虑栈溢出)!

代码:

int b[MAX] = {0};

void merge(int *a,intlow, int mid, inthigh)
{
    inti = low, j = mid + 1;//左边和右边的初始位置
    intk = i;
    while(i <= mid && j <= high) {
        if(a[i] <= a[j]) {
            b[k++] = a[i];
            i++;
        }else{
            b[k++] = a[j];
            j++;
        }
    }
    while(i <= mid){
        b[k++] = a[i++];
    }
    while(j <= high){
        b[k++] = a[j++];
    }

    for(intx = 1, i = low; x <= high-low+1; x++, i++)
        a[i] = b[i];
}

voidmerge_sort(int*a,int low, int high)
{
    intmid;
    if(low < high) {
        mid = (low + high) / 2;
        merge_sort(a, low, mid);
        merge_sort(a, mid+1, high);
        merge(a, low, mid, high);
    }
}

6. 堆排序

基本思想:利用最大堆的性质——父节点拥有最大值,所以不断的将堆的根节点与最后节点交换,减小堆长度,然后再恢复堆性质,堆排序主要就是建立最大堆和不断恢复堆性质两个过程。堆排序不需要用到递归,所以适合海量数据处理,同时堆还可以用于优先级队列。

特点:平均时间复杂度O(n*logn),最坏时间复杂度O(n*logn),额外空间O(1),不稳定排序(涉及根节点与最后节点的交换,可能会破坏两相等元素的相对位置!),当n较大时较好(海量数据)!

代码:

void max_heapify(int *a, int p, int n)
{
    intleft = 2 * p;
    intright = 2 * p + 1;
    intlarge = p;
    if(left <= n && a[left] > a[p])
        large = left;
    if(right <= n && a[right] > a[large])
        large = right;

    if(large != p) {
        inttemp = a[p];
        a[p] = a[large];
        a[large] = temp;
        max_heapify(a, large, n);
    }
}

voidheap_sort(int*a,int n)
{
    //build_max_heap
    for(inti = n/2; i > 0; i--)
        max_heapify(a, i, n);

    inttemp;
    while(n > 1){
        temp = a[n];
        a[n] = a[1];
        a[1] = temp;

        --n;
        max_heapify(a, 1, n);
    }
}

7. 快速排序

基本思想:快排是目前使用最多的排序算法,每次都是先选择一个位置的元素(可以为序列的最左或最右位置)作为中间值,将比其小的元素放在其左边,比其大的元素放在右边,然后递归对其左边和右边的子序列进行相同操作,直到子序列为单个元素。

特点:平均时间复杂度O(n*logn),最坏时间复杂度O(n^2)(序列基本有序时,退化为冒泡排序),额外空间O(logn),不稳定排序(举例:序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱),当n较大时较好(当也不能太大,用了递归就要考虑栈溢出)!

代码:

void quick_sort(int *a, int p, int r)
{
    if(p < r) {
        inttemp;
        intx = a[r];
        inti = p - 1;
        for(intj = p; j < r; j++)
            if(a[j] < x) {
                i++;
                temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }

        temp = a[i+1];
        a[i+1] = a[r];
        a[r] = temp;
        quick_sort(a, p, i);
        quick_sort(a, i+2, r);
    }
}

8. 计数排序

基本思想:假定输入是有一个小范围内的整数构成的(比如年龄等),利用额外的数组去记录元素应该排列的位置,思想比较简单,看代码即可了解。

特点:在一定限制下时间复杂度为O(n),额外空间O(n)(需要两个数组),稳定排序!

代码:

int b[MAX] = {0};
int c[MAX] = {0};

void counting_sort(int *a, int n)
{

    for(inti=1; i <= n; i++)
        c[a[i]]++;   //c[i]包含等于i的元素个数

    for(inti=1; i < MAX; i++)
        c[i] += c[i-1];//c[i]包含小于等于i的元素个数

    for(inti = n; i>0; i--){
        b[c[a[i]]] = a[i];
        c[a[i]]--;
    }
    for(inti = 1; i <=n; i++)
        a[i] = b[i];
}

9. 基数排序

基本思想:只适用于整数排序,确定序列中元素的最大位数d,只要进行d次循环,从低位开始根据相应位置的数进行排序。(我的代码中具体排序是参考了计数排序,数据结构中还可以用链式相关的方法)。

特点:在一定限制下时间复杂度为O(n),额外空间O(n)(需要两个数组),稳定排序!

代码:

int b[MAX] = {0};
int counter[10] = {0};
int get_value(int v, int d) //获取第d位上的值
{
    for(inti = 1; i < d; i++)
        v = v/10;
    returnv%10;

}
//只能排序d位的十进制数
voidradix_sort(int*a,int n, int d)
{
    intx;
    for(intk = 1; k <= d; k++) {
        for(inti = 0; i < 10; i++)
            counter[i] = 0;//注意,一定要清零
        for(inti = 1; i <= n; i++) {
            x = get_value(a[i], k);
            counter[x]++;
        }

        for(inti = 1; i < 10; i++)
            counter[i] += counter[i-1];
        for(inti = n; i > 0; i--) {
            x = get_value(a[i], k);
            b[counter[x]] = a[i];
            counter[x]--;
        }
        for(inti = 1; i <= n; i++)
            a[i] = b[i];
    }
}

排序总结

稳定性:选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。

快速排序算法使用最广泛,大数据量时适合使用快速排序、归并排序和堆排序,需要O(n)时间复杂度时(当然要考虑数值范围的限制),可以考虑使用计数排序、基数排序、桶排序(上面未介绍,思想很简单,假设数据分布均匀!)等。

最后是我用来测试排序算法的main函数,非常简单!

#include <iostream>
usingnamespacestd;

constintMAX = 255;

int main ()
{
    intn;
    inta[MAX];
    cin >> n;
    for(inti = 1; i <= n; i++)
        cin >> a[i];

    cout <<"Before sort:";
    for(inti = 1; i <= n; i++)
        cout << a[i] <<" ";
    cout << endl;
    //radix_sort(a, n, 2);
    //select_sort(a, n);
    //insert_sort(a, n);
    //bubble_sort(a, n);
    //quick_sort(a, 1, n);
    //heap_sort(a, n);
    //merge_sort(a, 1, n);
    //counting_sort(a, n);
    //shell_sort(a, n);
    cout <<"Sort:";
    for(inti = 1; i <= n; i++)
        cout << a[i] <<" ";
    cout << endl;
    return0;
}

排序算法基本上就总结如上,告诫自己,不能死记硬背!要理解思想,同时要注意实现上的一些技巧!

时间: 2024-10-25 06:36:27

9种排序算法总结的相关文章

C#的四种排序算法

排序|算法 本文介绍了C#的四种排序算法:冒泡排序.选择排序.插入排序和希尔排序 冒泡排序 using System: namespace BubbleSorter { public class BubbleSorter { public void Sort(int [] list) { int i,j,temp: bool done=false: j=1: while((j<list.Length)&&(!done)) { done=true: for(i=0:i<list.

经典算法-C#四种排序算法

排序|算法 本文介绍了C#的四种排序算法:冒泡排序.选择排序.插入排序和希尔排序 冒泡排序 using System: namespace BubbleSorter { public class BubbleSorter { public void Sort(int [] list) { int i,j,temp: bool done=false: j=1: while((j<list.Length)&&(!done)) { done=true: for(i=0:i<list.

C#的四种排序算法:冒泡排序、选择排序、插入排序和希尔排序

插入|排序|算法 本文介绍了C#的四种排序算法:冒泡排序.选择排序.插入排序和希尔排序 冒泡排序 using System: namespace BubbleSorter { public class BubbleSorter { public void Sort(int [] list) { int i,j,temp: bool done=false: j=1: while((j<list.Length)&&(!done)) { done=true: for(i=0:i<li

经典的7种排序算法 原理C++实现

经典的7种排序算法 原理C++实现 排序是编程过程中经常遇到的操作,它在很大程度上影响了程序的执行效率. 7种常见的排序算法大致可以分为两类:第一类是低级排序算法,有选择排序.冒泡排序.插入排序:第二类是高级排序算法,有堆排序.排序树.归并排序.快速排序. 一.低级排序算法 1. 选择排序 排序过程:给定一个数值集合,循环遍历集合,每次遍历从集合中选择出最小或最大的放入集合的开头或结尾的位置,下次循环从剩余的元素集合中遍历找出最小的并如上操作,最后直至所有原集合元素都遍历完毕,排序结束. 实现代

java语言中哪一种排序算法用的最多?

问题描述 java语言中哪一种排序算法用的最多? java语言中哪一种排序算法用的最多?快速排序既然效率高,为什么我们还要用冒泡呢?冒泡的好处是什么? 解决方案 不能说快速排序一定效率高,对于有序的序列,归并排序的效率就更高.对于大量小整数的排序,基数排序不但效率高,而且占用内存少.各种排序有不同的使用场合.所以都要学习,而不是问哪种常用. 解决方案二: 冒泡排序是用来理解排序的思路的,快速排序是默认的java排序,但是稳定性极差,建议你去百度八大排序,从快速插入排序开始,系统的学习理解排序.

C#几种排序算法_C#教程

作者:Sabine [导读]本文介绍了C#的四种排序算法:冒泡排序.选择排序.插入排序和希尔排序  冒泡排序 using System: namespace BubbleSorter  { public class BubbleSorter  { public void Sort(int [] list)  { int i,j,temp:  bool done=false:  j=1:  while((j<list.Length)&&(!done))  { done=true:  f

JavaScript中几种排序算法的简单实现_基础知识

排序算法的实现 我的JS水平就是渣渣,所以我就用类似于JAVA和C的方式来写JavaScript的排序算法了. 而且这里我不讲算法原理,仅仅只是代码实现,可能会有Bug,欢迎大家博客评论指导.插入排序 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排

Java中常用的6种排序算法详细分解_java

排序算法很多地方都会用到,近期又重新看了一遍算法,并自己简单地实现了一遍,特此记录下来,为以后复习留点材料. 废话不多说,下面逐一看看经典的排序算法: 1. 选择排序 选择排序的基本思想是遍历数组的过程中,以 i 代表当前需要排序的序号,则需要在剩余的 [i-n-1] 中找出其中的最小值,然后将找到的最小值与 i 指向的值进行交换.因为每一趟确定元素的过程中都会有一个选择最大值的子流程,所以人们形象地称之为选择排序.举个实例来看看: 初始: [38, 17, 16, 16, 7, 31, 39,

js三种排序算法分享_javascript技巧

复制代码 代码如下: /** * 值交换操作 * arr 被操作的数组 * i 被操作元素索引值 * j 被操作两元素的距离 */ function refer(arr, i, j){ var change = (arr[i] - arr[i - j]) < 0 ? true : false, value; if (change) { value = arr[i]; arr[i] = arr[i - j]; arr[i - j] = value; return arguments.callee(