数据结构与算法(22)—— 选择类排序

选择类排序

  • 简单选择排序
    • 空间复杂度:需要额外的存储空间仅为交换元素时借助的中间变量,所以空间复杂度是O(1)
    • 时间复杂度:
      关键操作在于交换元素操作,整个算法由双重循环组成,外层循环从0到n-2一共n-2+1=n-1次,
      对于第i层外层循环,内层循环执行n-1-(i+1)+1=n-i-1次。
      当i=0,内层循环执行n-1次,当i=n-2,内层循环执行1次,所以是一个等差数列求和,一共为(1+n-1)(n-1)/2=n(n-1)/2 ,所以时间复杂度为O(n^2)
    • 稳定性:不稳定 原因就在于交换部分会打破相对顺序
//选择排序
void Select_Sort(int *list,int count)
{
    int min,i,j;
    for(i=0;i<count;i++)
    {
        min = i;
        for(j=i+1;j<count;j++)
        {
            if(list[min]>list[j])
            {
                min = j;
            }
        }
        if(min!=i)
        {
            swap(list[i],list[min]);
        }
    }
}
  • 堆排序
    • 什么是堆?
      • 堆是一棵完全二叉树,而且满足任何一个非叶结点的值都不大于(或不小于)其左右孩子结点的值。
        • 如果是每个结点的值都不小于它的左右孩子结点的值,则称为大顶堆。
        • 如果是每个结点的值都不大于它的左右孩子结点的值,则称为小顶堆。
    • 什么是堆排序?
      • 我们知道对于一个堆来说,它的根结点是整个堆中所有结点的值的最大值(大顶堆)或者最小值(小顶堆)。所以堆排序的思想就是每次将无序序列调节成一个堆,然后从堆中选择堆顶元素的值,这个值加入有序序列,无序序列减少一个,再反复调节无序序列,直到所有关键字都加入到有序序列。
      • 时间复杂度:
        堆排序的总时间可以分为①建堆部分+②n-1次向下调整堆
        堆排序的时间复杂度为O(n)+O(nlog2n)=O(nlog2n)
      • 堆排序不稳定
     //调整为一个堆
void Heap_Adjust(int *list,int s,int m)
{
    int temp = list[s];
    for(int j=2*s+1;j<=m;j = 2*j+1)
    {
        if(list[j]<list[j+1]&&j<m)
        {
            j++;
        }
        if(temp>list[j])
            break;
        list[s] = list[j];
        s = j;
    }
    list[s] = temp;
}
 
//堆排序
void Heap_Sort(int *list,int len)
{
    //创建一个大顶堆
    for(int s = len/2-1;s>=0;s--)
    {
        Heap_Adjust(list,s,len-1);
    }
 
    //排序
    for(int i = len-1;i >= 1;i--)
    {
        swap(list[0],list[i]);
        Heap_Adjust(list,0,i-1);
    }
}

归并排序

  • 假定待排序表含有n个记录,则可以看成是n个有序的子表,每个子表长度为1,然后两两归并,得到 ⌈n/2⌉个长度为2或1的有序表;再两两归并,……如此重复,直到合并成一个长度为n的有序表为止,这种排序方法称为2-路归并排序。
  • 例如:49 38 65 97 76 13 27
    • ①首先将整个序列的每个关键字看成一个单独的有序的子序列
    • ②两两归并,49和38归并成{38 49} ,65和97归并成{65 97},76和13归并成{13 76},27没有归并对象
    • ③两两归并,{38 49}和{65 97}归并成{38 49 65 97},{13,76}和27归并成{13 27 76}
    • ④两两归并,{38 49 65 97}和{13 27 76}归并成{13 27 38 49 65 76 97}
  • 时间复杂度:O(nlog2n)
  • 空间复杂度:因为需要将这个待排序序列转存到一个数组,所以需要额外开辟大小为n的存储空间,即空间复杂度为O(n)
  • 稳定性:稳定
//归并排序
//将两个有序数组排序
void Merge(int *list,int start,int mid,int end)
{
    const int len1 = mid -start +1;
    const int len2 = end -mid;
    const int len = end - start +1;
    int i,j,k;
 
    int * front = (int *)malloc(sizeof(int)*len1);
    int * back = (int *)malloc(sizeof(int)*len2);
 
    for(i=0;i<len1;i++)
        front[i] = list[start+i];
    for(j=0;j<len2;j++)
        back[j] = list[mid+j+1];
 
    for(i=0,j=0,k=start;i<len1&&j<len2&&k<end;k++)
    {
        if(front[i]<back[j])
        {
            list[k] = front[i];
            i++;
        }else
        {
            list[k] = back[j];
            j++;
        }
    }
    while(i<len1)
    {
        list[k++] = front[i++];
    }
    while(j<len2)
    {
        list[k++] = back[j++];
    }
}       
 
void MSort(int *list,int start,int end)
{
    if(start<end)
    {
        int mid = (start+end)/2;
        MSort(list,0,mid);
        MSort(list,mid+1,end);
        Merge(list,start,mid,end);
    }
 
} 
 
void Merge_Sort(int *list,int count)
{
    MSort(list,0,count-1);
}

基数排序

  • 基数排序(也叫桶排序)是一种很特别的排序方法,它不是基于比较进行排序的,而是采用多关键字排序思想(即基于关键字各位的大小进行排序的),借助“分配”和“收集”两种操作对单逻辑关键字进行排序。基数排序又分为最高位优先(MSD)排序和最低位优先(LSD)排序。
  • 例子:53, 3, 542, 748, 14, 214, 154, 63, 616
    • 补充位数:053, 003, 542, 748, 014, 214, 154, 063, 616
    • 桶实际是一个队列,先进先出(从桶的上面进,下面出)
    • 关键字数量为n,关键字的位数为d,比如748 d=3,r为关键字的基的个数,就是组成关键字的数据的种类,比如十进制数字一共有0至9一共10个数字,即r=10
  • 空间复杂度:需要开辟关键字基的个数个队列,所以空间复杂度为O®
  • 时间复杂度:需要进行关键字位数d次"分配"和"收集",一次"分配"需要将n个关键字放进各个队列中,一次"收集"需要将r个桶都收集一遍。所以一次"分配"和一次"收集"时间复杂度为O(n+r)。d次就需要O(d(n+r))的时间复杂度。
  • 稳定性:由于是队列,先进先出的性质,所以在分配的时候是按照先后顺序分配,也就是稳定的,所以收集的时候也是保持稳定的。即基数排序是稳定的排序算法。
* #define MAX 20
#define BASE 10
 
void Radix_Sort(int *a, int n) {
  int i, b[MAX], m = a[0], exp = 1;
 
  for (i = 1; i < n; i++) {
    if (a[i] > m) {
      m = a[i];
    }
  }
 
  while (m / exp > 0) {
    int bucket[BASE] = { 0 };
 
    for (i = 0; i < n; i++) {
      bucket[(a[i] / exp) % BASE]++;
    }
 
    for (i = 1; i < BASE; i++) {
      bucket[i] += bucket[i - 1];
    }
 
    for (i = n - 1; i >= 0; i--) {
      b[--bucket[(a[i] / exp) % BASE]] = a[i];
    }
 
    for (i = 0; i < n; i++) {
      a[i] = b[i];
    }
 
    exp *= BASE;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值