JavaScript 中常用的排序
start
- 排序是日常编码中经常遇到的需求。
- 写这篇文章的目的,就是希望自己在写完这篇文章之后,能够非常熟悉常见的排序。
1. sort
JavaScript 中数组自带 sort 方法。
注意事项:
- sort 方法默认排序顺序为按字母升序。
- 使用数字排序,你必须通过一个函数作为参数来调用。
利用函数指定数字是按照升序还是降序排列。
使用示例
console.log([1, 2, 20, 45, 7, 3, 4].sort())
// [1, 2, 20, 3, 4, 45, 7]
console.log([1, 2, '40', '5', 7, 3, 4].sort())
// [1, 2, 3, 4, '40', '5', 7]
console.log(
[1, 2, '40', '5', 7, 3, 4].sort((a, b) => {
return a - b
})
)
// [1, 2, 3, 4, '5', 7, '40']
冒泡排序
手写冒泡排序1
function bubbleSort(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j + 1]
arr[j + 1] = arr[j]
arr[j] = temp
}
}
}
return arr
}
console.log(bubbleSort([1, 2, 3, 4, 5, 3, 4, 6, 8, 2]))
上述的代码,是最基础的冒泡排序。
实现原理就是:双重 for 循环,内层的循环实现相邻的两项进行对比,外层循环每执行完一次,都会筛选出当前数组中最大的项。
手写冒泡排序2
function bubbleSort2(arr) {
var i = arr.length - 1
while (i > 0) {
var index = 0
for (var j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
index = j
var temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
i = index
}
return arr
}
console.log(bubbleSort2([1, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 2, 1, 4]))
// [1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8]
// 从后向前遍历,记录最后一个交换的位置。节约不用交换的步骤的时间。
上述的代码,是优化后的冒泡排序。
实现原理:双层循环遍历,内层循环实现了相邻两项的对比,相对于 手写冒泡排序1
,它会记录最后交换位置的索引。外层循环变成了从后向前遍历,并且跳过没有被交换的索引。
我的思考?
- 对于循环的场景,不要太过于依赖 for 循环。某些场景,while 更加合适和好用。
- 优化的思路可以多考虑考虑,去除无用的循环次数?
快速排序
var quickSort = function (arr) {
if (arr.length <= 1) {
return arr
}
var left = []
var right = []
var point = Math.floor(arr.length / 2)
var pointElement = arr.splice(point, 1)[0]
for (var i = 0; i < arr.length; i++) {
if (arr[i] < pointElement) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([pointElement], quickSort(right))
}
var arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
console.log(quickSort(arr))
上述的代码,是快速排序。
实现原理:for 循环加递归,每次递归从数组中间抽取一项,比中间项小的项,放左侧数组,比中间项大的项,放右侧数组。递归到数组长度为 1 的情况返回数组。
我遇到的问题?
自己手写这里的代码的时候,这里的取中间元素,我没有使用 splice,将项截取出来。
var pointElement = arr.splice(point, 1)[0]
var pointElement = arr[point]
如果不取出中间的元素,假如:获取到的中间元素是最大的或者最小的元素。 那么左右两个数组会出现 [] [1,2,3,…]的情况,无限递归处理 [1,2,3,…] 就会陷入死循环,直到爆栈。
选择排序
function selectionSort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
var minIndex = i
for (var j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
var temp = arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = temp
}
return arr
}
var arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
console.log(selectionSort(arr)) //[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
上述的代码,是选择排序。
实现原理:双层 for 循环,内层每循环一次,会找出一个最小值,然后存储在头部。直到最后一项。
我遇到的问题?
- 注意外层循环的边界值,由于内层循环总是从后一项开始选择,所以外层循环需要最大值需要减一。
- 注意,最小索引
minIndex = i
,每次比较的值是 i
插入排序
function insertionSort(arr) {
for (var i = 1; i < arr.length; i++) {
var key = arr[i]
var j = i - 1
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j]
j--
}
arr[j + 1] = key
}
return arr
}
var arr2 = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
console.log(insertionSort(arr2)) //[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
上述的代码,是插入排序。
实现原理:双层循环,内层循环会将数组每一项向后移,直到匹配到满足条件的数据,将其插入对应的数组位置。
我遇到的问题?
我在手写这个代码的时候,没有定义 key,即: var key = arr[i]
,代码出错。
因为 var j = i - 1
, 然后循环中会修改 arr[j + 1]
的值,导致后续赋值都将不准确。
function insertionSort(arr) {
for (var i = 1; i < arr.length; i++) {
var key = arr[i]
var j = i - 1
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j]
j--
}
arr[j + 1] = key
}
return arr
}
var arr2 = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
console.log(insertionSort(arr2)) //[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
end
- 目前仅考虑熟练盲写,满足日常编程的需求,加油!
- 本文编写时间: 2022/11/17-2022/11/19