10.1 排序算法

  在学习《Java语言基础与面向对象编程实践》时,介绍了两种排序算法,一种是冒泡排序,另一种是直接插入排序,现在先回顾一下冒泡排序和直接插入排序。

  • 冒泡排序

  冒泡排序就是依次比较相邻的两个数,将小数放在前面,大数放在后面。

  第一轮:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,依次类推,直至比较最后两个数,将小数放前,大数放后。至此第一轮结束,将最大的数放到了最后。

  第二轮:仍从第1对数开始比较,将小数放前,大数放后,一直比较到倒数第2个数(倒数第一的位置上已经是最大的数),第二轮结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。

  第三轮:…

  按此规律操作,直至最终完成排序。由于在排序过程中总是小数往前放,大数往后放,类似于小的气泡往上升,所以称作冒泡排序。

  • 直接插入排序

  直接插入排序存在两个表,一个是有序表,另一个是无序表。每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。

  第一轮:比较前两个数,然后按顺序插入到有序表中,剩下的数仍在无序表中。

  第二轮:把无序表中剩下的第一个数据与有序表的有序数列进行比较,然后把这个数插入到合适位置。

  第三轮:……

  按此规律操作,直至无序表中的数全部插入到有序表,完成排序。

  接下来,将再学习两种排序算法,一种是选择排序,另一种是快速排序。

10.1.1 选择排序

  选择排序是常用的一种排序方式,接下来以直接选择排序算法为例介绍选择排序。直接选择排序算法思路的核心是:NN为需要排列的元素个数)从1开始,每一轮从待排数列中选择第N小(或大)的数放到排序列表的第N个位置。

  第一轮:从全部待排序数列中选出最小的数,然后与第1个位置的数进行交换。

  每二轮:从第2个位置到最后一个位置中(待排序数列)选出最小的数,然后与第二个位置的数进行交换。

  第三轮:…

  按此规律操作,N-1轮以后,待排序数列就变成从小到大进行排序的数列了。

  使用直接选择排序算法进行排序,具体代码如下:

  1. public class TestSelect{
  2. public static void main(String[] args) {
  3. int[] array = {65,34,74,24,89,1,58};
  4. System.out.println("排序前的数组:");
  5. for (int i = 0; i < array.length; i++) {
  6. System.out.print(array[i]+" ");
  7. }
  8. System.out.println();
  9. selectSort(array);//使用直接选择排序
  10. System.out.println("排序后的数组:");
  11. for (int i = 0; i < array.length; i++) {
  12. System.out.print(array[i]+" ");
  13. }
  14. System.out.println();
  15. }
  16. //直接选择排序
  17. public static void selectSort(int[] a) {
  18. for(int i = 0; i < a.length-1; i++){
  19. int k = i;
  20. //选择待排序数列中最小数的下标
  21. for(int j = i; j < a.length; j++){
  22. if(a[k] > a[j]){
  23. k = j;
  24. }
  25. }
  26. if(k != i){
  27. int temp = a[i];
  28. a[i] = a[k];
  29. a[k] = temp;
  30. }
  31. }
  32. }
  33. }

  编译、运行程序,运行结果如图10.1所示。

10.1 排序算法 - 图1


图10.1 直接选择排序

10.1.2 快速排序

  快速排序是对冒泡排序的一种改进,它的基本思想是通过一轮排序将要排序的数据分割成独立的两部分,其中一部分的所有数据比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行一轮排序,整个排序轮次递归进行,使整个数据变成一个有序序列。 每轮排序的具体算法如下:

  (1)设置两个变量i、j(均为下标变量),排序开始的时候i=0,j=N-1。

  (2)以第一个据元素作为关键数据,赋值给key,即key=a[0]。

  (3)从j开始向前搜索j—,即由后开始向前搜索,找到第一个小于key的值a[j],a[i]与a[j]交换。

  (4)从i开始向后搜索i++,即由前开始向后搜索,找到第一个大于key的a[i],a[i]与a[j]交换。

  重复步骤(3)和步骤(4),直到i=j,则将小于key的数全部都放在key前,将大于key的数都放在了key后。

  快速排序的算法在理解上还是有一定难度的,接下来通过执行一轮快速排序算法来对array{58,34,65,89,74,1,24}数组进行排序。

  array数组排序前的序列如下:

58 34 65 89 74 1 24

  选择初始关键数据key=58(注意关键key保持不变,总是和key进行比较,最后的目的就是把key放在中间,小的放前面,大的放后面)。

  第一次交换:从最后的数24开始搜索,找到第一个小于58的数24(此时j=6),将58和24进行交换,交换后结果如下:

24 34 65 89 74 1 58

  第二次交换:从第一个数24开始搜索,找到第一个大于58的数65(此时i=2),将58和65进行交换,交换后结果如下:

24 34 58 89 74 1 65

  第三次交换:从最后的数65开始搜索,找到第一个小于58的数1(此时j=5),将58和1进行交换,交换后结果如下:

24 34 1 89 74 58 65

  第四次交换:从第一个数24开始搜索,找到第一个大于58的数89(此时i=3),将58和89进行交换,交换后结果如下:

24 34 1 58 74 89 65

  再往下执行,在没有交换数据前即出现了ij的数值都为4的情况,满足第一轮退出条件。观察排序后的数序,发现小于58的数都排到了58的前面,大于58的数都排到了58的后面。再按此方法对前后两部分数据分别进行一轮排序,这样递归下去,达到排序的目的。

  使用快速排序的代码如下(请大家认真阅读注释,理解代码的含义):

  1. public class TestQuick
  2. {
  3. public static void main(String[] ary)
  4. {
  5. int[] array = {65,34,58,89,74,1,24};
  6. System.out.println("排序前的数组:");
  7. for (int i = 0; i < array.length; i++) {
  8. System.out.print(array[i]+" ");
  9. }
  10. System.out.println();
  11. sort(array, 0, array.length - 1);//使用快速排序
  12. System.out.println("排序后的数组:");
  13. for (int i = 0; i < array.length; i++) {
  14. System.out.print(array[i]+" ");
  15. }
  16. System.out.println();
  17. }
  18. //进行一轮排序,array为排序数组,i,j为排序起始和结束位置,返回关键数据排序后索引
  19. private static int sortUnit(int[] array, int i, int j)
  20. {
  21. int key = array[i];
  22. while (i < j)
  23. {
  24. //从后向前搜索比key小的值,比key小的放左边
  25. while(array[j] >= key && j > i)
  26. j--;
  27. //交换
  28. array[i] = array[j];
  29. //从前向后搜索比key大的值,比key大的放右边
  30. while (array[i] <= key && j > i)
  31. i++;
  32. //交换
  33. array[j] = array[i];
  34. }
  35. //当i=j时,一轮排序结束
  36. array[j] = key;
  37. //返回关键数据排序后索引
  38. return j;
  39. }
  40. //快速排序,递归调用
  41. public static void sort(int[] array, int low, int high)
  42. {
  43. if (low >= high)
  44. {
  45. return;
  46. }
  47. //完成一轮排序
  48. int index = sortUnit(array, low, high);
  49. //对左边部分进行排序
  50. sort(array, low, index - 1);
  51. //对右边部分进行排序
  52. sort(array, index + 1, high);
  53. }
  54. }

  编译、运行程序,运行结果如图10.2所示。

10.1 排序算法 - 图2


图10.2 快速排序