排序算法(二)-选择排序

0. 原理

选择排序和冒泡排序一样是一种简单直观的排序算法,工作原理如下:

  1. 在未排序序列中找到最大或者最小的数字,放到最前或者最后的位置
  2. 再从剩余的序列中找到最大或者最小的数字,放到剩余序列的最前或者最后的位置
  3. 重复操作直到序列有序

对[2, 8, 1, 5, 3]这个数组排序,完整流程如下

round 1 start
2 8 1 5 3  (exchange data: 2 <-> 1)  1 8 2 5 3 
round 1 finish
round 2 start
1 8 2 5 3  (exchange data: 8 <-> 2)  1 2 8 5 3 
round 2 finish
round 3 start
1 2 8 5 3  (exchange data: 8 <-> 5)  1 2 5 8 3 
1 2 5 8 3  (exchange data: 5 <-> 3)  1 2 3 8 5 
round 3 finish
round 4 start
1 2 3 8 5  (exchange data: 8 <-> 5)  1 2 3 5 8 
round 4 finish

可见第一趟将最小的1放到了第一位,第二趟将第二小的2放到了第二位,依此类推。
和冒泡排序类似,每一趟都会使得一个元素放到它最终的位置。和冒泡排序的差别就是它并不是两两相邻交换,而是两两可能跨越多个数字进行交换。

1. 实现

关键代码如下:

public int[] sort(int[] data) {
    if (data == null || data.length <= 1) {
        return data;
    }

    for (int i = 0; i < data.length - 1; i++) {
        System.out.println("round " + (i + 1) + " start");
        for (int j = i + 1; j < data.length; j++) {
            if (data[i] > data[j]) {
                swap(data, i, j);
            }
        }
        System.out.println("round " + (i + 1) + " finish");
    }

    return data;
}

具体实现可以查看:
GitHub/SelectionSort.java

可以看到其实代码实现和冒泡很类似,同样的两个循环,只不过内循环的遍历顺序有差别,内循环的比较也不同,冒泡是相邻比较,选择则是跨越比较。

2. 复杂度分析

同样从代码和原理分析,选择排序外层循环需要n - 1次, 内层循环需要n - i次,也就是n(n-1)/2次。内循环赋值操作三次,比较操作一次。因此最坏的情况应该是整个序列是逆向的,时间复杂度是O(n²),最好的情况是序列时正序的一次扫描即可,时间复杂度为O(n),平均复杂度为O(n²)。由于不需要额外空间,空间复杂度为O(1)。

3. 优化

选择排序的优化目前只找到一个版本,就是在一次遍历的时候,找到最大和最小值,一次外循环能同时找到最大和最小值并且放到最终位置。这个优化平均可以减少一半遍历的次数,但是每个循环的比较和交换操作相应也多了,因此优化应该小于一半的时间。
下面为代码实现:

@Override
public int[] sort(int[] data) {
    if (data == null || data.length <= 1) {
        return data;
    }

    int leftIndex = 0;
    int rightIndex = data.length - 1;

    int minIndex;
    int maxIndex;

    int roundCount = 1;

    while (leftIndex < rightIndex) {
        System.out.println("round " + roundCount + " start");
        minIndex = leftIndex;
        maxIndex = rightIndex;
        for (int i = leftIndex; i <= rightIndex; i++) {
            if (data[i] < data[minIndex]) {
                minIndex = i;
            } else if (data[i] > data[maxIndex]) {
                maxIndex = i;
            }
        }

        swap(data, maxIndex, rightIndex);
        swap(data, minIndex, leftIndex);

        leftIndex++;
        rightIndex--;
        System.out.println("round " + roundCount + " finish");
        roundCount++;
    }

    return data;
}

具体实现可以查看:
GitHub/SelectionSort.java

还是对[2, 8, 1, 5, 3]这个数组排序运行结果如下:

round 1 start
2 8 1 5 3  (exchange data: 8 <-> 3)  2 3 1 5 8 
2 3 1 5 8  (exchange data: 1 <-> 2)  1 3 2 5 8 
round 1 finish
round 2 start
1 3 2 5 8  (exchange data: 5 <-> 5)  1 3 2 5 8 
1 3 2 5 8  (exchange data: 2 <-> 3)  1 2 3 5 8 
round 2 finish

可以看到,与优化前相比遍历轮数少了一半(优化前4次,优化后2次),但是交换操作次数其实只减少了1次,少了一次left == right时的交换。偶数个元素的话交换次数应该是一致的。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值