常见排序算法有插入排序、冒泡排序、选择排序、快速排序、归并排序、堆排序。以下是我对这些排序的理解和代码实现
1.插入排序
按从左到右的顺序,从下标为1的数开始与前一个进行比较,小则往前移,大则退出循环。
let array = [1, 9, 3, 5, 2, 3, 5, 7, 2];
function arrayInsert(array) {
for (let i = 1; i < array.length; i++) {
let target = i;
console.log("array" + i, array[i]);
for (let j = i - 1; j >= 0; j--) {
console.log("array" + j, array[j]);
if (array[target] < array[j]) { //target目的:在排序一次后还能接着比较排序
let temp;
temp = array[target];
array[target] = array[j];
array[j] = temp;
target = j;
} else {
break;
}
}
}
return array;
}
//输出
let array1 = arrayInsert(array);
for (let i = 0; i < array.length; i++) {
console.log(array1[i]);
}
//结果如预期 1 2 2 3 3 5 5 7 9
2.冒泡排序
将数组从第一个开始,与后面相邻的数比较,如果这个数比后面相邻的数大,则交换位置。(向上冒泡),这一个数比较完成之后,最后那个数应该为最大的数。
接着循环第二个数,已经排好的不参与循环。
直至所有数排好完毕。
考虑当一次循环没有发生冒泡,说明已经排序完成,停止循环。
let array = [1, 9, 3, 5, 2, 3, 5, 7, 2];
function bubbleInsert(array) {
for (let i = 0; i < array.length; i++) {
let target = true;
console.log("arraybefore", array);
for (let j = 0; j < array.length - 1 - i; j++) {
console.log("arrayafter", array);
if (array[j] > array[j + 1]) { //看是否比后一个数大
let temp;
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
target = false;
}
}
if (target) { //如果没有改变target值,则表示数组已经完成冒泡排序,或者传入的数组是排好序的。
break;
}
}
return array;
}
//输出
let array1 = bubbleInsert(array);
for (let k = 0; k < array.length; k++) {
console.log(array1[k]);
}
结果如上。
3.选择排序
两层循环,第一次循环找出最小那个出赋值给array0,第二次循环找出第二小的那个数赋值给array1,依次类推,直至全部排序完毕。
版本一:(错误)
let array = [1, 9, 3, 5, 2, 3, 5, 7, 2];
function selectionSort(array) {
for (let i = 0; i < array.length; i++) {
let target = i; //假设首次最小是下标为i的数,因为默认小的放在了前面
console.log("arraybefore", array);
for (let j = i + 1; j < array.length; j++) {
if (array[j + 1] < array[j]) { //比较哪个大
let temp;
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
target = j;
}
console.log("arrayafter", array);
}
}
return array;
}
//输出
let array1 = selectionSort(array);
for (let k = 0; k < array.length; k++) {
console.log(array1[k]);
}
结果:
我们可以看见这是有问题的,代码只选择小的数往前推,并没有判断是否比之前的更小。
版本二:(正确)
let array = [1, 9, 3, 5, 2, 3, 5, 7, 2];
function selectionSort(array) {
for (let i = 0; i < array.length; i++) {
let target = i; //假设首次最小是下标为i的数,因为默认小的放在了前面
console.log("arraybefore", array);
for (let j = i + 1; j < array.length; j++) {
if (array[j] < array[target]) { //比较哪个大
target = j;
}
}
//把最小的赋给前头
let temp;
temp = array[target];
array[target] = array[i];
array[i] = temp;
console.log("arrayafter", array);
}
return array;
}
//输出
let array1 = selectionSort(array);
for (let k = 0; k < array.length; k++) {
console.log(array1[k]);
}
4.快速排序
选一个数(一般选第一个),作为标杆,将比它小的数存在它的左边,比它打的数存在右边,再分别左右进行第二次排序,直至全部完成。采用递归,分治思想。
方法一:递归
let array = [3, 9, 1, 5, 2, 3, 5, 7, 2];
function quickSort(array) {
//如果数组长度为1,直接返回
if (array.length <= 1) {
return array;
}
//数组长度不为1
const target = array[0];
const left = [];
const right = [];
for (let i = 1; i < array.length; i++) {
// console.log("arrayi", array[i]);
if (array[i] < target) {
left.push(array[i]);
// console.log("left", left);
} else {
right.push(array[i]);
// console.log("right", right);
}
}
return quickSort(left).concat(target, quickSort(right));
}
quickSort(array);
console.log("array", quickSort(array));
这里踩了一个坑,将left.push(array[i])写成了left.push=array[i];这会导致写不进去。
最终结果如前面结果。
方法二:
l,r分别为最左和最右。
在l<r时,右侧找到小于target的array[r],并将其赋值到array[l]
在l<r的条件下,找到左侧大于target的值array[l],并将其赋值到array[r]
这样让l=r时,左侧的值全部小于target,右侧的值全部小于target,将target放到该位置
优点是不需要额外存储空间,但写法思路较复杂
let array = [3, 9, 1, 5, 2, 3, 5, 7, 2];
function quickSort(array, start, end) {
if (end - start < 1) {
return;
}
const target = array[start];
let l = start;
let r = end;
while (l < r) {
while (l < r && array[r] >= target) {
r--;
}
array[l] = array[r];
while (l < r && array[l] < target) {
l++;
}
array[r] = array[l];
}
array[l] = target;
quickSort(array, start, l - 1);
quickSort(array, l + 1, end);
return array;
}
console.log("array", quickSort(array, 0, 8));