vlambda博客
学习文章列表

一分钟搞定基础排序算法——选择排序

选择排序可以看做是冒泡排序的一个改进型。工作原理也是非常的简单:(此处用从小到大排序举例)第一轮,整个数组从0号位开始,从左到右遍历一遍,找出最小的元素,将其放在数组的0号位。第二轮,从1号位开始遍历一遍,找出最小值,将其放在数组的1号位。以此类推直到结束。就完成了选择排序。

尝试实现

如上图所示,有一个待排序的数组nums。按开头时讲述的算法步骤。第一趟,从0号位开始我们从左到右遍历,找出最小值。此时准备3个指针。i直接指向0号位,标记此时的未排序数组的第一个位置。index,指向找到的最小值(开始时暂定0号位就是最小值)。j作为游标,从左到右开始遍历。因为0号位已经被标记,所以j指针从i+1位置开始。

首次比较nums[j] < nums[index],index指向j的位置。j继续向右遍历。结果如下图所示。

一分钟搞定基础排序算法——选择排序

这时比较i和index的值,发现不是同一值,双方值交换。

一分钟搞定基础排序算法——选择排序

然后如此类推开始第二趟遍历。

一分钟搞定基础排序算法——选择排序

而且这是我们也发现了一个规律,当有N个数字时,第N趟时,j指向为n,已经发生数组越界。所以当有N个数字时,n-1趟就能完成排序。

用代码实现:

public static void selectionSort(int[] nums) {
   if (null == nums || nums.length < 2) {
       return;
  }
   //N个数字,n-1趟即可完成排序
   for (int i = 0; i < nums.length - 1; i++) {
       //开始时,默认i指向的为最小值
       int index = i;
       for (int j = i + 1; j < nums.length; j++) {
           if (nums[j] < nums[index]) {
               index = j;
          }
      }
       if(index != i){
           int temp = nums[i];
           nums[i] = nums[index];
           nums[index] = temp;
      }
  }
}

算法分析

时间复杂度

选择排序时间复杂度和冒泡类似:

  1. 最好情况

    数组已经是一个排好序的数组,一趟遍历即可完成。算法复杂度为O(n)

  2. 最坏情况

    队列逆序排列,需要N-1趟进行排序。并且每次要进行N-i次的比较才能完成。

    i的取值为1<=i<=N-1。时间复杂度O(n^2)

  3. 平均情况

    数组乱序排列时,数组的平均复杂度为O(n^2)


    这里需要注意的是,虽然选择排序和冒泡排序的时间复杂度一样,但是选择排序进行的交换操作少,最多发生 N - 1次交换。冒泡排序最坏的情况下要发生N^2 /2的交换操作。因此可以得出结论,选择排序的性能略优于冒泡排序。

空间复杂度

空间复杂度和冒泡一样,因为交换的时候需要一个额外的空间来辅助完成元素交换动作。所以空间复杂度为O(1)。

算法稳定性

值得注意的是选择排序是不稳定的排序算法

我们对初始时的数组做个简单的修改。如下图所示。有两个13,分别为绿色和黄色。

第一趟完成后,结果会变为:

这时发现两个13的相对顺序已经发生了改变。所以说选择排序不是一个稳定的排序算法。