前端经典算法题(一)之冒泡排序及双向冒泡排序
一、背景介绍
1、我在干嘛?
用JavaScript写一个冒泡排序算法
2、什么是冒泡排序?
啊这。。。怎么说呢?冒泡排序就是这个那个,冒泡嘛,你懂的,就是这样那样,然后再这样那样再排一下顺序~^-^
就是冒泡排序
好吧,我认真说~
且看👀下图
就是每一轮,跟右边相邻的元素比较大小,比右边大,就跟右边换个位置
更新~
我在我的这篇博客里认真写了冒泡排序的思路,可以参考下
JavaScript实现冒泡排序、选择排序和插入排序
二、手写一个冒泡排序
其实只要理解了冒泡排序是啥意思,就知道这是一个双重for循环实现的,那就来吧~
(一)最简单最基本的冒泡排序
1、为了看清楚是怎么排序的,我用模版字符串给它打印出来
function bubble_sort1(array) {
for(let i = 0; i < array.length - 1; i++) {
for(let j = 0; j < array.length - i + 1; j++){
if(array[j] > array[j+1]) {
[array[j], array[j+1]] = [array[j+1], array[j]];
}
console.log(`第${i}轮的第${j}次排完后的数组是酱紫滴${array}`);
}
}
return array;
}
写个简单的数组测试一下
let arr = [78, 1, 9, 100, 4, 2, 7, 37];
console.log(bubble_sort1(arr));
我选了最后几行打印结果来看下,可以看出到最后几轮排序的时候,其实已经排好了顺序,但是它还要继续跑完循环,所以可以优化一下
......
第3轮的第5次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第4轮的第0次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第4轮的第1次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第4轮的第2次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第4轮的第3次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第4轮的第4次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第5轮的第0次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第5轮的第1次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第5轮的第2次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第5轮的第3次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第6轮的第0次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第6轮的第1次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
第6轮的第2次排完后的数组是酱紫滴1,2,4,7,9,37,78,100
2、为了让我们知道后面优化到底有没有效果,所以在这里写了一个生成长度为n,且数组内的值也是0到n之间的随机整数的一个函数
function gernerate_array(n) {
let array = [];
for(let i = 0; i < n; n++){
array.push(parseInt(Math.random() * n));
}
return array;
}
3、好的,我们加上时间戳来测试一下
let arr = generateArr(50000);
let timestamp1 = new Date().getTime();
console.log(bubble_sort1(arr));
let time_diff1 = new Date().getTime() - timestamp1;
console.log(`第一种解法所用时间是${time_diff1}`)
结果是8470
(二)稍微优化一下的冒泡排序
1、现在稍微优化一下,思路是设置一个标志位,假如一轮下来,一次都没有交换过,说明已经排好了,那就不要再继续循环了
function bubble_sort2(array) {
for(let i = 0; i < array.length - 1; i++) {
/*默认是true,只要交换了一次就改变成false*/
let flag = true;
for(let j = 0; j < array.length - i + 1; j++) {
if(array[j] > array[j+1]) {
[array[j], array[j+1]] = [array[j+1], array[j]];
flag = false;
}
console.log(`第${i}轮的第${j}次排完后的数组是酱紫滴${array}`);
}
if(flag) {
return array;
}
}
}
2、再用这个数组测试一下
let arr = [78, 1, 9, 100, 4, 2, 7, 37];
console.log(bubble_sort2(arr));
可以发现,从第4轮开始,一个数字都没有交换过,就不排了
3、那也来个时间戳测试一下性能
首先为了保证排的是同一个数组,来写一个数组的浅拷贝
function lightClone(arr) {
let res = []
for(let i = 0; i < arr.length; i++) {
res.push(arr[i]);
}
return res;
}
好的来测试一下吧
let arr = generateArr(50000);
let arr2 = lightClone(arr);
let timestamp1 = new Date().getTime();
console.log(bubble_sort1(arr));
let time_diff1 = new Date().getTime() - timestamp1;
console.log(`第一种解法所用时间是${time_diff1}`)
let timestamp2 = new Date().getTime();
console.log(bubble_sort2(arr2));
let time_diff2 = new Date().getTime() - timestamp2;
console.log(`第二种解法所用时间是${time_diff2}`)
测试结果是
第一种解法所用时间是8689
第二种解法所用时间是8416
结论就是这个优化的也不是很大,主要是排好了顺序以后,减少无意义的循环
(三)双向冒泡排序
这个排序的思路是,先从左到右冒泡,排出一个最大值放在最右边,再从右到左冒泡,排出一个最小值放在最左边,再双向的往中间排。
这里也结合一下第二种解法中用到的标志位,减少无意义的循环
1、聪明的小盆友一看就知道我们需要两个指针,来编码实现吧
function bubble_sort3(array) {
let left_index = 0;
let right_index = array.length - 1;
while (left_index < right_index) {
let flag = true;
/**先从左到右排**/
for(let i = left_index; i < right_index; i++) {
if(array[i] > array[i+1]) {
[array[i], array[i+1]] = [array[i+1], array[i]];
flag = false;
}
}
right_index --;
/**再从右到左排**/
for(let i = right_index; i > left_index; i--) {
if(array[i] < array[i-1]) {
[array[i], array[i-1]] = [array[i-1], array[i]];
flag = false;
}
}
left_index ++;
if(flag) {
return array;
}
}
return array;
}
2、好哒,来测试一下
let arr = generateArr(50000);
let arr2 = lightClone(arr);
let arr3 = lightClone(arr);
let timestamp1 = new Date().getTime();
let sort_array1 = bubble_sort1(arr);
console.log(sort_array1);
let time_diff1 = new Date().getTime() - timestamp1;
console.log(`第一种解法所用时间是${time_diff1}`)
let timestamp2 = new Date().getTime();
let sort_array2 = bubble_sort2(arr2);
console.log(sort_array2);
let time_diff2 = new Date().getTime() - timestamp2;
console.log(`第二种解法所用时间是${time_diff2}`)
let timestamp3 = new Date().getTime();
let sort_array3 = bubble_sort3(arr3);
console.log(sort_array3);
let time_diff3 = new Date().getTime() - timestamp3;
console.log(`第三种解法所用时间是${time_diff3}`)
测试结果
第一种解法所用时间是8451
第二种解法所用时间是8254
第三种解法所用时间是3858
好啦,可以看出双向冒泡排序比前两种明显快些~不过三种都还是O(n^2)哦
如果稀饭本篇博客就点个赞关注一下吧~