排序算法总结

Posted suqingdong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法总结相关的知识,希望对你有一定的参考价值。

关于排序算法的总结

1.1概述

生活中处处存在排序,考试成绩要排序,年龄大小要排序,图书馆的藏书也要排序。排序也是数据结构课程的一个重要组成部分。教材中的出现的排序有名次排序、选择排序冒泡排序等,下面我们一一进行介绍。

1.2排序算法

1.2.1名次排序 rank sort

所谓排序,也就是给排列的内容一个“排名”,也就是“第几”的“几”。比如,第三个,也就是前面有两个,“小于等于第三名”的有3个。名次排序的原理就来源于此:

  • 求出每个元素的“排名”,即“队列中所有比它小的元素数目+在它左边出现的与它相同的元素数目”。
  • 把“位次”记录到另一个数组中
  • 按照“位次”数组重新排列原数组的元素

参考代码:

template <class T>
void Rank(T a[], int n, int r[])
{ //计算元素的排名
    for (int i = 0; i < n; i++) r[i] = 0; //初始化
//逐对比较所有的元素。任两个都做比较,次数为(n-1)n/2 
    for (int i = 1; i < n; i++)
        for ( int j = 0; j < i; j++)
            if (a [j] <= a[i])   r[i]++;
                 else r[j]++;
}
template <class T>
void Rearrange (T a[], int n, int r[])
{ //按序重排数组a中的元素,使用附加数组u,数组r即为“排名”数组
    T *u = new T[n+1];
    //在u中按排名排列
    for (int i = 0; i < n; i++)
        u[r[i]] = a[i];
    //把u中的元素移回到a中
    for (int i = 0; i < n; i++)
        a[i] = u[i] ;
    delete [ ]u;
}

算法性能分析:
在计算排名的Rank函数中,比较次数为${frac{(n-1)n}{2}}$,Rearrange函数中移动次数为${2n}$,该排序算法的时间复杂度为O(${n^2}$)。
{

1.2.2选择排序 selection sort

对于给定的n个元素的数组:

  • 从中选出最大的元素,将其置于最后a[n-1]。
  • 余下的n-1个元素中选出最大,置于a[n-2]。
  • 以此类推,直至剩余一个元素。

参考代码:

template<class T>
int Max(T a[], int n)
{// 确定a [0 : n-1]中的最大元素
    int pos = 0;
    for (int i = 1; i < n; i++)
        if (a[pos] < a[i])
            pos = i;
    return pos;
}
template <class T>
void selectionSort (T a[], int n)
{ 
    for ( int size = n;  size>1; size- -) { //作比较并交换
        int j= Max(a, size);//size-1次比较
        Swap(a[j],a[size-1] ); //移动3次
    }
}

算法性能分析:
每次调用Max函数需要执行size-1次比较,总比较次数为${frac{(n-1)n}{2}}$,移动次数为${3(n-1)}$。时间复杂度O(${n^2}$)。

1.2.3冒泡排序 bubble sort

冒泡排序是一种简单的排序算法。它重复地走访要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

  • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  • 对每一对相邻元素作同样的工作,第一对到结最后一对,这样在最后的元素应该会是最大的数;
  • 针对所有的元素重复以上的步骤,除了最后一个;
  • 重复步骤1~3,直到排序完成。
    参考代码:
template <class T>
void bubbleSort (T a[], int n)
{ 
    for (int i =n; i>1; i--) {
        for (int j = 0; j < n-1; j++) {
            if (a[j] > a[j + 1])  
               swap(a[j], a[j + 1]);
        }
    }
}

算法性能分析:
该算法时间复杂度是${O(n^2)}$。

1.2.4插入排序 insert sort

插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。在从后向前扫描过程中,反复把已排序元素逐步向后挪位,为最新元素提供插入空间。举个形象的例子:打扑克牌摸牌的时候按牌的大小整理牌。
步骤:

  • 从第一个元素开始,该元素可以认为已经被排序
  • 取出下一个元素,在已经排序的元素序列中从后向前扫描
  • 如果扫描到的以排序的元素大于新元素,将该元素移到下一位置
  • 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  • 将新元素插入到该位置后
  • 重复步骤2~5
    参考代码:
template<class T>
void insertionSort(T a[], int n)
{   
    for (int i=1; i < n; i++) {
      //将a[i]插入a [0 : i-1]
    T t=a[i];
    int j;
    for (j=i-1; j >= 0 && t<a[j];  j--)
        a[j+1]=a[j];//向后移位,空出位置来插入
        a[j+1]=t;
    } 
}  

算法性能分析:
该算法时间复杂度是${O(n^2)}$。

1.2.5箱子排序 bin sort

箱子排序是指利用链表这种数据结构所设计的一种排序算法。这种排序首先把元素相同的节点放在同一个箱子里,然后通过把箱子链接起来就可以创建一个有序的链表。
箱子的理解和实现:

  • 每个箱子都是一个由节点组成的线性表。
  • 箱子中的节点数目介于0到n之间。
  • 把每个箱子都描述成一个链表。
  • 在进行节点分配之前,所有的箱子都是空的。

步骤:

  • 从欲排序链表的首部开始,逐个删除每个节点
  • 把所删除的节点放入适当的箱子中(即相应的链表中)
  • 收集并链接每个箱子中的节点,产生一个排序的链表
    参考代码:
void binSort(Chain<Node>& X,int range)
{//按分数排序
    intlen=X.Length();      Node x;
    Chain<Node> *bin;
    bin=new Chain<Node>[range+1];
    //分配到每个箱子中
    for(int i=1; i<=len; i++){
        X.Delete(1,x); //从X摘取第一个节点
        bin[x.score].Insert(0,x); 
        //向bin的合适位置插入节点
    }
    //从箱子中收集各元素,
    for(int j=range; j>=0; j--)
        while(!bin[j].IsEmpty()){
            bin[j].Delete(1,x);
            X.Insert(0,x);//链成新链表
        }
    delete[]bin;
}

算法性能分析:
两个for循环中执行的每一次插入和删除操作所需要的时间均为${Θ}$(1),因此第一个for循环的复杂性为${Θ}$(n),其中n为输入链表的长度。第二个for循环的复杂性为${Θ}$(n+range)。因此函数binSort总的复杂性为${Θ}$(n+range)。

1.2.6基数排序 radix sort

基数排序也是非比较的排序算法。它是一种稳定的排序算法。有两种方法:最高位优先法(MSD)和最低位优先法(LSD)。通常用于对数的排序选择的是最低位优先法,即先对最次位关键字进行排序,再对高一位的关键字进行排序,以此类推。
类似于桶排序,我们需要给待排序记录准备10个桶,因为一个数的任何一位上,其数字大小都位于0~9之间,因此采用10个桶,桶的编号分别为0,1,2,3,4...9,与待排序记录中每个数相应位的数值对应,基数排序也是因此而得名。
步骤:

  • 利用箱子排序的方法,根据最低位数字(即个位数字)对数字进行排序
  • 利用箱子排序的方法,对第一步的结果按照次低位数字(即十位数字)进行排序
  • 依此顺序依次进行,共(最大值的位数)次

算法性能分析:

  • 对于一般的基数r,相应的分解式为:
    ${x%r}$; ${x % {r^2} / r }$; ${x % {r^3}/{r^2}}$; ...
  • 当使用基数$r=n$对n个介于0~$n^c$-1范围内的整数进行分解时,每个数将可以分解出c个数字。
  • 因此,可以采用c次n个箱子的箱子排序。整个排序所需要的时间为$Θ(cn)=Θ(n)$(因为c是一个常量)。

总结:
|排序法|时间复杂度|稳定性|
|--|---|--|
|名次排序|$O(n^2)$|稳定|
|冒泡排序|$O(n^2)$|稳定 |
|选择排序|$O(n^2)$|不稳定|
|插入排序|$O(n^2)$|稳定|
|箱子排序|$Θ$(n+range)|稳定|
|基数排序|$Θ(n)$|稳定|

以上是关于排序算法总结的主要内容,如果未能解决你的问题,请参考以下文章

10 大排序算法总结

十大经典排序算法总结(归并排序)

十大经典排序算法总结(桶排序)

十大经典排序算法总结(基数排序)

十大经典排序算法总结(希尔排序)

十大经典排序算法总结(快速排序)