冒泡排序
为了方便,本文所有的排序皆为升序
冒泡排序是指:
- 将数组中相邻的元素两两比较,若不符合条件,则交换位置,例如升序排序,如果arr[4] > arr[5],则交换位置
- 交换位置之后,应当继续将上一次交换中较小的元素与前一个元素比较,重复上面的过程,直到符合要求
代码实现:
// 冒泡排序
function bubbleSort(arr) {
const length = arr.length
// i < num 即i执行num次,而冒泡总是两两对比,最后一个数是不需要再往后对比的,因此对比length-1次
for (let i = 0; i < length - 1; i++) {
// 因为本方法是让最大值冒泡到最后,即冒泡外层循环将最大值排在末尾,
// 所以每次循环从头开始,而处理过的末尾固定为正确,不需要再比较,通过控制j的最大值掐掉这段末尾
for (let j = 0; j < length - 1 - i; j++) {
swap(arr, j, j + 1)
}
}
}
选择排序
选择排序是指:
- 将数组中的元素与第一个元素比较,若不符合条件,则交换位置,例如升序排序,第一个元素应当是最小值,则对比中,比第一个元素小的其他元素,需要和第一个元素交换位置
- 将最小值确定位置之后,接下来是第二小,以此类推,将所有元素固定到应该的位置上
代码实现:
// 选择排序
function selectionSort(arr) {
const length = arr.length
// 每个都是跟最小值比较,而最小值不跟自己比较,最小值在内层循环中不变,且i从0开始,且小于length-1
for (let i = 0; i < length - 1; i++) {
// 从最小值位置+1开始,遍历到最后一个元素
for (let j = i + 1; j < length; j++) {
if (arr[j] < arr[i]) {
swap(arr, j, i)
}
}
}
}
插入排序
插入排序是指:
- 遍历数组,将每个元素(origin)和他之前的元素(arr[j])比较,如果之前的元素比他大,则将之前的元素的值赋值给他后面的那个元素(arr[j+1] = arr[j])
- 如果比他小,则将origin赋值给arr[j+1]
光看文字比较难理解,这里推荐一个网站,算法可视化
代码实现:
// 插入排序
function insertSort(arr) {
const length = arr.length
// 根据插入算法规则,第一次对比是第二个元素对比第一个,
// 也就是说,循环从第二个开始,应当let i = 1
for (let i = 1; i < length; i++) {
// 分两种情况
// 1.子循环未结束,origin就找到位置,origin会插入到比他小的元素的后面,
// 在代码中,它会在循环的下一次开始时赋值,并跳出
let origin = arr[i]
for(let j = i - 1; j >= 0; j--) {
if(arr[j] < origin) {
arr[j + 1] = origin
break
} else {
arr[j + 1] = arr[j]
// 2.子循环已结束,origin未找到位置,这种情况,是因为origin需要排在第一个,没有比他小的,
// 而循环将原第一个元素赋值给第二个之后,j-- == -1,循环结束了,必须作出处理
if(j === 0) arr[0] = origin
}
}
}
}
归并排序
归并排序是指:
- 将长度为n的数组拆解成两个数组,分别为left和right,然后再讲left和right拆解,直到将原数组的每个元素都拆为单独的一个数组
- 将这些单独的数组两两对比,返回两者合并的升序的数组,然后再将这些合并的数组,按照升序将其中的元素排序并合并,直到返回完成的一个数组
代码实现:
// 归并排序
// 整体从拆散到合并
function megerSort(arr) {
if (arr.length > 1) {
let middle = Math.floor(arr.length / 2)
let left = arr.splice(0, middle)
let right = arr
// 有点类似于二叉树的后序遍历
left = megerSort(left)
right = megerSort(right)
return meger(left, right)
} return arr
}
//合并两组升序数组
function meger(left, right) {
let leftIndex = 0
let rightIndex = 0
const arr = []
// 当合并的是分解到最后一层,也就是左右都只有一个元素时
if (left.length === 1 && right.length === 1) {
left[0] < right[0] ?
arr.push(left[0], right[0]) :
arr.push(right[0], left[0])
return arr
}
// 当合并的并非是最后一层,我们要通过不断地将左右中最小的值取出,排到从左到右位置上
// 使用arr记录顺序,arr的长度应该小于 left.length + right.length - 1
while (arr.length < (left.length + right.length - 1)) {
// 这块用于判断是否有一边走完,处理另一边
// 因为left和right都是升序数组,所以走完的数组的最大值一定小于没走位的数组的走到的那个值
if(leftIndex === left.length - 1 && right[leftIndex] > left[left.length - 1]) {
arr.push(left[leftIndex])
return arr.concat(right.slice(rightIndex))
}
if(rightIndex === right.length - 1 && left[leftIndex] > right[right.length - 1]) {
arr.push(right[rightIndex])
return arr.concat(left.slice(leftIndex))
}
// 普通情况处理
if (left[leftIndex] < right[rightIndex]) {
arr.push(left[leftIndex])
leftIndex++
} else {
arr.push(right[rightIndex])
rightIndex++
}
}
return arr
}