十大排序(上)

目录

一.选择排序

定义

性质

稳定性

时间复杂度

代码实现

二.冒泡排序

定义

过程

性质

稳定性

时间复杂度

代码实现

三.插入排序 

定义

性质

稳定性

时间复杂度

代码实现

 四.计数排序

定义

过程

计算前缀和的原因

性质

稳定性

时间复杂度

代码实现

 五.基数排序

定义

过程

性质

稳定性

时间复杂度

空间复杂度

算法实现


今天我们开始排序算法的学习,本篇文章主要讲述选择排序,插入排序,冒泡排序,计数排序,基数排序.

其他五个排序算法太low了,这里就不细讲了.(主要是因为我不会)

一.选择排序

定义

选择排序(英语:Selection sort)是一种简单直观的排序算法。它的工作原理是每次找出第i小的元素(也就是A_{i...n}中最小的元素),然后将这个元素与数组第i个位置上的元素交换。

 

性质

稳定性

由于 swap(交换两个元素)操作的存在,选择排序是一种不稳定的排序算法。

时间复杂度

选择排序的最优时间复杂度、平均时间复杂度和最坏时间复杂度均为 O(n^{2})

代码实现

void selection_sort(int* a,int n){
  for(int i=1;i<n;++i){
    int ith=i;
    for(int j=i+1;j<=n;++j){
      if(a[j]<a[ith]){
        ith=j;
      }
    }
    std::swap(a[i],a[ith]);
  }
}

二.冒泡排序

定义

冒泡排序(英语:Bubble sort)是一种简单的排序算法。由于在算法的执行过程中,较小的元素像是气泡般慢慢「浮」到数列的顶端,故叫做冒泡排序。

过程

它的工作原理是每次检查相邻两个元素,如果前面的元素与后面的元素满足给定的排序条件,就将相邻两个元素交换。当没有相邻的元素需要交换时,排序就完成了。

经过i次扫描后,数列的末尾i项必然是最大的i项,因此冒泡排序最多需要扫描n-1遍数组就能完成排序。

性质

稳定性

冒泡排序是一种稳定的排序算法。

时间复杂度

在序列完全有序时,冒泡排序只需遍历一遍数组,不用执行任何交换操作,时间复杂度为O(n) 。

在最坏情况下,冒泡排序要执行(n-1)*n/2次交换操作,时间复杂度为O(n^{2}) 。

冒泡排序的平均时间复杂度为 O(n^{2}) 。 

代码实现

void bubble_sort(int *a,int n){
  bool flag=true;
  while(flag){
    flag=false;
    for(int i=1;i<n;++i){
      if(a[i]>a[i+1]){
        flag=true;
        int t=a[i];
        a[i]=a[i+1];
        a[i+1]=t;
      }
    }
  }
}

三.插入排序 

本页面将简要介绍插入排序。

定义

插入排序(英语:Insertion sort)是一种简单直观的排序算法。它的工作原理为将待排列元素划分为「已排序」和「未排序」两部分,每次从「未排序的」元素中选择一个插入到「已排序的」元素中的正确位置。

一个与插入排序相同的操作是打扑克牌时,从牌桌上抓一张牌,按牌面大小插到手牌后,再抓下一张牌。

性质

稳定性

插入排序是一种稳定的排序算法。

时间复杂度

插入排序的最优时间复杂度为O(n) ,在数列几乎有序时效率很高。 

插入排序的最坏时间复杂度和平均时间复杂度都为O(n^{2}) 。 

代码实现

void insertion_sort(int* a,int n){
  for(int i=2;i<=n;++i){
    int key=a[i];
    int j=i-1;
    while(j>0&&a[j]>key){
      a[j+1]=a[j];
      --j;
    }
    a[j+1]=key;
  }
}

 四.计数排序

Warning

本页面要介绍的不是基数排序

定义

计数排序(英语:Counting sort)是一种线性时间的排序算法。

过程

计数排序的工作原理是使用一个额外的数组C ,其中第i个元素是待排序数组 A中值等于i 的元素的个数,然后根据数组C来将A中的元素排到正确的位置。

它的工作过程分为三个步骤:

  1. 计算每个数出现了几次;
  2. 求出每个数出现次数的 前缀和;
  3. 利用出现次数的前缀和,从右至左计算每个数的排名。

计算前缀和的原因

阅读本章内容只需要了解前缀和概念即可

直接将C中正数对应的元素依次放入A中不能解决元素重复的情形。

我们通过为额外数组C中的每一项计算前缀和,结合每一项的数值,就可以为重复元素确定一个唯一排名:

额外数组C中每一项的数值即是该 key 值下重复元素的个数,而该项的前缀和即是排在最后一个的重复元素的排名。

如果按照A的逆序进行排列,那么显然排序后的数组将保持A的原序(相同 key 值情况下),也即得到一种稳定的排序算法。

 

性质

稳定性

计数排序是一种稳定的排序算法。

时间复杂度

计数排序的时间复杂度为O(n+w) ,其中w代表待排序数据的值域大小。

代码实现

const int N=100010;
const int W=100010;
int n,w,a[N],cnt[W],b[N];
void counting_sort(){
  memset(cnt,0,sizeof(cnt));
  for(int i=1;i<=n;++i) ++cnt[a[i]];
  for(int i=1;i<=w;++i) cnt[i]+=cnt[i-1];
  for(int i=n;i>=1;--i) b[cnt[a[i]]--]=a[i];
}

 五.基数排序

定义

基数排序(英语:Radix sort)是一种非比较型的排序算法,最早用于解决卡片排序的问题。

过程

基数排序的工作原理是将待排序的元素拆分为k个关键字(比较两个元素时,先比较第一关键字,如果相同再比较第二关键字……),然后先对第 k关键字进行稳定排序,再对第k-1关键字进行稳定排序,再对第k-2关键字进行稳定排序……最后对第一关键字进行稳定排序,这样就完成了对整个待排序序列的稳定排序。

性质

稳定性

基数排序是一种稳定的排序算法。

时间复杂度

一般来说,如果每个关键字的值域都不大,就可以使用 计数排序 作为内层排序,此时的复杂度为 O(nk+\sum_{i=1}^{k}w_{i}),其中w_{i}为第i关键字的值域大小。如果关键字值域很大,就可以直接使用基于比较的O(nk log n)排序而无需使用基数排序了。

空间复杂度

基数排序的空间复杂度为O(k+n) 。

算法实现

const int N=100010;
const int W=100010;
const int K=100;
int n,w[K],k,cnt[W];
struct Element{
  int key[K];
  bool operator<(const Element& y)const{
    for(int i=1;i<=k;++i){
      if(key[i]==y.key[i])continue;
      return key[i]<y.key[i];
    }
    return false;
  }
} a[N],b[N];
void counting_sort(int p){
  memset(cnt,0,sizeof(cnt));
  for(int i=1;i<=n;++i)++cnt[a[i].key[p]];
  for(int i=1;i<=w[p];++i) cnt[i]+=cnt[i-1];
  for(int i=n;i>=1;--i) b[cnt[a[i].key[p]]--]=a[i];
  memcpy(a,b,sizeof(a));
}
void radix_sort(){
  for(int i=k;i>=1;--i){
    counting_sort(i);
  }
}

 本来已经准备好的动图他突然传不上来了,只好在baike.baidu.com上面寻找(复制).所以对于萌新来说比较难理解(我就是萌新).

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cqbzcyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值