冒泡排序
使用两重for循环,将数组中的元素进行逐个两两对比,第一重for循环用于次数统计,一共要循环arr.length-1次,第二重for循环用于元素两两比较,一共需要进行arr.length - i -1 次,其中, -i,因为之前排序好的元素(一轮比较下来最大或最小的元素)会放在数组的最末尾,因此不必比较 已排好的 i个,-1是因为在循环内取值是取 j和j+1项进行比较,因此需要-1不让数组访问越界。在循环内比较值如果满足条件,使用ES6的解构语法进行值的交换即可。
var arr = [105, 100, 70, 20, 50, 5, 6];
// 冒泡排序
function bubbleSort(arr) {
for (let i = 0; i < arr.length; i++) { //冒泡排序需要进行length-1轮,每轮通过两两比较得出最大值然后冒泡到数组末尾
for (let j = 0; j < arr.length - i -1; j++) { //这层遍历由于数组末尾i项是以排序好的,无需访问,-1是由于需要两两比较,下面需要+1,所以for里面需要-1防止数组越界
if (arr[j] > arr[j + 1]) { // 当后来值大于前面的值,两两交换,最终实现每轮将最大值冒泡到数组最末尾
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; //ES6解构语法进行数组元素两两交换
}
}
}
}
var newArr = bubbleSort(arr);
选择排序
使用两重for循环,第一重for循环的i下标值到length-1,因为选择排序前面的值已排序,所以最后一个值是已经有序,不需排列,因此不必到最后一个值。每次i下标往后移,默认最开始的i为最小值的下标minIndex,通过第二重for循环,将minIndex代表的元素值与循环的每一个值(从i+1处开始,因为前面的值以排序)作比较,得到小的下标值 j 则更新minIndex,第二重for循环每轮结束后会得到最小值的下标,将其与i下标值进行交换即可。
// 选择排序
function selectSort(arr) {
let minIndex; //用于标记每轮循环最小的值的下标
for (let i = 0; i < arr.length - 1; i++) { //由于选择排序前面的值已排序,所以最后一个值是已经有序,不需排列,因此不必到最后一个值
let minIndex = i; //每次i下标往后移,默认最开始的i为最小值的下标
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j; //找到比minIndex下标里值还要小的值,更新最小值的下标
}
}
// 一轮循环下来,已获取到i位置应为minIndex的值,将i位置和minIndex下标的值进行交换
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
}
}
插入排序
类似于纸牌游戏中,选出一张牌,依次进行比较,最后插入合适的位置中。JS实现,使用一重for循环+while循环,首先第一重循环是拿出数组中的值,然后使用preIndex用来表示前面的值的下标,current表示当前i下标的值,使用while循环进行逐一比较,条件是,preIndex>=0,防止数组访问越界(<0),满足arr[preIndex]>current方可进行交换,此时需要将arr[preIndex+1] = arr[preIndex]进行赋值,即将preIndex下标的值赋给它后一位,然后继续往前查找,走出while循环时,表明此时preIndex的值小于或等于i下标的值,将preIndex+1索引下的值赋为current即可。
//插入排序
function insertSort(arr) {
let current; //存放for循环中拿出来进行比较的值
let perIndex; //存放for循环下标 i前的索引值
for (let i = 0; i < arr.length; i++) {
preIndex = i - 1; //起始下标值为i前一个下标
current = arr[i]; //将for循环中i下标的值暂存为current
while (preIndex >= 0 && arr[preIndex] > current) {
//进入while循环的条件,需preIndex>=0,防止访问数组越界,同时arr[preIndex]值需要大于current
arr[preIndex + 1] = arr[preIndex]; //将前面较大的值传递给后一位,此时,最初的preIndex+1为i下标,而i下标的值已存储为current,不会发生覆盖,
//而如果它不发生后移传递值,说明前面的值小于current,不会进入while循环,因此也不会发生覆盖,即进入while循环,说明前面的值需要往后移,而每次移动完,preIndex值就需要-1
preIndex--; //preIndex--表示这个preIndex下标的值已比较且移动赋值完,前面的比较可以覆盖这个preIndex下标的值,因为它已经在preIndex+1下标的位置赋值了。
}
// while循环结束,说明此时preIndex下标的值会小于current,不再需要往前比较,将current即一开始存储的arr[i]的值赋在preIndex+1下标处即可
arr[preIndex + 1] = current;
}
}
快速排序
1.以一个数为基准(中间的数),比基准小的放到左边,比基准大的放到右边
2.再按此方法对这两部分数据分别进行快速排序(递归进行)
3.不能再分后退出递归,并重新将数组合并
// 快速排序
function quickSort(arr) {
if (arr.length <= 1) {
return arr; //递归到只剩一个元素或空数组,直接返回
//假如取到的基准数就是最小的或者最大的,这时候left数组或者right数组就是空的
}
let middleIndex = Math.floor(arr.length / 2); //以中间下标为基准值,Math.floor表示向下取整
let middle = arr.splice(middleIndex, 1)[0]; //将中间下标的元素裁剪出来,此时arr数组已经没有中间基准元素,此时返回裁剪出来的元素数组,需要使用下标0将其取出
let left = []; //定义左边数组,用来存放比基准元素小的值
let right = []; //定义右边数组,用来存放比基准元素大的值
// forEach取出数组每个元素与基准值比较
arr.forEach(item => {
if (item < middle) {
left.push(item);
} else {
right.push(item);
}
})
// 本次arr排序完毕,即 [left] middle [right]
// 接下来传入left数组,和right数组,进行递归的快速排序,直到数组元素只有一个
return quickSort(left).concat([middle], quickSort(right))
}
参考文章:js十大排序算法