排序算法之选择排序

选择排序(Selection Sort)的基本思想是:每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子文件的最后,直到全部记录排序完毕。常用的选择排序方法有简单选择排序(Simple Selection Sort)和堆排序(Heap Sort)。

1.        简单选择排序

(1)      简单选择排序思想

n个记录可通过n-1趟简单选择排序得出结果。初始状态为记录分无序区和有序区,无序区为整个记录序列,有序区为空。第一趟排序从整个无序区中选出一个关键字最小的记录,和第一个记录交换位置,此时第一个记录有序,有序区记录个数为1,无序区记录个数减去1。第二趟排序再从无序区中选出一个关键字最小的记录,将其与第二个记录(无序区第一个数)交换,此时前两个数有序,为有序区。再重复前面步骤,第i趟排序是将第i个元素到第n个元素中的关键字最小的记录与第i个记录交换。进过n-1趟排序可以得出有序结果。

(2)      简单排序算法

[cpp]  view plain copy
  1. void select_sort(sqlist *s)  
  2. {  
  3.     int         i=0, j=0, k=0;  
  4.   
  5.     for (i = 1; i < s->len; ++i) {  
  6.         j = i;  
  7.         for (k = i+1; k <= s->len; ++k) {  /* 寻找关键字最小的记录,j记录其位置 */  
  8.             if (s->data[k].key < s->data[j].key) {  
  9.                 j = k;  
  10.             }  
  11.         }  
  12.   
  13.         if (i != j) {                /* 当关键字最小记录不是无序区首个记录时交换数据 */  
  14.             s->data[0] = s->data[i];  
  15.             s->data[i] = s->data[j];  
  16.             s->data[j] = s->data[0];  
  17.         }  
  18.         output_list(*s);/*测试用*/  
  19.     }  
  20. }  

(3)      算法分析

1)       关键字比较次数:无论文件初始状态如何,在第i趟排序中选出最小关键字的记录,需做n-i次比较,因此,总的比较次数为:n(n-1)/2= 0(n2)。

2)       记录的移动次数:当初始文件为正序时,移动次数为0,文件初态为反序时,每趟排序均要执行交换操作,总的移动次数取最大值3(n-1)。直接选择排序的平均时间复杂度为0(n2)。

3)       直接选择排序是不稳定的。
2.        堆排序

(1)     堆排序定义

n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质): ki≤K2i且ki≤K2i+1  或 Ki≥K2i且ki≥K2i+1(1≤i≤  ),若将此序列所存储的一位数组R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。堆顶元素(完全二叉树的根)必为序列中的最大或最小值,堆顶元素最大称为大根堆,最小称为小根堆。若在输出堆顶元素的最小值之后,使得剩余的n-1元素序列又重新建成一个堆,得到n个元素的次小值,如此反复,便能得到一个有序序列,这个过程称之为堆排序。

(2)     堆排序的思想

堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。用大根堆排序的基本思想:

1)       先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区。

2)       再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key。

3)       由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。

4)       重复2)、3),直到无序区只有一个元素为止,此时排序完成。

上面说到将无序序列建成一个大根堆,但如何来建一个大根堆呢?从一个无序序列建堆的过程就是一个反复筛选的过程,若将此序列看成是一个完全二叉树,则最后一个非终端结点是第个元素,由此筛选只需要从第个元素开始,从到第1个元素依次与其孩子结点比较,如果结点记录关键字小于其孩子结点,则发生交换。堆建成之后,根结点为最大值。

(3)      堆排序算法

[cpp]  view plain copy
  1. void adjust_heap(sqlist *h, int s, int m)  
  2. {  
  3.     datatype        t;  
  4.     int         j;  
  5.       
  6.     t = h->data[s];  
  7.     for (j = 2*s; j <= m; j *= 2) {  
  8.         if (j<m && h->data[j].key < h->data[j+1].key)  
  9.             ++j;  
  10.         if (t.key > h->data[j].key)  
  11.             break;  
  12.         h->data[s] = h->data[j];  
  13.         s = j;  
  14.     }  
  15.     h->data[s] = t;  
  16. }  
[cpp]  view plain copy
  1. void heap_sort(sqlist *h)  
  2. {  
  3.     datatype        t;  
  4.     int         i;  
  5.       
  6.     for (i = h->len/2; i > 0; --i) {         /* 将初始序列建成一个大顶堆 */  
  7.         adjust_heap(h, i, h->len);  
  8.     }  
  9.   
  10.     for (i = h->len; i > 1; --i) {  
  11.         output_list(*h);                    /* 测试用 */  
  12.         t = h->data[1];                     /* 交换堆顶和无序区最后一个记录 */  
  13.         h->data[1] = h->data[i];  
  14.         h->data[i] = t;  
  15.         adjust_heap(h, 1, i-1);             /* 交换后重新建堆 */  
  16.     }  
  17.     output_list(*h);                         /* 测试用 */  
  18. }  

(4)      算法分析

     堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成。堆排序的最坏时间复杂度为O(nlgn)。堆排序的平均性能较接近于最坏性能。 由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。堆排序是就地排序,辅助空间为O(1),它是不稳定的排序方法。

3.        堆排序和简单选择排序区别

直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。堆排序可通过树形结构保存部分比较结果,可减少比较次数。

有序区和无序区:在任何时刻,堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止。

在输入数据:34 78 56 12 22 66 89 77 的情况下,两种排序的排序过程如下:(红色部分为有序区

堆排序:

89 78 6677 22 34 56 12      对初始序列建大顶堆

78 77 6612 22 34 56 89      每一趟排序将堆顶元素和无序区最后一个元素交换,之后再建堆

77 56 6612 22 34 78 89

66 56 3412 22 77 78 89

56 22 3412 66 77 78 89

34 22 12 56 66 77 78 89

22 12 34 56 66 77 78 89

12 22 34 56 66 77 78 89

简单选择排序:

12 78 56 34 22 66 89 77      每一趟排序选择无序区之后最小元素和无序区第一个元素交换

12 22 56 34 78 66 89 77

12 22 34 56 78 66 89 77

12 22 34 56 78 66 89 77

12 22 34 56 66 78 89 77

12 22 34 56 66 77 89 78

12 22 34 56 66 77 78 89


4.        算法实现源码  

http://download.csdn.net/detail/algorithm_only/3861687


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值