10.1 排序算法

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

  • 冒泡排序

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

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

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

  第三轮:…

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

  • 直接插入排序

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

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

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

  第三轮:……

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

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

10.1.1 选择排序

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

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

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

  第三轮:…

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

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

public class TestSelect{

    public static void main(String[] args) {

        int[] array = {65,34,74,24,89,1,58};

        System.out.println("排序前的数组:");

        for (int i = 0; i < array.length; i++) {

            System.out.print(array[i]+"  ");

        }

        System.out.println();

        selectSort(array);//使用直接选择排序

        System.out.println("排序后的数组:");

        for (int i = 0; i < array.length; i++) {

            System.out.print(array[i]+"  ");

        }

        System.out.println();

    }

    //直接选择排序

    public static void selectSort(int[] a) {

        for(int i = 0; i < a.length-1; i++){

            int k = i;

            //选择待排序数列中最小数的下标

            for(int j = i; j < a.length; j++){

                if(a[k] > a[j]){

                k = j;

            }

        }

        if(k != i){

            int temp = a[i];

            a[i] = a[k];

            a[k] = temp;

        }

        }

    }

}
copy

  编译、运行程序,运行结果如图10.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的后面。再按此方法对前后两部分数据分别进行一轮排序,这样递归下去,达到排序的目的。

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

public class TestQuick

{

    public static void main(String[] ary)

    {

        int[] array = {65,34,58,89,74,1,24};

        System.out.println("排序前的数组:");

        for (int i = 0; i < array.length; i++) {

            System.out.print(array[i]+"  ");

        }

        System.out.println();

        sort(array, 0, array.length - 1);//使用快速排序

        System.out.println("排序后的数组:");

        for (int i = 0; i < array.length; i++) {

            System.out.print(array[i]+"  ");

        }

        System.out.println();

    }

    //进行一轮排序,array为排序数组,i,j为排序起始和结束位置,返回关键数据排序后索引

    private static int sortUnit(int[] array, int i, int j)

    {

        int key = array[i];

        while (i < j)

        {

            //从后向前搜索比key小的值,比key小的放左边

            while(array[j] >= key && j > i)

            j--;

            //交换

            array[i] = array[j];

            //从前向后搜索比key大的值,比key大的放右边

            while (array[i] <= key && j > i)

                i++;

            //交换

            array[j] = array[i];

        }

        //当i=j时,一轮排序结束

        array[j] = key;

        //返回关键数据排序后索引

        return j;

    }

    //快速排序,递归调用

    public static void sort(int[] array, int low, int high)

    {

        if (low >= high)

        {

            return;

        }

        //完成一轮排序

        int index = sortUnit(array, low, high);

        //对左边部分进行排序

        sort(array, low, index - 1);

        //对右边部分进行排序

        sort(array, index + 1, high);

    }

}
copy

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

图10.2 快速排序