选择排序(select sorting)基本思想是:第一次从arr[0] ~ arr[n-1]中选取最小值,与arr[0]交换;第二次从arr[1] ~ arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]~arr[n-1]中选取最小值,与arr[2]交换…第i次从arr[i-1] ~ arr[n-1]中选取最小值,与arr[i-1]交换…,第n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。
二、例如主要思想就是假设第一个数据是最小值,然后跟右侧对比,看看谁最小,谁最小就标记谁,然后拿着标记值(最小值)再去和右边找,就这样一直找一直找,最后找到一个在全部数据中的最小值,与第一个数据互换位置,第二次找就从第二个位置开始(因为第一个位置已经是最小值了),以此类推。
原始数据:
[10, 34, 100, 1]
第一次选择排序:
[1,34,100,10]
先假设最小值是第一个数据,也就是10,然后先拿10和34对比大小,10<34,不处理,继续拿10和100比较,10<100,不处理,继续拿10和1比较,10>1,重新记录下最小值是1,发现1右边没数据了,不能对比,所以循环结束,将10和1的位置互换即可。
第二次选择排序:
[1,10,100,34]
这次要从第二个数据开始了,所以先假设最小值是第二个数据34,然后拿34和100对比,34<100,不处理,在拿34和10对比,34>10,重新记录当前最小值为10,发现10右边没数据了,不能对比,所以循环结束,将34和10的位置互换即可。
第三次选择排序:
[1,10,34,100]
三、coding这次要从第三个数据开始了,所以先假设最小值是第三个数据100,然后拿100和34对比,100>34,重新记录当前最小值为34,发现34右边没数据了,不能对比,所以循环结束,将100和34的位置互换即可。
1、lowb演示版
package com.chentongwei.struct.sort;
import java.util.Arrays;
/**
* Description:
* 10, 34, 100, 1
*
* @author TongWei.Chen 2019-12-31 11:05:18
*/
public class SelectorSort {
public static void main(String[] args) {
int[] arr = {10, 34, 100, 1};
selectSort(arr);
}
public static void selectSort(int[] arr) {
// 逐步推导的方式讲解选择排序
// 先假定最小值是下标为0的元素
int minIndex = 0;
int minValue = arr[minIndex];
// 从当前数之后的数比较,所以是0 + 1
for (int j = 0 + 1; j < arr.length; j++) {
if (minValue > arr[j]) {
// 代表假定的最小值并不是最小值,需要重置下minIndex和minValue
minValue = arr[j];
minIndex = j;
}
}
// 将最小值放在arr[0]的位置,把arr[0]的值放在最小值的位置,也就是交换一下
if (minIndex != 0) {
arr[minIndex] = arr[0];
arr[0] = minValue;
}
System.out.println("第一轮后:");
System.out.println(Arrays.toString(arr));
// 先假定最小值是下标为1的元素
minIndex = 1;
minValue = arr[minIndex];
// 从当前数之后的数比较,所以是1 + 1
for (int j = 1 + 1; j < arr.length; j++) {
if (minValue > arr[j]) {
// 代表假定的最小值并不是最小值,需要重置下minIndex和minValue
minValue = arr[j];
minIndex = j;
}
}
// 将最小值放在arr[1]的位置,把arr[1]的值放在最小值的位置,也就是交换一下
if (minIndex != 1) {
arr[minIndex] = arr[1];
arr[1] = minValue;
}
System.out.println("第二轮后:");
System.out.println(Arrays.toString(arr));
// 先假定最小值是下标为1的元素
minIndex = 2;
minValue = arr[minIndex];
// 从当前数之后的数比较,所以是1 + 1
for (int j = 2 + 1; j < arr.length; j++) {
if (minValue > arr[j]) {
// 代表假定的最小值并不是最小值,需要重置下minIndex和minValue
minValue = arr[j];
minIndex = j;
}
}
// 将最小值放在arr[2]的位置,把arr[2]的值放在最小值的位置,也就是交换一下
if (minIndex != 2) {
arr[minIndex] = arr[2];
arr[2] = minValue;
}
System.out.println("第三轮后:");
System.out.println(Arrays.toString(arr));
}
}
第一轮后:
[1, 34, 100, 10]
第二轮后:
[1, 10, 100, 34]
第三轮后:
[1, 10, 34, 100]代码很low,就是为了演示说明选择排序是怎么回事,可以发现代码几乎相同,只是某个变量不同而已。
2、优化版
package com.chentongwei.struct.sort;
import java.util.Arrays;
/**
* Description:
* 10, 34, 100, 1
*
* @author TongWei.Chen 2019-12-31 11:05:18
*/
public class SelectorSort {
public static void main(String[] args) {
int[] arr = {10, 34, 100, 1};
selectSortNew(arr);
}
public static void selectSortNew(int[] arr) {
// 原始数组:[101, 34, 119, 1]
for (int i = 0; i < arr.length - 1; i++) {
// 先假定最小值是下标为i的元素
int minIndex = i;
int minValue = arr[minIndex];
// 从当前数之后的数比较,所以是i + 1
for (int j = i + 1; j < arr.length; j++) {
if (minValue > arr[j]) {
// 代表假定的最小值并不是最小值,需要重置下minIndex和minValue
minValue = arr[j];
minIndex = j;
}
}
// 将最小值放在arr[0]的位置,把arr[0]的值放在最小值的位置,也就是交换一下
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = minValue;
}
System.out.println("第"+ (i + 1) +"轮后:");
System.out.println(Arrays.toString(arr));
}
}
}
四、测试效率第1轮后:
[1, 34, 100, 10]
第2轮后:
[1, 10, 100, 34]
第3轮后:
[1, 10, 34, 100]将上面lowb版进行升级,也就是将变量部分换成了一层for来解决。时间复杂度O(n2)
测试10w条数据,选择排序需要花费时间。
package com.chentongwei.struct.sort;
import java.util.Arrays;
/**
* Description:
* 10, 34, 100, 1
*
* @author TongWei.Chen 2019-12-31 11:05:18
*/
public class SelectorSort {
public static void main(String[] args) {
// 测试选择排序的效率,假设给10w个数据测试
int[] arr = new int[100000];
for (int i = 0; i < arr.length; i ++) {
// 生成【0,8000000】之间的一个随机数
arr[i] = (int)(Math.random() * 8000000);
}
long startTime = System.currentTimeMillis();
selectSortNew(arr);
long endTime = System.currentTimeMillis();
System.out.println("选择排序10w个数据所需要的时间是:" + (endTime - startTime) / 1000);
}
public static void selectSortNew(int[] arr) {
// 原始数组:[101, 34, 119, 1]
for (int i = 0; i < arr.length - 1; i++) {
// 先假定最小值是下标为i的元素
int minIndex = i;
int minValue = arr[minIndex];
// 从当前数之后的数比较,所以是i + 1
for (int j = i + 1; j < arr.length; j++) {
if (minValue > arr[j]) {
// 代表假定的最小值并不是最小值,需要重置下minIndex和minValue
minValue = arr[j];
minIndex = j;
}
}
// 将最小值放在arr[0]的位置,把arr[0]的值放在最小值的位置,也就是交换一下
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = minValue;
}
}
}
}
选择排序10w个数据所需要的时间是:6
可以发现只需要6s就能排序完10w条数据,对比上一篇的冒泡来讲简直神速,冒泡10w条需要24s。四倍的提升。具体原因是什么呢?是因为选择排序不需要每次都互换位置,而是内层循环完成后找到了最小值后,外层循环才开始互换位置,而冒泡是每次内层循环都互换一次位置。这里的互换位置并不是简单的赋个值就影响效率了,而是每一次都互换位置必然导致进入双层for循环里的if条件的次数过多。因为每一次都呼唤位置会导致数据每次都发生变化,满足if判断的次数也就会变多。对比冒泡的缺点就是选择不能优化,时间复杂度最好最坏都是O(n2),而冒泡可以优化到最好时间复杂度为O(n)。