简单排序的Java实现与效率分析

来源:互联网 发布:淘宝客活动报名技巧 编辑:程序博客网 时间:2024/06/04 20:06
简单排序应该是编程中最基本的,本次分析的简单排序包括冒泡排序、选择排序、插入排序。

首先我们准备一个要排序的数组,当然还有一些方法,基本如下:

[java] view plaincopy
  1. private int[] sortInts;//排序数组  
  2.   
  3. // 初始化sortInts  
  4. public SimpleSorts(int[] sortInts) {  
  5.     this.sortInts = sortInts;  
  6. }  
  7.   
  8. // getter  
  9. public int[] getSortInts() {  
  10.     return sortInts;  
  11. }  
  12.   
  13. // setter  
  14. public void setSortInts(int[] sortInts) {  
  15.     this.sortInts = sortInts;  
  16. }  
  17.   
  18. // 显示数组  
  19. public void display() {  
  20.     System.out.println("数组:" + Arrays.toString(sortInts));  
  21. }  
  22.   
  23. // 交换元素  
  24. public void exchange(int first, int last) {  
  25.     int temp = sortInts[first];  
  26.     sortInts[first] = sortInts[last];  
  27.     sortInts[last] = temp;  
  28. }  

冒泡排序是最简单的排序方法,基本上每个程序员都能不假思索,信手拈来。其算法简单,基本逻辑如下:
1.取数组第一个数为基数,并将数组最后一个数标记为标志数。
2.比较基数右边的数,如果基数大于此数,那么就交换两个数,否则不处理。
3.令基数右边的数为新基数,重复2-3步,直到新基数为标志数为止(此数需执行2-3过程)。
4.取数组第一个数为基数,,将原标志数左边的数标记为新标志数,重复2-4步,直到新标志数为第一个数为止(此数不执行2-4过程)。
其代码如下:
[java] view plaincopy
  1. // 冒泡排序  
  2. public void bubbleSort() {  
  3.     for (int i = sortInts.length - 1; i > 0; i--) { // 元素遍历  
  4.         for (int j = 0; j < i; j++) { // 元素比较,比i大的已经排好  
  5.             if (sortInts[j] > sortInts[j + 1]) // 比较大小,交换  
  6.             {  
  7.                 exchange(j, j + 1);  
  8.             }  
  9.         }  
  10.     }  
  11. }  

选择排序是冒泡排序的优化,虽然比较次数上没有改变,但在交换次数上大大减少。其逻辑如下:
1.在数组取第一个数为基数,令标识位等于基数的下标。
2.依次比较基数右边的数,如果此数小于标识位所指向的数,则令标识位等于此数的下标,直到数组最后一个为止(此数也要比较)。
3.交换基数与标志位所指向的数。
4.取基数右边的数为新基数,令标志数等于新基数的下标,重复2-4过程,直到数组倒数第二个数(此数要比较)。
其代码如下:
[java] view plaincopy
  1. // 选择排序  
  2. public void selectSort() {  
  3.     for (int i = 0; i < sortInts.length; i++)// 元素遍历  
  4.     {  
  5.         int temp = i;  
  6.         for (int j = i + 1; j < sortInts.length; j++)// 元素比较  
  7.         {  
  8.             if (sortInts[temp] > sortInts[j])// 比较大小,缓存下标  
  9.             {  
  10.                 temp = j;  
  11.             }  
  12.         }  
  13.         if (temp != i)// 若下标有变,交换  
  14.         {  
  15.             exchange(i, temp);  
  16.         }  
  17.     }  
  18. }  
插入排序可以说是三者中最好的算法,尤其是在数据局部有序的情况下,其算法逻辑如下:
1.在数组中取第二个数为基数,令缓冲数等于基数值。
2.依次比较基数左边的数,若此数大于基数,则将此数右移(令此数右边的数等于此数。)直到有数小于基数为值(此数不要移动)。
3.令数组最后右移的数等于缓冲数。
4.取数组第三个数为新基数,令缓冲数等于基数值,重复2-4过程,直到最后一个数(此数需执行243过程)。
其代码如下:
[java] view plaincopy
  1. //插入排序  
  2. public void InsertSort()  
  3. {  
  4.     for (int i = 1; i < sortInts.length; i++) //元素遍历  
  5.     {  
  6.         int temp =  sortInts[i];  
  7.         int j = i;  
  8.         while(j > 0 && temp < sortInts[j-1]) //元素比较,直到比temp小,停止循环  
  9.         {  
  10.             sortInts[j] = sortInts[j-1];  
  11.             j--;  
  12.         }  
  13.         if( j != i)//若下标有变,交换  
  14.         {  
  15.             sortInts[j] = temp;//交换停止处元素与sortInts[i]  
  16.         }  
  17.     }  
  18. }  

从上面看,好像插入排序的算法并不比冒泡和选择简单。但是请注意,如果数组局部有序(那怕只有两个数是有序的),情况就大大不同。例如数组{2,3,4,5,6,1,0},数组中2,3,4,5,6属于局部有序。插入排序时,基数为2,3,4,5,6时,都不用处理,当基数为1时,方法只需要移动6次,基数为0时,移动7次。选择排序需要交换7次,冒泡排序需要交换21次,而一次交换至少需要移动2次。

为了测试效率,我们可以添加一些代码,整个类的代码如下:

[java] view plaincopy
  1. import java.util.Arrays;  
  2. import java.util.Random;  
  3.   
  4. public class SimpleSorts {  
  5.     // 排序数组、比较次数、复制次数  
  6.     private int[] sortInts;  
  7.     private Long compareNum = 0L;  
  8.     private Long copyNum = 0L;  
  9.   
  10.     // 初始化sortInts、setter、  
  11.     public SimpleSorts(int[] sortInts) {  
  12.         this.sortInts = sortInts;  
  13.     }  
  14.   
  15.     // getter  
  16.     public int[] getSortInts() {  
  17.         return sortInts;  
  18.     }  
  19.   
  20.     // setter  
  21.     public void setSortInts(int[] sortInts) {  
  22.         compareNum = 0L;  
  23.         copyNum = 0L;  
  24.         this.sortInts = sortInts;  
  25.     }  
  26.   
  27.     // 显示数组  
  28.     public void display() {  
  29.         System.out.println("数组:" + Arrays.toString(sortInts));  
  30.     }  
  31.   
  32.     // 显示比较信息  
  33.     public void displayNum() {  
  34.         System.out.println("统计: = 比较 " + compareNum + " 次, 复制 = " + copyNum  
  35.                 + "次");  
  36.         System.out.println();  
  37.     }  
  38.   
  39.     // 复制次数  
  40.     public void addCopyNum() {  
  41.         copyNum++;  
  42.     }  
  43.   
  44.     // 比较次数  
  45.     public void addCompareNum() {  
  46.         compareNum++;  
  47.     }  
  48.   
  49.     // 交换元素  
  50.     public void exchange(int first, int last) {  
  51.         int temp = sortInts[first];  
  52.         addCopyNum();  
  53.         sortInts[first] = sortInts[last];  
  54.         addCopyNum();  
  55.         sortInts[last] = temp;  
  56.         addCopyNum();  
  57.     }  
  58.   
  59.     // 冒泡排序  
  60.     public void bubbleSort() {  
  61.         for (int i = sortInts.length - 1; i > 0; i--) { // 元素遍历  
  62.             for (int j = 0; j < i; j++) { // 元素比较,比i大的已经排好  
  63.                 if (sortInts[j] > sortInts[j + 1]) // 比较大小,交换  
  64.                 {  
  65.                     exchange(j, j + 1);  
  66.                 }  
  67.                 addCompareNum();  
  68.             }  
  69.         }  
  70.     }  
  71.   
  72.     // 选择排序  
  73.     public void selectSort() {  
  74.         for (int i = 0; i < sortInts.length; i++)// 元素遍历  
  75.         {  
  76.             int temp = i;  
  77.             for (int j = i + 1; j < sortInts.length; j++)// 元素比较  
  78.             {  
  79.                 if (sortInts[temp] > sortInts[j])// 比较大小,缓存下标  
  80.                 {  
  81.                     temp = j;  
  82.                 }  
  83.                 addCompareNum();  
  84.             }  
  85.             if (temp != i)// 若下标有变,交换  
  86.             {  
  87.                 exchange(i, temp);  
  88.             }  
  89.         }  
  90.     }  
  91.   
  92.     // 插入排序  
  93.     public void InsertSort()// 元素遍历  
  94.     {  
  95.         for (int i = 1; i < sortInts.length; i++) {  
  96.             int temp = sortInts[i];  
  97.             addCopyNum();  
  98.             int j = i;  
  99.             addCompareNum();  
  100.             while (j > 0 && temp < sortInts[j - 1]) // 元素比较,直到比temp小,停止循环  
  101.             {  
  102.                 if (compareNum != 1) {  
  103.                     addCompareNum();  
  104.                 }  
  105.                 sortInts[j] = sortInts[j - 1];  
  106.                 addCopyNum();  
  107.                 j--;  
  108.             }  
  109.             if (j != i)// 若下标有变,交换  
  110.             {  
  111.                 sortInts[j] = temp;// 交换停止处元素与sortInts[i]  
  112.                 addCopyNum();  
  113.             }  
  114.         }  
  115.     }  
  116.   
  117.     // 产生随机数组  
  118.     public static int[] randomInts(int initNum) {  
  119.         int[] ri = new int[initNum];  
  120.         for (int i = 0; i < ri.length; i++) {  
  121.             Random rd = new Random();  
  122.             ri[i] = rd.nextInt(10000);  
  123.         }  
  124.         return ri;  
  125.     }  
  126.   
  127.     // 主方法  
  128.     public static void main(String[] args) {  
  129.         int[] a = randomInts(1000);  
  130.         int[] b = a.clone();  
  131.         int[] c = a.clone();  
  132.   
  133.         // 冒泡  
  134.         SimpleSorts ss = new SimpleSorts(a);  
  135.         ss.bubbleSort();  
  136.         ss.displayNum();  
  137.   
  138.         // 选择  
  139.         ss.setSortInts(b);  
  140.         ss.selectSort();  
  141.         ss.displayNum();  
  142.   
  143.         // 插入  
  144.         ss.setSortInts(c);  
  145.         ss.InsertSort();  
  146.         ss.displayNum();  
  147.     }  
  148. }  

当测试数为10时,结果如下:

[plain] view plaincopy
  1. 统计: 比较 = 45 次, 复制 = 66次  
  2. 统计: 比较 = 45 次, 复制 = 24次  
  3. 统计: 比较 = 31 次, 复制 = 36次  

当测试数为100时:结果如下:

[plain] view plaincopy
  1. 统计: 比较 = 4950 次, 复制 = 6489次  
  2. 统计: 比较 = 4950 次, 复制 = 291次  
  3. 统计: 比较 = 2261 次, 复制 = 2356次  

当测试数为1000时:结果如下:

[java] view plaincopy
  1. 统计: 比较 = 499500 次, 复制 = 773589次  
  2. 统计: 比较 = 499500 次, 复制 = 2991次  
  3. 统计: 比较 = 258861 次, 复制 = 259853次  

当测试数为10000时:结果如下:

[java] view plaincopy
  1. 统计: = 比较 49995000 次, 复制 = 74298648次  
  2. 统计: = 比较 49995000 次, 复制 = 29979次  
  3. 统计: = 比较 24776215 次, 复制 = 24786200次  
通过比较我们可以看出,大数据下冒泡排序效率最低,选择排序虽然移动次数最少,但是比较次数高,而插入排序是三者中综合效率最好的。
参考《Java数据结构和算法》一书,冒泡排序交换和比较操作次数为 N*(N-1)/2,是与N2成正比(记作O(N2));选择排序交换虽然为O(N),但是比较时间与冒泡排序相同,还是O(N2);插入排序比较时间为 N*(N-1)/4。而移到次数与插入次数大致相同,两者近似O(N2),但是如果数据局部有有序,While循环基本为假,则算法所需的时间就能蜕变成O(N)。
简单排序一般适用于小数据量,大数据量排序还得高级排序(如快速排序等)。如果大家想深入研究可以参考《Java数据结构和算法》一书。
原创粉丝点击