这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
今天我给大家介绍常见三种排序算法,冒泡排序、插入排序和选择排序,他们的算法复杂度都是O(n^2)
冒泡排序
冒泡排序是所有排序算法中最简单的一个,但是性能也是最差的一个。冒泡排序比较的所有相邻的两个项,如果第一个比第二个大,则交换他们,就像气泡冒升至表面一样,所以也叫冒泡排序。
实现思路:两层循环,外循环控制循环次数,进行多少轮,每轮内循环进行相邻两项的比较,是否进行交换
const bubbleSort = (array, compareFn = defaultCompare) => {
// 控制循环多少次
for (let i = 0; i < array.length; i++) {
// 每轮循环挨个比较
for (let j = 0; j < array.length - 1 - i; j++) {
// 满足条件,进行交换
if (compareFn(array[j], array[j + 1])) {
[array[j], array[j + 1]] = [array[j + 1], array[j]];
}
}
}
return array;
}
const array = [1, 5, 3, 4, 6, 8]
console.log(bubbleSort(array)); // [1, 3, 4, 5, 6, 8]
其中使用了defaultCompare函数,用来给用户自定义比较函数,来升序还是降序排列,默认(升序)如下:
const defaultCompare = (a, b) => {
if (a === b) return 0;
return a > b ? true : false;
}
至此,我们简单的冒泡排序就实现了,两层循环复杂度就是O(n^2),降低不了复杂度,可我们还有其他方式优化嘛?比如数组hai1是[1, 5, 3, 4, 6, 8],我们可以看到,该数组基本是有序的,只需要执行两次,把数字5放到该放的位置就好了,就完成有序[1, 3, 4, 5, 6, 8],就不需要在进行接下来的四轮比较了,接下来的比较是不会交换元素的。怎么改呢?将算法优化如下:
const bubbleSort = (array, compareFn = defaultCompare) => {
for (let i = 0; i < array.length; i++) {
console.log(i); // 0, 1
let flag = true
for (let j = 0; j < array.length - 1 - i; j++) {
if (compareFn(array[j], array[j + 1])) {
[array[j], array[j + 1]] = [array[j + 1], array[j]];
flag = false;
}
}
if (flag) {
break;
}
}
return array;
}
const array = [1, 5, 3, 4, 6, 8]
console.log(bubbleSort(array)); // [1, 3, 4, 5, 6, 8]
再次执行代码,在打印 i 的值,只会打印0,1两次,不会进行多余的循环,达到了我们的需求,至此,我们优化后的冒泡排序就实现了。
插入排序
插入排序每次排一个数组项,以此方式构建最后的排序数组。假设第一项已经有序,接着将第二项与他比较,第二项是否该移动位置,插入到第一项前呢?这样就确定了两项,然后依次按照第二项的方式比较,类推。
const insertionSort = (array, compareFn = defaultCompare) => {
const length = array.length;
let temp;
// 从第二项开始比较
for (let i = 1; i < length; i++) {
let j = i;
temp = array[j]; // 记录下开始的位置,待比较的数据
// 将前面的每一项(以有序数组)与待比较数据进行比较,看是否需要交换
while(j > 0 && compareFn(array[j - 1], temp)) {
array[j] = array[j - 1];
j--;
}
// 将此轮待比较数据插入应该的位置
array[j] = temp;
}
return array;
}
const array = [1, 5, 3, 4, 6, 8]
console.log(insertionSort(array)); // [ 1, 3, 4, 5, 6, 8 ]
选择排序
选择排序是一种原址比较排序算法。他的大概思路就是找到数据结构中最小的值,将它放在第一位,接着将第二小的放在第二位,依此类推。
const selectionSort = (array, compareFn = defaultCompare) => {
const length = array.length;
let indexMin; // 记录最小值的索引
for (let i = 0; i < length - 1; i++) {
// 每次开始时的起始项最小(数组分为前后两个数组,后面的是未有序的,假设未有序中第一项最小)
indexMin = i;
// 遍历未有序数组,j = i说明前 i - 1 项是有序的,不需要比较,节省性能
for (let j = i; j < length; j++) {
// 最小值索引大于当前值,更新最小值索引
if(compareFn(array[indexMin], array[j])){
indexMin = j;
}
}
// 将第 i 小的值,放在第 i 项
if (i !== indexMin) {
[array[i], array[indexMin]] = [array[indexMin], array[i]]
}
}
return array;
}
const array = [1, 5, 3, 4, 6, 8]
console.log(selectionSort(array)); // [ 1, 3, 4, 5, 6, 8 ]
今天我们就介绍了这三种算法,下次我们来看快速排序和归并排序这两个算法复杂度更低的算法。