代码改变世界

[排序算法总结]冒泡排序、简单选择排序

2013-09-04 19:33  庸男勿扰  阅读(663)  评论(0编辑  收藏  举报

  排序是日常生活中最常见的操作,也是算法中最有趣的问题之一,排序问题可以描述为:

  输入:n个数<a1,a2,…,an>

  输出:输入序列的一个排列(即重新排序)<a1’,a2’,….,an’>,使得a1’<=a2’<=…<=an’。

  待排序的数也称为关键字(key)。

       本篇文章将列举出常见的排序算法,分别从思想、复杂度、稳定性等方面,总结其优劣和适用情形,并给出源码示例。

  要想解决排序问题,最直观也是最简单的做法就是两两比较,每次找出当前最大(最小)的,直至整个排序完成。这种算法有一个比较形象的的名字叫“冒泡法”。

一、冒泡排序BubbleSort):

  思想:

  冒泡排序的是第一次通过两两比较,找出a1-an中最大的数,并将其放在an的位置,然后第二次执行同样的操作,找出a1-an-1中最大的数,放在an-1的位置,如此下去,直至待排序集合只剩一个数。

  代码:

  输入:n个元素的数组A[0…n-1]

  输出:按非降序排列的数组A[0…n-1](下同)

 1 void BubbleSort(int A[],int length)
 2 
 3 {
 4 
 5      for(int i=1; i<length; i++)
 6 
 7      {
 8 
 9              for(int j=1; j<=length-i; j++)
10 
11              {
12 
13                      if(A[j-1]>A[j])
14 
15                      {
16 
17                           swap(A[j-1],A[j]);                    
18 
19                      }       
20 
21              }       
22 
23      }    
24 
25 }
View Code

  复杂度:

  不难看出,排序的基本运算是比较操作,当待排序数组长度为n时,比较操作执行的次数是(n-1)+(n-2)+…+1即n(n-1)/2,故其时间复杂度是O(n2)的。至于空间复杂度,冒泡排序无需申请额外的空间。

  稳定性:

  由算法过程不难看出,冒泡排序不会打乱相同数的原顺序,因此是稳定的。

  改进:

  算法的外层循环是扫描次数,内层循环是不停对当前待排序数组进行排序,当执行到某次扫描后,数组可能已经是有序的,但是外层循环还是会继续执行,因此,改进的动机便是,当数组已是有序时,即退出排序。判断是否有序的依据便是是否执行了交换操作。因此,改进后的代码如下:

 1 void BubbleSort(int A[],int length)
 2 
 3 {
 4 
 5 bool sorted = false;
 6 
 7      for(int i=1; i<length&&!sorted; i++)
 8 
 9      {       sorted = true;
10 
11              for(int j=1; j<=length-i; j++)
12 
13              {
14 
15                      if(A[j-1]>A[j])
16 
17                      {
18 
19                           swap(A[j-1],A[j]);  
20 
21                                                    sorted = false;                 
22 
23                      }       
24 
25              }       
26 
27      }    
28 
29 }
View Code

二、简单选择排序(SelectionSort):

  思想:

  简单选择排序是对冒泡排序的一种改进,冒泡排序每次都会执行两两交换,不停“吐泡”,目的就是为了让最大的数,能够到当前的最后位置。这中间执行了很多多余的交换操作。简单选择排序是每次找出当前最大的数,放到数组最后,即第一次执行时,找出a1-an中最大的数放到an的位置,在找出a1-an-1中最大的数,放到an-1的位置,直至待排序集合只剩一个数。

  不难看出,以上算法过程是一个归纳的过程,因此选择排序也很容易转换成递归版本的算法。

  代码:

  非递归版本:

 1 //返回最大的那个数的位置
 2 
 3 int SelectMaxKey(int A[],int offset)
 4 
 5 {
 6 
 7      int MAX = INT_MIN;
 8 
 9      int pos=0;
10 
11      for(int i=0; i<=offset; i++)
12 
13      {
14 
15           if(A[i]>MAX){
16 
17                MAX = A[i];
18 
19                pos = i;            
20 
21           }
22 
23      }
24 
25      return pos;    
26 
27 }
28 
29  
30 
31 void SelectionSort(int A[],int length)
32 
33 {
34 
35      int temp;
36 
37      for(int i=1; i<length; i++)
38 
39      {
40 
41            temp = SelectMaxKey(A,length-i);
42 
43            if(temp!=length-i)
44 
45                swap(A[temp],A[length-i]);         
46 
47      }    
48 
49 }
View Code

  递归版本

 1 void SelectionSortRec(int A[],int offset,int length)
 2 
 3 {
 4 
 5      int k;
 6 
 7      if(offset<length-1)
 8 
 9      {
10 
11           k = offset;
12 
13           for(int j=offset+1; j<length; j++)
14 
15           {
16 
17                   if(A[j]<A[k]) k = j;       
18 
19           }                  
20 
21           if(k!=offset)
22 
23                   swap(A[k],A[offset]);
24 
25           SelectionSortRec(A,offset+1,length);
26 
27      }
28 
29 }
View Code

  复杂度:

  简单选择排序是对冒泡排序的一种改进,减少的只是交换操作,比较操作并没有减少,因此,其时间复杂度也是O(n2),选择排序同样无需申请额外空间。

  稳定性:

  当当前排序数组中有两个最大的数时,由于在找最大数时,是使用的if(A[i]>MAX)判断,因此,总会把先遇到的那个最大的数放到数组最后,因此打乱了原来的顺序,从这个角度上看,简单选择排序是不稳定的。当然,如果判断使用if(A[i]>=MAX),那么每次总是找最大的数中的最后一个放到数组最后,此时并没有打乱原顺序,此时又可以说是稳定的。

  接下来《排序算法总结》系列将带来插入排序和归并排序讲解。以上如有任何错误或表述不清楚的,欢迎指出。