算法算法提高之LeetCode刷题数据结构和算法分析

逻辑之美(2)_选择排序

2019-09-28  本文已影响0人  xiaofei_dev

开篇

上篇我们好好聊了聊冒泡排序,这篇我们来聊聊另一种初级排序算法——选择排序

正文

选择排序的算法思路同样很简单。还是数组为例,我们现在有个整数数组,要求将其中整数元素按值的大小从小到大排个序。选择排序的具体思路是这样:先从头遍历数组,找出其中值最小的那个元素,然后将其值同遍历区间最开始那个元素交换,如果值最小的元素恰是最开始那个元素,就自己跟自己交换值。第一遍遍历完成,数组中最小的值已经是数组第一个元素,此时数组第一个元素已部分有序,将重新遍历的初始下标加一,开始下次遍历,如此循环,直至遍历区间内只剩一个元素,此时数组已整体有序。

来具象化捋一遍选择排序的逻辑:

``

    /*
    设现有无序数组 a = [40, 50, 20, 30, 10]
    其有序状态应为 a = [10, 20, 30, 40, 50]
    我们对其做下选择排序,具象展示如下:
数组下标:a[0]  a[1]  a[2]  a[3]  a[4]
初始值: 40   50   20    30   10  

第一次选择遍历过程: 40   50   20   30   10  (遍历区间值最小元素为a[4],
                    ↑                             ↑  与遍历区间初始下标a[0]交换值)
                   
第二次选择遍历过程: 10   50   20   30   40  (最小元素为a[2],与a[1]交换值,
                   ---     ↑       ↑                 下划线标示的元素已部分有序)

第三次选择遍历过程: 10   20   50   30   40  (最小元素为a[3],与a[2]交换值,
                   ----------      ↑      ↑          下划线标示的元素已部分有序)
                   
第四次选择遍历过程: 10   20   30   50   40  (最小元素为a[4],与a[3]交换值,
                   ------------------     ↑      ↑   下划线标示的元素已部分有序)
                   
第五次选择遍历过程: 10   20   30   40   50  (遍历区间内只剩一个元素了,
                   -------------------------     ↑↑   表明此时数组已整体有序)
                   
数组此时已整体有序: 10   20   30   40   50  (数组此时已整体有序)
                   --------------------------------
                    
*/

OK 逻辑捋的差不多了我们开始撸代码,以 Java 为例,我们先来撸个为整数数组选择排序的递归实现版本:

/**
     * @see: 选择排序的递归实现
     * @param array: 待排序数组,我们采用原地排序
     * @param start: 当次查找最小值(遍历区间)的初始下标,初次调用值应为0
     */
    public static void sortSelect(int[] array, int start){
        //递归结束条件,此时数组已整体有序
        if (start >= array.length - 1){
            return;
        }

        //先假定遍历区间第一个下标的值为最小值;
        int minValueIndex = start;
        //开始遍历特定数组区间,将区间内最小值交换到区间初始位置
        for (int i = start + 1; i <= array.length - 1; i ++){
            if (array[i] < array[minValueIndex]){
                minValueIndex = i;
            }
        }
        //把最小的值交换到遍历区间初始下标位置
        int mid = array[start];
        array[start] = array[minValueIndex];
        array[minValueIndex] = mid;
        //递归开始遍历下个遍历区间
        sortSelect(array, start + 1);
    }

我们在实际生产环境中写排序算法肯定不能用递归,在 Java 中递归的函数调用栈太深会致开销甚大,上面主要是为便于理解来的初级版本,我们来优化成非递归版本,即双层嵌套版本:

/**
     * @see: 选择排序的非递归实现,即双层嵌套实现
     * @param array: 待排序数组,我们采用原地排序
     */
    public static void sortSelect(int[] array){
        //递归结束条件,此时数组已整体有序
        for (int start = 0; start < array.length - 1; start ++){
            //先假定遍历区间第一个下标的值为最小值;
            int minValueIndex = start;
            for (int startInner = start + 1; startInner <= array.length - 1; startInner ++ ){
                if (array[startInner] < array[minValueIndex]){
                    minValueIndex = startInner;
                }
            }
            //把最小的值交换到遍历区间初始下标位置
            int mid = array[start];
            array[start] = array[minValueIndex];
            array[minValueIndex] = mid;
            //循环开始遍历下个遍历区间
        }
    }

两种实现代码撸下来,相信你已彻底掌握了选择排序算法。

尾巴

选择排序有两个特点:

  1. 运行时间和输入无关,其时间复杂度恒为 О(n²),即使你输入的数组本来就整体有序也是如此!
  2. 数据值交换(或言数据移动)是最少的,如上所示,一次遍历只有一次值交换,还有可能是自己跟自己交换,对比冒泡排序,你知道我在说什么!

选择排序需要的额外空间复杂度同冒泡排序,为О(1)

读者请自行发散思维把以上代码改成为 Comparable 数组排序的版本。

下篇我们聊插入排序。

完。

上一篇 下一篇

猜你喜欢

热点阅读