vlambda博客
学习文章列表

会打扑克就会选择排序

会打扑克就会选择排序

选择排序很像一小部分人打扑克牌时(因为大部人是另外一种方式,对应我们常见的另外一种排序了),把牌从左到右扫描,选出最小的一张牌,放到最左边,然后从第二张牌继续扫描第二张小的牌,放到第二小的位置,依次进行下去。。。这个画面感是不是很熟悉。没错,这在程序中就是选择排序,是不是相当的简单。

定义

选择排序是一种很简单直观的排序算法, 属于选择类排序算法。

选择排序,一般我们指的是简单选择排序,也可以叫直接选择排序,它不像冒泡排序一样相邻地交换元素,而是通过选择最小的元素,每轮迭代只需交换一次。虽然交换次数比冒泡少很多,但效率和冒泡排序一样的糟糕。

动图展示


思路

设左边为有序区,右边乱序区。从右边剩下的乱序数字中找最小,放到左边。(反过来也是可以的,这里我们只讲解这种从小到大的情况)


具体操作:  多次比较,一次交换。 每次从右边剩余数字中选最小,与乱序区最左边的元素交换,使左边有序区边界右移。

文字叙述过程

参照上面的动图演示,我们也很容易给出过程的文字描述:

  • 第1趟比较:拿第1个元素依次和它后面的每个元素进行比较,如果第1个元素大于后面某个元素,交换它们,经过第1趟比较,数组中最小的元素被选出,它被排在第一位
  • 第2趟比较:拿第2个元素依次和它后面的每个元素进行比较,如果第2个元素大于后面某个元素,交换它们,经过第2趟比较,数组中第2小的元素被选出,它被排在第二位
  • ......
  • 第n-1趟比较:第n-1个元素和第n个元素作比较,如果第n-1个元素大于第n个元素,交换它们

实现

也许你想说,"说了这么多,还说很简单,你倒是整一个啊!", 好的,这就安排上。

算法安排

package sort

func SelectSort(array []int) {
 arrayLen := len(array)
 // 进行N-1轮迭代
 for i := 0; i < arrayLen-1; i++ {
  // 最小元素的索引位
  minIndex := i
  // 记录下最小数
  min := array[i]
  for j := i + 1; j < arrayLen; j++ {
   if array[j] < min {
    // 变更最小数和最小数下标
    min = array[j]
    minIndex = j
   }
  }
  // 进行元素位置交换
  if minIndex != i {
   array[i], array[minIndex] = array[minIndex], array[i]
  }
 }
}

一种优化思路

这边其实还有一种优化思路就是: 我们一轮同时进行最小值和最大值的扫描,然后分别和最前面和最后面的元素进行交换,这样将会使我们的复杂度少一半。

代码安排上:

package sort

func SelectSortOpt1(array []int) {
 arrayLen := len(array)
 // 进行N/2轮迭代
 for i := 0; i < arrayLen/2; i++ {
  // 最小元素的索引位
  minIndex := i
  // 最大元素的索引位
  maxIndex := i
  // 记录下最小数
  min := array[i]
  // 记录下最大数
  max := array[i]

  for j := i + 1; j < arrayLen-i; j++ {
   if array[j] < min {
    // 变更最小数和最小数下标
    min = array[j]
    minIndex = j
   }
   if array[j] > max {
    // 变更最大数和最大数下标
    max = array[j]
    maxIndex = j
   }
  }
  // 进行元素位置交换
  if minIndex != i {
   array[i], array[minIndex] = array[minIndex], array[i]
  }
  if maxIndex != arrayLen-i-1 {
   array[arrayLen-i-1], array[maxIndex] = array[maxIndex], array[arrayLen-i-1]
  }
 }
}

复杂度

时间复杂度

首先,为了在第 1 轮找到最小的数字,需要从左往右确认数列中的数字,只要查询 n 个数 字即可。在接下来的第 2 轮中,需要从 n -1 个数字中寻找最小值,所以需要查询 n -1 个数字。将这个步骤进行到第 n 轮的时候,需要查询的次数如下。

n+( n- 1)+(n-2)+…3+2 +1=n(n+1)/2≤ n^2

平均时间复杂度 О(n²) 最坏时间复杂度 О(n²) 最优时间复杂度 О(n²)

空间复杂度

空间复杂度:O(1),只需要一个附加程序单元用于交换

稳定性

稳定性:选择排序是不稳定的排序算法,因为无法保证值相等的元素的相对位置不变。

例如: [3, 4, 3, 1]这个数组,第一次交换,第一个3和1交换位置,此时原来两个3的相对位置发生了变化。

总结

选择排序可以说是表现最稳定的排序算法之一,因为最优最坏的时间复杂度都是都是O(n2)。但唯一的好处可能就是不占用额外的内存空间了吧, 空间复杂度为O(1)。选择排序按讲应该也是最容易想到的一种排序算法,因为我们生活中这样的实例太多了,比比皆是。

参阅:

选择排序

图解排序算法及实现——选择排序(Selection sort)