数据结构与算法(Java版) | 详解十大经典排序算法之一:选择排序

接下来,我给大家来讲解第二种排序算法,即选择排序。

基本介绍

首先,我们来看下选择排序的基本介绍。

选择排序,它同样也属于内部排序,具体而言,就是从欲排序的数据中,按照指定的规则选出某一元素,然后再依照规定交换位置,以便达到排序的目的。

基本思想

简单认识选择排序之后,接下来我就要来给大家详细说说它的排序思想了。

选择排序的基本思想是这样子的,即第一次从整个无序序列里面(即arr[0]~arr[n - 1])选取一个最小值,然后让它与arr[0]交换;第二次,从arr[1]~arr[n - 1]中选取一个最小值,然后让它与arr[1]交换;第三次,从arr[2]~arr[n - 1]中选取一个最小值,然后让它与arr[2]交换;依此类推,第i次,那就是从arr[i - 1]~arr[n - 1]中选取一个最小值,然后再让它与arr[i - 1]交换了;至于第n - 1次嘛,则就是从arr[n- 2]~arr[n - 1]中选取一个最小值,然后再让它与arr[n - 2]交换了。很明显,最终总共须通过n - 1次,我们才可得到一个按排序码从小到大排列的有序序列。

知晓以上选择排序的基本思想之后,我想大家应该还得注意以下两点。

  1. 在每一次找最小值的过程中,大家应该都得清楚地明白这一点,即数值之间的交换并不会时时刻刻发生,而是只有在找到最小值时才会发生。
  2. 其实,选择排序的基本思想说开了来看,无非就是先确定下来第一个最小值之后,再去确定第二个最小值,依此类推,直至最终得到一个按排序码从小到大排列的有序序列罢了。当然,前提是我们确实是要按从小到大的顺序来排序,如果说你想按照从大到小的顺序来排序,那么此时找最小值就应该得变成找最大值才行。

图解选择排序的完整过程

在大致了解选择排序的基本思想之后,接下来我可就要用图解的方式来为大家详细阐述选择排序的完整过程了。之所以这样做,是因为我觉得对于那些初次接触选择排序的同学来说,可能他都看不大懂以上我写的选择排序的基本思想到底是个啥意思,既然文字解释不清楚,那我就只好选择用图解的方式来为大家进行解释说明了,毕竟这样更直观,也更易理解。

说啊,目前有一个数组,它的初始状态是下面这样子的。

在这里插入图片描述

针对该数组,接下来我们是不是得来对其进行选择排序了啊!那如何对该数组进行选择排序呢?很简单,看我下面图解选择排序的整个过程就行,相信你看完,一定会对选择排序有更深的体会。

首先,我们来看一下第一轮排序,根据选择排序的基本思想可知,第一轮排序就是从整个数组里面找到一个最小值(即1),然后让它与第1个元素(即8)交换,大家能想象得到这一过程就像下图所示的这样吧!

在这里插入图片描述

也就是说,经过第一轮排序过后,数组里面最小的那个值就已然被我们找出来了,而且它还被我们放在了它应该在的正确位置。

然后,我们再来看一下第二轮排序,第二轮排序其实也很简单,无非就是从后面7个数里找一个最小值(即2),然后让它与第2个元素(即3)交换罢了。

在这里插入图片描述

接着,我们再来看一下第三轮排序,第三轮排序同样也很简单,就是从后面6个数里找一个最小值(即3),然后让它与第3个元素(即3)交换,只不过在这一轮排序中,最小值3已经处在第3个元素的位置上了,所以它并不需要交换,这点一定要请大家注意。

在这里插入图片描述

紧接着,我们再来看一下第四轮排序,第四轮排序不用我说,相信大家也都知道了吧,无非就是从后面5个数里找一个最小值(即4),然后让它与第4个元素(即8)交换嘛!

在这里插入图片描述

再接下来,我们就要来看一下第五轮排序了,第五轮排序同样也很简单,就是从后面4个数里找一个最小值(即5),然后让它与第5个元素(即7)交换。

在这里插入图片描述

再再接下来,我们就要来看一下第六轮排序了,第六轮排序同样也很简单,就是从后面3个数里找一个最小值(即6),然后让它与第6个元素(即8)交换。

在这里插入图片描述

最后,我们再来看一下第七轮排序,注意,这可是最后一轮排序了哟!同上,第七轮排序其实也很简单,就是从后面2个数里找一个最小值(即7),然后让它与第7个元素(即8)交换。

在这里插入图片描述

有同学可能心里犯嘀咕,这真的是最后一轮排序了嘛?还有没有第八轮排序?没有了啊,因为真的没有必要再进行第八轮排序了,你想啊,以上数组总共只有8个数据,我们已经确定下来了7个,那最后那个数是不是肯定就在它该在的位置上了啊,你说,对吧!

至此,选择排序的整个过程,我就以图解的形式给大家阐述明白了,相信大家看完我上面图解选择排序的完整过程之后,应该是对选择排序有了一个更为深刻的理解,如果你要还是不太理解,那你不就枉费了我的一片苦心嘛,毕竟画出上面的图解过程还是蛮耗费了我的一些时间和精力的。

既然现在大家都对选择排序有了一个比较深刻的认识,那下面咱们就来看一个选择排序的应用实例吧!希望通过该案例,大家都能自己敲出来用以实现选择排序的代码。

应用实例

说啊,草场有一群牛,它们的颜值分别是101、34、119、1,现请你对这群牛的颜值从低到高进行排序,而且只能使用选择排序。

在这里插入图片描述

以上需求大家都看明白了吧,还是挺简单的,无非就是对一个数组进行选择排序罢了。既然是对一个数组来进行选择排序,那下面咱们就按照以上图解选择排序的整个过程那样再来对该数组整一遍,以加深大家的印象。

思路分析

很明显,从以上案例中可知,我们是要对下面这样一个原始数组来进行选择排序。

在这里插入图片描述

首先,我们来看一下第一轮排序,根据选择排序的基本思想可知,第一轮排序就是从整个数组里面找到一个最小值(即1),然后让它与第1个元素(即101)交换,大家能想象得到这一过程就像下图所示的这样吧!

在这里插入图片描述

也就是说,经过第一轮排序过后,数组里面最小的那个值就已然被我们找出来了,而且它还被我们放在了它应该在的正确位置。至于数组里面那个最小值是怎么通过这轮排序来找到的,那就要在接下来的代码中再去给大家详细说了。

然后,我们再来看一下第二轮排序,第二轮排序其实也很简单,无非就是从后面3个数里找一个最小值(即34),然后让它与第2个元素(同样也是34)交换罢了。只不过在这一轮排序中,最小值34已经处在第2个元素的位置上了,所以它并不需要交换,这点一定要请大家注意。

在这里插入图片描述

最后,我们再来看一下第三轮排序,注意,这可是最后一轮排序了哟!同上,第三轮排序其实也很简单,就是从后面2个数里找一个最小值(即101),然后让它与第3个元素(即119)交换。

在这里插入图片描述

也就是说,经过三轮排序过后,我们就已然可以把前面3个最小的数确定下来了。那还有必要再进行第四轮排序吗?没有必要了,因为你想啊,以上数组总共只有4个数据,我们已经确定下来了3个,那最后那个数是不是肯定就在它该在的位置上了啊,你说,对吧!

通过以上又一次为大家图解选择排序的整个过程,相信大家应该能总结出如下两点说明吧!

  1. 选择排序一共得经历数组大小 - 1轮排序。
  2. 每一轮排序,其实又是一个循环。而且循环的规则大致是下面这样子的,即:
    1. 先假定当前数是最小数,当前数就是每一轮排序里面未排序序列中的第一个数。
    2. 然后让它和它后面的每个数依次进行比较,若发现有比它更小的数,则重新确定最小数,并记录下其下标。
    3. 当遍历到数组的最后时,我们就可以得到本轮排序中的最小数和其下标了。
    4. 最后,将确定好的最小数与当前数进行交换。

对于以上每一轮排序里面的循环规则,大家是不是看得有点蒙圈啊,就不知道我在讲些什么,嘿嘿🤭,不过这也挺正常的,初次接触选择排序的童鞋可能就是看不大懂,当然,要是你能看一遍就看懂,那说明你TM就是一个编程天才,假以时日,你必定能成就一番伟业。不过,话说回来,看不懂以上循环规则也没关系,等待会我在为大家编写选择排序的实现代码的过程中,再在代码中给大家细说吧,说完,你就能明白我说的是什么意思了。

注意,正如我上篇为大家编写冒泡排序的完整实现代码那样,这里我也会采用逐步推导的方法来为大家编写选择排序的完整实现代码,我相信,只有这样做,大家才会理解得更容易一点。

至此,相信大家应该对一个数组如何来进行选择排序有了一个大概的解决思路吧!虽然上面我都说得很清楚了,但不明白的我想还是会不明白,毕竟排序算法它本身就比较绕,一时半会理解不了也是很正常的一件事,因此你也不用太过于焦虑,而且就算你不明白,接下来我也会在代码中给你讲解清楚,嘿嘿😘,我是不是很贴心啊!

废话不多说了,接下来我就要采用逐步推导的方法来为大家编写选择排序的完整实现代码了,相信写完,大家印象一定会非常深刻!

代码实现

上面我已经说过,这里我会采用逐步推导的方法来为大家编写选择排序的完整实现代码,而之所以这样做,是因为我觉得只有这样做,大家理解起来才会更容易一点。

首先,我们来看一下第一轮排序,第一轮排序不用我说,想必大家应该都知道干了些什么事吧,无非就是找到无序数列中的最小数,然后再将其放在首位。

这里,我得提醒大家一点,就是当你遇到了一个复杂的算法问题时,你得具备这样一个极其重要的能力,即能将这个复杂的算法问题拆分成多个简单的问题,然后再对这多个简单的问题逐一进行解决,解决完毕后,再将结果进行汇总。否则的话,你要遇到一个复杂的算法问题,上来过后就去想那个最终的解决方案,那么往往你是很难想得到的。就拿上述案例来说,我们就不要先一上来就去思考怎么一次性就将选择排序的完整实现代码写出来了,而是应该去思考每一轮排序该怎样用代码来实现,例如第一轮排序,我们就要找到无序数列中的最小数,然后再将其放在首位,如此一来,问题是不是就被简化了啊,而且这样大家理解起来,包括记忆起来也都会轻松许多。

总之,遇到复杂的难解决的算法问题,你脑海中得有先简单再复杂这样一个思想钢印。

废话不多说,让我们直接来看第一轮排序的实现代码吧!

package com.meimeixia.sort;

import java.util.Arrays;

/**
 * @author liayun
 * @create 2024-03-20 10:12
 */
public class SelectSort {

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        System.out.println("排序前的数组:");
        System.out.println(Arrays.toString(arr));
        selectSort(arr);
    }

    /**
     *
     * @param arr:你只要给我传进来一个数组,那我就帮你进行选择排序
     */
    public static void selectSort(int[] arr) {
        /**
         * 第一轮排序。
         *
         * 经过我们上述分析,我想大家应该都知道第一轮排序就是要找到无序数列中的最小数,然后再将其放在首位了吧!
         */
        // 1. 先假定当前这一轮的最小数就是无序数列中的第一个数,当然,至于它到底是不是,那我们就不得而知了
        int minIndex = 0;
        int min = arr[minIndex];
        // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
        for (int j = 0 + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得重置最小数了
                min = arr[j];
                minIndex = j;
            }
        }

        // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

        // 4. 最后,交换最小数与arr[0]
        arr[minIndex] = arr[0];
        arr[0] = min;

        System.out.println("第一轮排序过后的数组:");
        System.out.println(Arrays.toString(arr));
    }

}

我不知道大家能不能看懂上面我所写的代码,但我想,经过我上面那么详细的分析,以及上述代码中的关键注释,大家应该是看懂了。当然,如果你要实在是没看懂,那也没关系,因为这里我会再给大家说一遍第一轮排序我们到底都干了些什么事情。

说起来也很简单,就是我们先假定无序数列中的第一个数就是当前第一轮排序中的最小数,但它到底是不是,我们并不知道,既然不知道,那我们就得从下标1开始对该无序数列进行遍历了,若在遍历过程中发现假定的最小数还大于别人,则说明我们假定的这个最小数它并不是最小的,故我们还得来重置最小数。不难想像,当遍历到无序数列的最后时,我们便得到了本轮排序中的最小数及其下标。得到之后,最后自然便是要来交换该最小数与无序数列中的第一个数了。

说这么多也没用,我还是运行一把咱的程序给大家看看结果吧!如下图所示,是不是可以看到经过第一轮排序之后,我们就把无序数列中的最小数放在首位了啊!

在这里插入图片描述

综上,我又给大家讲了一遍。其实,还有一种方法可以让大家理解得更透彻,那便是启用Debug模式来跟踪代码的运行流程,这样,大家就会更加直观地看到代码运行过程中参数的变化了。

接下来,我就来给大家打个断点,Debug一下咱们写的代码吧!

既然是要以Debug模式来调试代码,那么我们不妨就在如下这一行代码处打上一个断点。

在这里插入图片描述

断点打好之后,接下来我们便要以Debug模式来执行我们写好的程序了。

在这里插入图片描述

从上图中大家可以看到,Variables区中的arr变量其值是{101, 34, 119, 1},而这正是我们传递进来的待排序数组,对吧,换句话说,此时此刻该数组还没发生变化。

然后,让程序往下走两步,如下图所示,可以看到此时此刻我们假定的最小数是101,为啥是它呢?原因我不说,想必大家已经也都知道了,就是因为我们假定了无序数列中的第一个数就是当前第一轮排序中的最小数。

在这里插入图片描述

接着,让程序往下走一步,如下图所示,可以看到此时此刻我们假定的最小数要与待排序数组中下标从1开始的数依次来进行比较了。

在这里插入图片描述

由于此刻待排序数组中下标为1的数是34,而我们假定的最小数又是101,因此接下来程序会进入到if语句里面中去。而当我们执行完if语句之后,你会发现其实最小数就已经发生变化了,如下图所示,此时是变成了34。为什么会发生这样一个变化呢?原因也很简单,就是因为我们刚刚假定的101它并不是最小数,故而我们还得来重置一下它。

在这里插入图片描述

接下来,让程序继续往下走,很明显,此时我们是要来让最小数(此刻它已经重置为了34)与待排序数组中下标为2的数进行比较。

在这里插入图片描述

由于此刻待排序数组中下标为2的数是119,而我们假定的最小数又是34,因此接下来程序并不会进入到if语句里面中去。

于是,程序继续往下走,又会让最小数(它已重置为了34)与待排序数组中下标为3的数进行比较。

在这里插入图片描述

由于此刻待排序数组中下标为3的数是1,而我们假定的最小数又是34,因此接下来程序又会进入到if语句里面中去,干什么呢,重置最小数。相信不用我说,大家也应该都知道当执行完if语句之后,最小数已经由34重置为了1了吧!

在这里插入图片描述

接下来,让程序继续往下走,本来此时是要让最小数(它已重置为了1)与待排序数组中后续的数来进行比较的,但由于此时已经比到了待排序数组的最后,因此也就毋须再接着比较了,也就是说程序会直接退出整个for循环,来到此处。

在这里插入图片描述

很明显,这儿是来交换最小数与arr[0]的,交换过后,待排序数组就变成了下面这样。

在这里插入图片描述

从上可以看到,经过第一轮排序之后,待排序数组中的最小数就已被确定了下来,并且还被放在了首位。

现在大家应该非常理解第一轮排序我们到底都做了些什么事情了吧!而且,通过上述讲解,相信大家也都明白了一个道理,就是遇到不理解的代码,咱们可以启用Debug模式来跟踪代码的运行流程,以这种方式来理解,相信大家理解起来也会更加透彻一些。

总之,算法学起来肯定是有一定难度的,而且相信你必定会碰到一些让你觉得难以理解的点,真碰到了,也不用怕,多去尝试Debug就行,放宽心啊😘!

第一轮排序的实现代码写完过后,第二轮排序的实现代码写起来那就比较简单了,我们只须在前一轮的基础上稍微地修改一下代码即可,如下所示。

package com.meimeixia.sort;

import java.util.Arrays;

/**
 * @author liayun
 * @create 2024-03-20 10:12
 */
public class SelectSort {

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        System.out.println("排序前的数组:");
        System.out.println(Arrays.toString(arr));
        selectSort(arr);
    }

    /**
     *
     * @param arr:你只要给我传进来一个数组,那我就帮你进行选择排序
     */
    public static void selectSort(int[] arr) {
        /**
         * 第一轮排序。
         *
         * 经过我们上述分析,我想大家应该都知道第一轮排序就是要找到无序数列中的最小数,然后再将其放在首位了吧!
         */ 
        // 1. 先假定当前这一轮的最小数就是无序数列中的第一个数,当然,至于它到底是不是,那我们就不得而知了
        int minIndex = 0;
        int min = arr[minIndex];
        // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
        for (int j = 0 + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                min = arr[j];
                minIndex = j;
            }
        }

        // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

        // 4. 最后,交换最小数与arr[0]
        arr[minIndex] = arr[0];
        arr[0] = min;

        System.out.println("第一轮排序过后的数组:");
        System.out.println(Arrays.toString(arr));

        /**
         * 第二轮排序。
         *
         * 经过我们上述分析,我想大家应该都知道第二轮排序就是要从无序数列后面3个数里找一个最小数,然后再将其放在下标1处了吧!
         */
        // 1. 先假定当前这一轮的最小数就是无序数列中下标为1的数,当然,至于它到底是不是,那我们就不得而知了
        minIndex = 1;
        min = arr[minIndex];
        // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
        for (int j = 1 + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                min = arr[j];
                minIndex = j;
            }
        }

        // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

        // 4. 最后,交换最小数与arr[1]
        arr[minIndex] = arr[1];
        arr[1] = min;

        System.out.println("第二轮排序过后的数组:");
        System.out.println(Arrays.toString(arr));
    }

}

执行以上程序,可以看到经过第二轮排序之后,待排序数组变成了下面这个样子。

在这里插入图片描述

咦!这第二轮排序过后,待排序数组也没有发生任何变化啊!咋会这样呢?原因嘛,也很简单,就是因为在第二轮排序的过程中我们假定的最小数34它的的确确就是这一轮排序里面的最小数,所以你才会看到这一现象。

而这又出现了一个新的问题,就是在这一轮排序里面,我们假定的最小数还就刚刚好正是待排序数组后面3个数里的最小数,既然如此,那么是不是在最后阶段就没有交换其的必要了啊,是这样子吧!既然交换没有意义,那我们是不是得对上述代码进行一个优化啊!如何优化呢?看我下面所写的代码即可。

package com.meimeixia.sort;

import java.util.Arrays;

/**
 * @author liayun
 * @create 2024-03-20 10:12
 */
public class SelectSort {

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        System.out.println("排序前的数组:");
        System.out.println(Arrays.toString(arr));
        selectSort(arr);
    }

    /**
     *
     * @param arr:你只要给我传进来一个数组,那我就帮你进行选择排序
     */
    public static void selectSort(int[] arr) {
        /**
         * 第一轮排序。
         *
         * 经过我们上述分析,我想大家应该都知道第一轮排序就是要找到无序数列中的最小数,然后再将其放在首位了吧!
         */ 
        // 1. 先假定当前这一轮的最小数就是无序数列中的第一个数,当然,至于它到底是不是,那我们就不得而知了
        int minIndex = 0;
        int min = arr[minIndex];
        // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
        for (int j = 0 + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                min = arr[j];
                minIndex = j;
            }
        }

        // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

        // 4. 最后,交换最小数与arr[0]
        // 注意,在交换的时候,我们还得来做一个判断,即倘若我们假定的最小数还就刚刚好正是待排序数组中的第一个数,那么我们就毋须来交换了,因为交换没有意义,反之,则交换
        if (minIndex != 0) {
            arr[minIndex] = arr[0];
            arr[0] = min;
        }

        System.out.println("第一轮排序过后的数组:");
        System.out.println(Arrays.toString(arr));

        /**
         * 第二轮排序。
         *
         * 经过我们上述分析,我想大家应该都知道第二轮排序就是要从无序数列后面3个数里找一个最小数,然后再将其放在下标1处了吧!
         */
        // 1. 先假定当前这一轮的最小数就是无序数列中下标为1的数,当然,至于它到底是不是,那我们就不得而知了
        minIndex = 1;
        min = arr[minIndex];
        // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
        for (int j = 1 + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                min = arr[j];
                minIndex = j;
            }
        }

        // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

        // 4. 最后,交换最小数与arr[1]
        // 注意,在交换的时候,我们还得来做一个判断,即倘若我们假定的最小数还就刚刚好正是待排序数组后面3个数里的最小数,那么我们就毋须来交换了,因为交换没有意义,反之,则交换
        if (minIndex != 1) {
            arr[minIndex] = arr[1];
            arr[1] = min;
        }

        System.out.println("第二轮排序过后的数组:");
        System.out.println(Arrays.toString(arr));
    }

}

从上可以看到,我们也对第一轮排序的实现代码做了一个优化,即在最后阶段交换最小数时也添加上了一个判断。

接下来,我们就该来写第三轮排序的实现代码了,照葫芦画瓢,相信大家应该不难写出。

package com.meimeixia.sort;

import java.util.Arrays;

/**
 * @author liayun
 * @create 2024-03-20 10:12
 */
public class SelectSort {

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        System.out.println("排序前的数组:");
        System.out.println(Arrays.toString(arr));
        selectSort(arr);
    }

    /**
     *
     * @param arr:你只要给我传进来一个数组,那我就帮你进行选择排序
     */
    public static void selectSort(int[] arr) {
        /**
         * 第一轮排序。
         *
         * 经过我们上述分析,我想大家应该都知道第一轮排序就是要找到无序数列中的最小数,然后再将其放在首位了吧!
         */ 
        // 1. 先假定当前这一轮的最小数就是无序数列中的第一个数,当然,至于它到底是不是,那我们就不得而知了
        int minIndex = 0;
        int min = arr[minIndex];
        // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
        for (int j = 0 + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                min = arr[j];
                minIndex = j;
            }
        }

        // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

        // 4. 最后,交换最小数与arr[0]
        // 注意,在交换的时候,我们还得来做一个判断,即倘若我们假定的最小数还就刚刚好正是待排序数组中的第一个数,那么我们就毋须来交换了,因为交换没有意义,反之,则交换
        if (minIndex != 0) {
            arr[minIndex] = arr[0];
            arr[0] = min;
        }

        System.out.println("第一轮排序过后的数组:");
        System.out.println(Arrays.toString(arr));

        /**
         * 第二轮排序。
         *
         * 经过我们上述分析,我想大家应该都知道第二轮排序就是要从无序数列后面3个数里找一个最小数,然后再将其放在下标1处了吧!
         */
        // 1. 先假定当前这一轮的最小数就是无序数列中下标为1的数,当然,至于它到底是不是,那我们就不得而知了
        minIndex = 1;
        min = arr[minIndex];
        // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
        for (int j = 1 + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                min = arr[j];
                minIndex = j;
            }
        }

        // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

        // 4. 最后,交换最小数与arr[1]
        // 注意,在交换的时候,我们还得来做一个判断,即倘若我们假定的最小数还就刚刚好正是待排序数组后面3个数里的最小数,那么我们就毋须来交换了,因为交换没有意义,反之,则交换
        if (minIndex != 1) {
            arr[minIndex] = arr[1];
            arr[1] = min;
        }

        System.out.println("第二轮排序过后的数组:");
        System.out.println(Arrays.toString(arr));

        /**
         * 第三轮排序。
         *
         * 经过我们上述分析,我想大家应该都知道第三轮排序就是要从无序数列后面2个数里找一个最小数,然后再将其放在下标2处了吧!
         */
        // 1. 先假定当前这一轮的最小数就是无序数列中下标为2的数,当然,至于它到底是不是,那我们就不得而知了
        minIndex = 2;
        min = arr[minIndex];
        // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
        for (int j = 2 + 1; j < arr.length; j++) {
            if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                min = arr[j];
                minIndex = j;
            }
        }

        // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

        // 4. 最后,交换最小数与arr[2]
        // 注意,在交换的时候,我们还得来做一个判断,即倘若我们假定的最小数还就刚刚好正是待排序数组后面2个数里的最小数,那么我们就毋须来交换了,因为交换没有意义,反之,则交换
        if (minIndex != 2) {
            arr[minIndex] = arr[2];
            arr[2] = min;
        }

        System.out.println("第三轮排序过后的数组:");
        System.out.println(Arrays.toString(arr));
    }

}

执行以上程序,可以看到经过第三轮排序之后,待排序数组变成了下面这个样子,即在第二轮排序的基础之上交换了一下最末两个数。

在这里插入图片描述

至此,待排序数组就被我们按照要求排成为有序的了。

诸位,相信我讲解到这里,大家也已经通过我上述逐步推导的过程发现了其中所隐藏的规律,如果没发现,那么就说明你没认真跟着我写每一轮排序的实现代码,要知道这些规律就隐藏在这每一轮排序的实现代码里面。算了,下面我还是把这个规律给大家说一下吧!

在这里插入图片描述

既然规律都已经有了,那接下来就让我们使用一个循环将上述三个for循环给嵌套起来吧!

package com.meimeixia.sort;

import java.util.Arrays;

/**
 * @author liayun
 * @create 2024-03-20 10:12
 */
public class SelectSort {

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        System.out.println("排序前的数组:");
        System.out.println(Arrays.toString(arr));
        selectSort(arr);
    }

    /**
     *
     * @param arr:你只要给我传进来一个数组,那我就帮你进行选择排序
     */
    public static void selectSort(int[] arr) {

        for (int i = 0; i < arr.length - 1; i++) {
            // 1. 先假定当前这一轮的最小数就是无序数列中下标为i的数,当然,至于它到底是不是,那我们就不得而知了
            int minIndex = i;
            int min = arr[minIndex];
            // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                    min = arr[j];
                    minIndex = j;
                }
            }

            // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

            // 4. 最后,交换最小数与arr[i]
            // 注意,在交换的时候,我们还得来做一个判断。至于为什么这儿要做这样一个判断,相信大家经过我上述的讲解已经也都知道了,因此我就不在这儿多赘述了
            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }

            System.out.println("第" + (i + 1) + "轮排序过后的数组:");
            System.out.println(Arrays.toString(arr));
        }

    }

}

执行以上程序,可以看到经过三轮排序之后,待排序数组同样也给弄成有序的了,看来,咱们写的代码是一点问题都没有啊,嘿嘿😘!

在这里插入图片描述
只是,有一点不好的是,在每次选择排序的时候,我们都将每一轮排序之后的数组给打印出来了,而这显然是没有必要的,因此我们还得注销如下两句代码。只不过,这样一来,我们就得在main方法中为待排序数组排序之后,再将其输出了。

package com.meimeixia.sort;

import java.util.Arrays;

/**
 * @author liayun
 * @create 2024-03-20 10:12
 */
public class SelectSort {

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        System.out.println("排序前的数组:");
        System.out.println(Arrays.toString(arr));
        selectSort(arr);
        System.out.println("排序后的数组:");
        System.out.println(Arrays.toString(arr));
    }

    /**
     *
     * @param arr:你只要给我传进来一个数组,那我就帮你进行选择排序
     */
    public static void selectSort(int[] arr) {

        for (int i = 0; i < arr.length - 1; i++) {
            // 1. 先假定当前这一轮的最小数就是无序数列中下标为i的数,当然,至于它到底是不是,那我们就不得而知了
            int minIndex = i;
            int min = arr[minIndex];
            // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                    min = arr[j];
                    minIndex = j;
                }
            }

            // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

            // 4. 最后,交换最小数与arr[i]
            // 注意,在交换的时候,我们还得来做一个判断。至于为什么这儿要做这样一个判断,相信大家经过我上述的讲解已经也都知道了,因此我就不在这儿多赘述了
            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }

            // 由于没有必要在每一轮排序的时候都输出排序好的数组,因此我们还得注销掉如下两句代码
            // System.out.println("第" + (i + 1) + "轮排序过后的数组:");
            // System.out.println(Arrays.toString(arr));
        }

    }

}

此时,再来执行以上程序,可以看到待排序数组经选择排序之后依旧变得有序了。

在这里插入图片描述

至此,经过我们的一步步推导,选择排序的完整实现代码就被我们给写出来了。

当然,你也可以将待排序数组中的数据搞得多一点,看看我们写的选择排序是否能经得起考验。

package com.meimeixia.sort;

import java.util.Arrays;

/**
 * @author liayun
 * @create 2024-03-20 10:12
 */
public class SelectSort {

    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1, -1, 90, 123};
        System.out.println("排序前的数组:");
        System.out.println(Arrays.toString(arr));
        selectSort(arr);
        System.out.println("排序后的数组:");
        System.out.println(Arrays.toString(arr));
    }

    /**
     *
     * @param arr:你只要给我传进来一个数组,那我就帮你进行选择排序
     */
    public static void selectSort(int[] arr) {

        for (int i = 0; i < arr.length - 1; i++) {
            // 1. 先假定当前这一轮的最小数就是无序数列中下标为i的数,当然,至于它到底是不是,那我们就不得而知了
            int minIndex = i;
            int min = arr[minIndex];
            // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                    min = arr[j];
                    minIndex = j;
                }
            }

            // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

            // 4. 最后,交换最小数与arr[i]
            // 注意,在交换的时候,我们还得来做一个判断。至于为什么这儿要做这样一个判断,相信大家经过我上述的讲解已经也都知道了,因此我就不在这儿多赘述了
            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }

            // 由于没有必要在每一轮排序的时候都输出排序好的数组,因此我们还得注销掉如下两句代码
            // System.out.println("第" + (i + 1) + "轮排序过后的数组:");
            // System.out.println(Arrays.toString(arr));
        }

    }

}

执行以上程序,可以看到待排序数组排序过后,依旧还是按照从小到大的顺序来排序的,而这就说明了我们写的选择排序是经受住了考验的,嘿嘿😘!

在这里插入图片描述

这里,我有个问题想问大家,就是如果要求你使用选择排序来对待排序数组按照从大到小的顺序排序,那么你会怎样修改上述代码呢?是不是只需要将下面这个>符号给改成<符号即可啊!

在这里插入图片描述

此时,再来执行以上程序,可以看到现在确实是按照从大到小的顺序来对待排序数组进行排序了。

在这里插入图片描述

当然,这里我们还是将其改回来啊,即还是按照从小到大的顺序来对待排序数组进行排序。

以上就是我们选择排序的整个推导和编写过程,不知大家能不能看懂,要是看不懂,那你就多看几遍吧,因为不管怎么说,算法学起来还是具有一定难度的。别看现在咱们学的选择排序还比较简单,等到后面你学到希尔排序、快速排序、归并排序以及堆排序时,你才会知道难度究竟有多大。正是因为这些算法学起来具有一定难度,所以你一定得具备能将一个复杂的算法问题给拆分成多个简单的问题然后再逐步进行解决的这样一个能力。相信你具备这样一个能力之后,算法学起来也会轻松许多,要不然我一上来就直接写两个for循环给大家整出来选择排序,那我相信,肯定会有一部分同学看不懂,更别说理解了。

最后,我问大家一个问题啊,就是选择排序它的时间复杂度是多少?是不是仍然也是O(n²)啊!为什么是它呢?不用我说,相信大家也都知道是因为我们使用嵌套for循环来写选择排序的吧!

最后的最后,我还想跟大家说几句心里话,就是写就这篇文章,真的耗费了我巨量的时间和精力,为什么会这么说呢?因为在写作的过程中,我为了追求完美,经常反复地推敲与修改,以致最后这才堪堪完善。因此,为了不枉费我的这一片良苦用心,大家在看的时候就一定要认真仔细地看了,充分地感受一下我笔下的每一个字在你心中跳跃时的生命力。

另外,我还想说的是,十大经典排序算法我本身就会,故在讲这些算法时,我其实是可以讲的很简单的,就上来过后我根本就不做上述那些推导,而是直接把代码一写就完了,但我想我这样做,很多同学应该是很难理解的。所以说为什么我要像上面那样一步一步地给大家推导啊,原因就是希望大家能够真正的理解。其实,很多书上也好,网上写的文章也好,动不动就说让你彻底理解,但实际上你根本就没有彻底理解过,对吧,反而是越看越晕,对此有切身体会吧!总之,我必须得把这个逐步推导的过程给大家讲明白,因为只有这样做,我觉得大家才能够真真正正地理解,至少我是这样认为的。

速度测试

就像我们在上篇文章中测试冒泡排序的速度那样,这里,我们同样也要来对选择排序的速度测试一把。

那如何来测试呢?还是像我们在上篇文章中测试冒泡排序的速度那样,先创建一个8万个随机数的数组,然后再来对该数组进行选择排序,当然我们得分别记录下来排序前和排序后的时间,这样两相比较之后,我们就能知道选择排序到底会花费我们多长时间了。

package com.meimeixia.sort;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * @author liayun
 * @create 2024-03-20 10:12
 */
public class SelectSort {

    public static void main(String[] args) {
        // int[] arr = {101, 34, 119, 1, -1, 90, 123};

        // 首先,创建一个拥有8万个随机数的数组
        int[] arr = new int[80000];
        for (int i = 0; i < 80000; i++) {
            arr[i] = (int) (Math.random() * 8000000); // 想必大家应该都知道这儿会产生一个[0, 8000000)该范围的随机数吧!
        }

        Date date1 = new Date();
        // 大家应该还记得SimpleDateFormat这个玩意吧!是不是用它我们就可以来格式化一把时间啊!就像下面这样,
        // 我们是把当前时间以年-月-日 时:分:秒的形式来格式化了一把,然后再进行了一个输出。之所以这样做,是因
        // 为待会更方便我直观地给大家呈现出排序前后的时间,这样大家心里就能有一个比较了。
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(date1);
        System.out.println("排序前的时间是:" + date1Str);

        // 然后,对以上数组进行选择排序
        selectSort(arr);

        Date date2 = new Date();
        String date2Str = simpleDateFormat.format(date2);
        System.out.println("排序后的时间是:" + date2Str);
    }

    /**
     *
     * @param arr:你只要给我传进来一个数组,那我就帮你进行选择排序
     */
    public static void selectSort(int[] arr) {

        for (int i = 0; i < arr.length - 1; i++) {
            // 1. 先假定当前这一轮的最小数就是无序数列中下标为i的数,当然,至于它到底是不是,那我们就不得而知了
            int minIndex = i;
            int min = arr[minIndex];
            // 2. 然后让假定的这一个最小数和它后面的每一个数依次进行比较,若发现有比它更小的数,则重新确定最小数
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) { // 若该条件成立,则说明当前我们假定的最小数它并不是最小的,如此一来,我们就得需要重置最小数了
                    min = arr[j];
                    minIndex = j;
                }
            }

            // 3. 当以上for循环结束之后,本轮排序中的最小数及其下标我们也就确定下来了

            // 4. 最后,交换最小数与arr[i]
            // 注意,在交换的时候,我们还得来做一个判断。至于为什么这儿要做这样一个判断,相信大家经过我上述的讲解已经也都知道了,因此我就不在这儿多赘述了
            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }

            // 由于没有必要在每一轮排序的时候都输出排序好的数组,因此我们还得注销掉如下两句代码
            // System.out.println("第" + (i + 1) + "轮排序过后的数组:");
            // System.out.println(Arrays.toString(arr));
        }

    }

}

在执行以上程序之前,大家不妨先来想一想,和冒泡排序相比,选择排序花费的时间到底是长还是短呢?肯定要比冒泡排序花费的时间短,为什么呢?因为在选择排序的整个过程中,发生交换的次数其实是非常少的,咋这样说呢,你想啊,每一轮排序是不是只有在假定的最小数和它后面的每一个数依次进行比较,且发现有比它更小的数时,我们才重置最小数啊,是不是,重置完之后我们才交换,而且最后到底要不要交换,还得判断一下,如此一来 ,选择排序花费的时间自然就要比冒泡排序花费的时间短了。

言归正传,下面我们来执行一下以上程序,看看其结果,如下图所示,可以看到8万个数据进行选择排序只须耗时3秒,对比我们冒泡排序所须耗时10秒来看,选择排序所耗时间确实是要比冒泡排序缩短很多。

在这里插入图片描述

接下来,我们再来执行一下以上程序,看看其结果,如下图所示,可以看到8万个数据进行选择排序现在只须耗时2秒了。

在这里插入图片描述

啥也不说了,咱们再来执行一次以上程序,看看其结果,如下图所示,可以看到8万个数据进行选择排序所耗时间依旧还是2秒。

在这里插入图片描述

由此,我觉着现在8万个数据选择排序完大致应该是要耗费我们2~3秒左右的时间,反正我估摸着是差不了多少了,当然,这是在我电脑上运行的结果,可能在你电脑上运行又是另一个结果,嘿嘿🤭!但不管怎么说,选择排序所耗时间确实是要比冒泡排序缩短很多,也即选择排序比冒泡排序要快,而且还快了很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值