学点算法(六)——数组选择排序

今天我们来学习数组的插入排序算法。

假设你是一个农场工人,收获了100个萝卜,长短不一,农场主让你从小到大排列好售卖,你应该怎么做?

一般做法就是选一个萝卜,并假设它是最小的,然后依次与其他萝卜比较,如果发现更小的,则换成更小的萝卜,一直比到最后,然后把作为最小的萝卜放好。接着重新选一个萝卜,重复刚才的操作,直到最后选出作为第二小的萝卜放好,依次重复这个步骤,最后,萝卜就从小到大排好了。

从上面的场景中,我们可以发现,如果每次从无序集合中选出一个最小的或者最大的,把选出的这个最小或者最大的放到指定位置,那么随着每次选择的进行,最后我们会得到一个有序的集合,这个过程就是今天的主角——选择排序的算法过程。

我们来看对数组[6, 3, 9, 7, 8, 5, 0, 2, 4, 1]的从小到大选择排序过程:

  1. 得到一个未排序数组[6, 3, 9, 7, 8, 5, 0, 2, 4, 1]
    在这里插入图片描述
  2. 在未排序部分中,选出最小值,此时最小值为0,将0取出,放到有序部分的下一个位置上,因为此时没有有序部分(可以理解为有序部分最后索引为-1),即放到索引为0的位置上,但是索引为0的位置已经有一个元素了,为了不丢失这个元素,我们交换一下这两个元素,即将索引为0的元素6与索引为6的元素0交换位置,这样就将最小值0放到了指定位置,并且也没有丢失原来在索引0的元素6
    在这里插入图片描述
  3. 继续在未排序部分中,选出最小值,此时最小值为1,将1取出,和索引为1的元素2交换位置。
    在这里插入图片描述
  4. 继续在未排序部分中,选出最小值,此时最小值为2,将2取出,和索引为2的元素9交换位置。
    在这里插入图片描述
  5. 继续在未排序部分中,选出最小值,此时最小值为3,将3取出,和索引为3的元素7交换位置。
    在这里插入图片描述
  6. 继续在未排序部分中,选出最小值,此时最小值为4,将4取出,和索引为4的元素8交换位置。
    在这里插入图片描述
  7. 继续在未排序部分中,选出最小值,此时最小值为5,将5取出,在这一步,我们发现,5已经处于它所在的位置上,可以不用交换,而且程序想要知道不交换,也需要每次去判断一下,与其去判断,不如直接交换,同时还保持了编程实现的简洁和统一。
    在这里插入图片描述
  8. 继续在未排序部分中,选出最小值,此时最小值为6,将6取出,同样,我们还是继续交换6
    在这里插入图片描述
  9. 继续在未排序部分中,选出最小值,此时最小值为7,将7取出,和索引为7的元素9交换位置。
    在这里插入图片描述
  10. 继续在未排序部分中,选出最小值,此时最小值为8,将8取出,同样,我们还是继续交换8
    在这里插入图片描述
  11. 继续在未排序部分中,选出最小值,此时最小值为9,将9取出,同样,我们还是继续交换9
    在这里插入图片描述
  12. 至此,所有元素就位,排序完毕。

在元素就位的过程中,我们可以发现,由于要将元素放到指定位置,而不能使原来在这个位置上的元素丢失,我们就需要交换元素,而交换元素就导致了相同元素的排列的原始顺序被破坏,所以数组的选择排序是一个不稳定的排序算法。

代码实现如下:

/**
 * 数组的选择排序算法
 * 从小到大排序
 *
 * @param nums 待排序数组
 * @param lo   排序区间lo索引(包含)
 * @param hi   排序区间hi索引(不包含)
 */
public static void selectionSort(int[] nums, int lo, int hi) {
    // 数组为null则直接返回
    if (nums == null) {
        return;
    }
    // 索引检查
    if (lo < 0 || nums.length <= lo) {
        throw new IllegalArgumentException("lo索引必须大于0并且小于数组长度,数组长度:" + nums.length);
    }
    if (hi < 0 || nums.length < hi) {
        throw new IllegalArgumentException("hi索引必须大于0并且小于等于数组长度,数组长度:" + nums.length);
    }
    if (hi <= lo) {
        // lo索引必须小于hi索引(等于也不行,因为区间是左闭右开,如果等于,区间内元素数量就为0了)
        throw new IllegalArgumentException("lo索引必须小于hi索引");
    }
    if (lo + 1 >= hi) {
        // 区间元素个数最多为1
        // 无需排序
        return;
    }
    // 排序部分待存放元素的索引
    int nextSortedIdx = 0;
    // 未排序部分的起始位置
    int unsortedStartIdx = 0;
    while (unsortedStartIdx < hi) {
        // 还有元素未排序
        // 找到最小值元素所在的索引位置
        int minIdx = findMinIdx(nums, unsortedStartIdx, hi);
        // 交换排序部分待存放元素和最小值元素
        int tmp = nums[minIdx];
        nums[minIdx] = nums[nextSortedIdx];
        nums[nextSortedIdx] = tmp;
        // 排序部分待存放元素的索引+1
        nextSortedIdx++;
        // 未排序部分的起始位置+1
        unsortedStartIdx++;
    }
    // 无未排序元素,排序完毕
}

/**
 * 在[lo, hi)区间内找到最小值元素所在的索引位置
 * @param nums 待查找数组
 * @param lo   查找区间lo索引(包含)
 * @param hi   查找区间hi索引(不包含)
 */
private static int findMinIdx(int[] nums, int lo, int hi) {
    // 假设最小值元素所在索引位置为lo
    int minIdx = lo;
    for (int i = lo; i < hi; i++) {
        if (nums[i] < nums[minIdx]) {
            // 如果发现比当前假设元素小的,则更新索引位置
            minIdx = i;
        }
    }
    return minIdx;
}

测试代码如下:

int[] nums = {6, 3, 9, 7, 8, 5, 0, 2, 4, 1};
System.out.println("排序前:" + Arrays.toString(nums));
selectionSort(nums, 0, nums.length);
System.out.println("排序后:" + Arrays.toString(nums));

输出如下:

排序前:[6, 3, 9, 7, 8, 5, 0, 2, 4, 1]
排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

符合我们的预期。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值