前言
此文衍生自《【JS算法】排序算法》,针对选择排序的稳定性进行补充讲解。排序算法的稳定性在《【JS算法】排序算法》已经进行讲解,此处不再重复。文中用 JavaScript 实现算法,讲述选择排序算法的基本实现,验证此算法的基本实现是不稳定的,同时扩展,如何实现稳定版的选择排序
基本实现
思路
- 遍历数组中未排序的元素,找出最小(大)值,放在第一个位置
- 重复第一步操作,直到所有元素均排序完毕
代码
// 算法的基本实现
var arraySort = function(arr){
let min
let temp
for(let i = 0; i < arr.length - 1; i++){
min = i;
// 从未排序的数据中寻找最小值
for(let j = i + 1; j < arr.length; j++){
if(arr[min] > arr[j]){
min = j
}
}
// 将最小值放到数据的第一个位置
if(min != i){
temp = arr[i];
arr[i] = arr[min]
arr[min] = temp
}
}
}
不稳定验证
对上面代码进行修改,对对象数组,基于 price 属性进行递增排序
- 声明数组
// 数组
var arr = [
{
price: 11,
value: 453
},
{
price: 123,
value: 456
},
{
price: 12,
value: 457
},
{
price: 11,
value: 459
},
{
price: 10,
value: 460
}
]
- 改造函数
// 代码第 8 行,原来比较自身,现在改为比较内部属性的 price
var arraySort = function(arr){
let min
let temp
for(let i = 0; i < arr.length - 1; i++){
min = i;
// 从未排序的数据中寻找最小值
for(let j = i + 1; j < arr.length; j++){
if(arr[min].price > arr[j].price){
min = j
}
}
// 将最小值放到数据的第一个位置
if(min != i){
temp = arr[i];
arr[i] = arr[min]
arr[min] = temp
}
}
}
arraySort(arr)
- 打印结果
// 调用 arraySort 进行排序后,arr 打印出来的结果如下
// [
// {
// price: 10,
// value: 460
// },
// {
// price: 11,
// value: 459
// },
// {
// price: 11,
// value: 453
// },
// {
// price: 12,
// value: 457
// },
// {
// price: 123,
// value: 456
// }
// ]
- 从上面代码可看出, 排序前
{price: 11, value: 453}
在{price: 11, value: 459}
前面,排序后{price: 11, value: 453}
在{price: 11, value: 459}
后面,所以此实现是不稳定的
稳定实现
思路
- 补充数组 minArray,使用首元素与 arr 中的元素进行比对时,如果当前元素与首元素相等,将 key 存储到 minArray
- 寻找完毕如果 minArray 的长度大于 1,则证明有重复的元素
- 将 arr 中在 minArray 存储了 key 且 key < min 的元素做逆时针移动,这样可以保证与首元素相等的元素的相对位置不变,同时用与 min 最接近的和首元素相等的元素代替首元素的位置,保证在与 min 替换后,与首元素相等的元素之间的相对位置依然不变
- 最后执行原有步骤中的位置替换操作,然后清空 minArray,避免污染下一轮循环
代码
var arraySort = function(arr){
let min
let temp
// 声明数组
let minArray = []
for(let i = 0; i < arr.length - 1; i++){
min = i
// 存放第一个元素的 key
minArray.push(i)
for(let j = i + 1; j < arr.length; j++){
if(arr[i].price === arr[j].price){
// 当第一个元素与当前元素相等时,存放当前元素的 key
minArray.push(j)
}
if(arr[min].price > arr[j].price){
min = j
}
}
if(min != i){
if(minArray.length > 1){
// 将元素做逆时针移动
// 将 minArray 中存储的并且小于 min 的 key 对应 arr 中的元素逐个后移,然后将第一个被替换的元素存储,放置第一个
for(let k = minArray.length - 1; k > 0; k--){
if(minArray[k] > min){
continue
}
// 如果 temp 为空 temp 就将当前的元素存放,这样就达到保留第一个元素的目的
if(temp === undefined){
temp = arr[minArray[k]]
}
// 将前面的元素逐个后移
arr[minArray[k]] = arr[minArray[k - 1]]
}
// 然后将 temp 赋值给最后一个
arr[minArray[0]] = temp
}
// 执行完上面的操作后,再将最小的值与第一个值互换
temp = arr[i]
arr[i] = arr[min]
arr[min] = temp
}
minArray = []
}
}
arraySort(arr)
// 调用 arraySort 进行排序后,arr 打印出来的结果如下
// [
// {
// price: 10,
// value: 460
// },
// {
// price: 11,
// value: 453
// },
// {
// price: 11,
// value: 459
// },
// {
// price: 12,
// value: 457
// },
// {
// price: 123,
// value: 456
// }
// ]
总结
以上代码实现了选择排序的稳定版,通过消耗多余的资源来保证原有位置不被改变,可能并不是最优解,欢迎评论指导