原理
- 从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置。
- 从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。
- 以此类推,直到全部待排序的数据元素的个数为零。
算法分析
- 时间复杂度 :O(n^2)
- 空间复杂度:O(1)
算法实现
selectionSort = (arr) => {
// 判断边界
if (arr == null || arr.length < 2) {
return;
}
for (let i = 0; i < arr.length; i++) {
let minIndex = i
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
minIndex = j
}
}
if (i != minIndex) {
swap(arr, i, minIndex)
}
}
return arr
}
// 交换位置
swap = (arr, i, j) =>{
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
// let arr = [1, 4, 2, 3, 5, 9, 7, 8, 0]
let arr = [ 0, 1, 2, 3, 4, 5, 7, 8, 9]
console.log(selectionSort(arr))
优化
之前遍历一次只是找出了最小值或者最大值,若是一次将最大值和最小值全都找出,是不是就节省了遍历次数,虽然复杂度仍然是O(n^2),但是时间会减少,请看以下代码
selectionSort1 = (arr) => {
// 判断边界
if (arr == null || arr.length < 2) {
return;
}
let left = 0
let right = arr.length - 1;
while (left < right) {
// 假设最大值和最小值
let minIndex = left
let maxIndex = right
for (let i = left; i <= right; i++) {
// 若当前索引值比最小值还要小,则更新最小值索引
if (arr[i] < arr[minIndex]) {
minIndex = i
}
// 若当前索引值比最大值还要大,则更新最大值索引
if (arr[i] > arr[maxIndex]) {
maxIndex = i
}
}
// 当前循环最大值索引和最小值索引分别为maxIndex,minIndex
// 先交换最小值
if (minIndex != left) {
swap(arr, minIndex, left)
}
// 若最小值交换前最大值在left处时,即left是最大值maxIndex,
// 但是由于第一次最小值交换,将真正的最小值放入left处,而left处的最大值被放到了交换前最小值处minIndex,最大值被掉包了
// left索引值此时并不是最大值了,因此,需要重新将最小值赋值给最大值
if (left == maxIndex) {
maxIndex = minIndex
}
// 交换最大值
if (maxIndex != right) {
swap(arr, maxIndex, right)
}
left++
right--
console.log(arr);
}
return arr
}
优化的难点
在交换了最小值后,之前的排列已经不稳定了,原本索引对应的值都已经发生变化,若直接交换最大值,可能造成真正的最大值最小值掉包,请看下面分析
let arr = [11, 0, 1, 2, 3, 4, 5, 7, 8, 9]
//上面数组中最大值在第一个位置,此时假设最小值索引是0,最大值索引是9.
//经过第一轮循环后,找出最小值索引是1,最大值索引是0.
//接着进行最小值交换,将索引1的值放到索引0处,索引0处的值放到了索引1处。
//但是此时最大值的索引仍然指向0,然而经过了最小值交换后,索引0处的值并不是了最小值,最小值被放到了索引1处,
//因此在交换最大值前,需要先重新赋值最大值索引,将现在的最小值索引(经过最小值交换后的)赋值给最大值索引。
//最后进行最大值赋值就没有问题了
//后面的循环也是这个意思
//用大白话就是进行最大值交换时的最大值已经被最小值掉包了,需要重新找回真正的最大值
欢迎大脚一起来交流!