排序算法
初识排序
排序算法算是程序员在学习过程中必不可少的基本功了,但是还是有很多人对于基础排序的思想不是很了解。正好卡在重学排序的时间,干脆就写一篇博客来记录排序算法。
推荐一个可以看算法动画的网站 ==> visualgo.net/zh
冒泡排序
冒泡排序(Bubble Sort)是一种典型的交换排序算法,通过交换数据元素的位置进行排序。
基本思想: 从无序序列头部开始,进行两两比较,根据大小交换位置,直到最后将最大(小)的数据元素交换到了无序队列的队尾,从而成为有序序列的一部分;下一次继续这个过程,直到所有数据元素都排好序。
核心在于每次通过两两比较交换位置,选出剩余无序序列里最大(小)的数据元素放到队尾。
思想==> 实现:
- 比较相邻的元素。如果第一个比第二个大(小),就交换他们两个。
- 对每一对相邻元素做同样的处理,从开始第一对到结尾的最后一对。每轮处理后,最后的元素会是最大(小)的数。
- 针对所有的元素重复以上的步骤,除了最后已经选出的元素(有序)。
- 持续每次对越来越少的元素(无序元素)重复上面的步骤,直到没有任何一对数字需要比较,则序列最终有序。
实现==> 代码:
[1,2,3]=>[2,1,3]==>[2,3,1]
[2,3,1]=>[3,2,1]
- 假设存在n个元素,则需要冒泡n-1轮,因为每轮只能冒泡一个元素到正确位置。
- 假设存在n个元素无序状态,每次排序都是最坏情况,一共要排序n-1次才能将最小元素冒泡到队尾。
- 由此分析得到,代码结构应该选择双循环,外轮控制冒泡的轮数,内部控制冒泡的次数。
function bubSort(numbers) {
let temp;
for (let j = 0; j < numbers.length - 1; j++) {
for (let i = 0; i < numbers.length - j; i++) {
if (numbers[i] > numbers[i + 1]) {
temp = numbers[i];
numbers[i] = numbers[i + 1];
numbers[i + 1] = temp;
}
}
console.log(`这是第${j + 1}轮排序,结果为` + numbers);
}
return numbers;
}
增加Flag改进
改进之前,先考虑一个问题,对于[1,4,3,2,5]。冒泡排序要排几次呢?每次的结果是什么? 结果: 这是第1轮排序,结果为1,3,2,4,5 这是第2轮排序,结果为1,2,3,4,5 这是第3轮排序,结果为1,2,3,4,5 这是第4轮排序,结果为1,2,3,4,5
思考: 很容易发现,在已经是有序的情况下,冒泡并不会结束运行,而是继续排序,这样的浪费是可耻的。考虑通过检测已经排序完成后去打断循环,以减少浪费。那什么时候排序已经完成呢?就是当一轮循环中不存在交换位置的时候就说明已经完成了排序
代码:
function bubSort(numbers) {
let temp;
let check = false;
for (let j = 0; j < numbers.length - 1; j++) {
for (let i = 0; i < numbers.length - j; i++) {
if (numbers[i] > numbers[i + 1]) {
temp = numbers[i];
numbers[i] = numbers[i + 1];
numbers[i + 1] = temp;
check = true;
}
}
console.log(`这是第${j + 1}轮排序,结果为` + numbers);
if (!check) {
break;
}
check = !check;
}
return numbers;
}
其实还可以更精细化的处理,如果在排序中,有部分元素已经排好序,怎么去忽略这些元素呢?
鸡尾酒冒泡
鸡尾酒冒泡的思路是,外轮循环次数减半,正反轮换,正向冒泡大数,反向冒泡小数。
function cocktailSort(array) {
let count = 0;
let temp;
for (let i = 0; i < array.length / 2; i++) {
//外层循环次数折半
for (let j = count; j < array.length - 1 - count; j++) {
//一次正向循环,从头开始循环
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
count++; //一次循环,冒泡出一个大数,记录已找出"较大"数个数+1
for (let k = array.length - 1 - count; k > count - 1; k--) {
//一次反向循环,从尾开始循环 k为除去正向冒泡数的未排序元素
if (array[k] < array[k - 1]) {
temp = array[k];
array[k] = array[k - 1];
array[k - 1] = temp;
}
}
}
}