vlambda博客
学习文章列表

十大排序算法之选择排序

这里不得不先解释一下上一篇文章冒泡排序的空间复杂度,我写的是最好是O(0)这个我可以接受,就是初始数据已经有序。

最坏是O(n), 这个是很多人都在网上说,但是我个人不是很理解为什么是O(n). 因为在for循环里就算我们定义了一个int并且每一次循环都重新赋值,但是按照我的理解,就算循环n次,但是每一次新的循环开始上一次声明的int值就会被回收,这样应该复杂度还是O(1)才对。。。。这里存疑!!!!!!!


所以大家也不要盲目相信网上的文章,毕竟不是官方的。自己稍微验证一下总是好的。共勉!


那么今天来为大家介绍第二种算法,选择排序。这个算法应该是平时排序的时候能用到最多了的吧。。。我反正一般第一时间就是想到这个哈哈。


择排序(Selection Sort)

应该是最容易被想到的排序方法了,原理如下

  1. 在所有未排序的数组中遍历一次找到最小(大)值,然后把找到的值放到数组的最左(右)边。

  2. 在剩下无序区域继续执行步骤一的操作

  3. 若数组长度为n,则第n-1次会完成完成排序。


动图帧数太大使用不了, 可以直接使用下面的链接进去看一下。很直观易懂


https://upload-images.jianshu.io/upload_images/17400545-e3a784e614fca758.gif?imageMogr2/auto-orient/strip|imageView2/2/w/811/format/webp


不跟你多BB,直接上代码

 /** * 每次找到最小值,并记录index, 然后和当前无序区最左边的数进行交换 * @param nums 无序数组, nums.length >= 2 */ public static void selectionSort(int[] nums) {
int left = 0; //为什么是length-1: 因为如果只剩下一个数的时候就不需要比了,因为只剩一个数了 while (left < nums.length - 1) { int minValIndex = left; for (int i = left; i < nums.length; i++) { if (nums[i] < nums[minValIndex]) { minValIndex = i; } } int temp = nums[left]; nums[left] = nums[minValIndex]; nums[minValIndex] = temp; left++; } }

上面是按照算法描述直接进行实现得到的代码,感觉确实是很直观,没什么可解释的了十大排序算法之选择排序

唯一的一个地方就是循环条件那个地方,我们可以看到最外层我是写的left < nums.length -1,但是里面那层是写的 i < nums.length


这是因为left是代表左边的有序区已经包含了left个元素,所以当我们的left已经等于倒数第二个元素下标时候,就不需要再进循环了,因为只剩下一个元素了,可以默认放到最后面。


但是内层却不能这么写,因为内层是无序区,你怎么能保证最后一个元素不是这次循环的最小值呢?所以必须是  i < nums.length;


PS:反正无论什么排序算法,写代码的时候把那个下表整明白了你就完成一大半了


但是很多人一定问那我可以最大值和最小值同时排序啊!!

果然天才们的思想都是比较类似,我反正是读这个算法的描述的时候就想的是最大值和最小值一起排。上面那个代码纯粹是为了凑字数哈哈


 /** * 每次同时找到最大和最小值,并交换位置到当前无序区的头部和末尾 * @param nums 无序数组, nums.length >= 2 */ public static void selectionSort1(int[] nums) { int left = 0, right = nums.length - 1; int minValIndex, maxValIndex; while (left < right) { minValIndex = left; maxValIndex = right;            //这里一定要注意是<=,因为我们的right=nums.length - 1 for (int i = left; i <= right; i++) { if (nums[i] > nums[maxValIndex]) { maxValIndex = i; } if (nums[i] < nums[minValIndex]) { minValIndex = i; } }
//交换最小值的位置到最左边 int temp = nums[left]; nums[left] = nums[minValIndex]; nums[minValIndex] = temp; if (left == maxValIndex) { maxValIndex = minValIndex; } //交换最大值的位置到末尾 temp = nums[right]; nums[right] = nums[maxValIndex]; nums[maxValIndex] = temp; left++; right--; } }


这个代码说实话, 我写了一个多小时十大排序算法之选择排序 我在写完之后自己写了一些测试用例,结果就一直测不过。。肉眼找了将近一个小时才发现问题。

犯了两个错误:

  1. 在while里面的那层for循环一定是 i <= right 而不是 i < right

    因为我的right取值不是nums.length而是nums.length-1, 

    心里一万匹草泥马崩腾而过有没有十大排序算法之选择排序十大排序算法之选择排序十大排序算法之选择排序

  2.  我每一次都是先交换的最小值,然后再交换最大值。但是如果最大值开始就在最左边的话,一定要将最大值的下标还原。因为当我们交换最小值到最左边的之后,就相当于把最大值换到了minValIndex这个下标的位置,所以这里一定要加下面这段代码。否则你就测吧,哭死你

 if (left == maxValIndex) {     maxValIndex = minValIndex; }

 

复杂度和稳定性分析
稳定性:很明显不稳定,就比如[3, 1, 3],排序完就变成了[1, 33], 红3排序完之后到了绿3的后面。

时间复杂度:这个算法无论是何种情况复杂度都是O(n^2),所以个人认为数据规模小的时候用用可以,大的话就换别的吧。

空间复杂度:O(1),没有用到额外的数据结构或内存空间