前言
前端的代码运行环境本来就不善于处理大量的数据计算,前端也有很多东西比算法重要。那我们为什么要学习算法?
明确学习和使用算法的目的。
一个排序功能,用冒泡排序法,归并排序法,快速排序法?who tm care。一个原生sort函数直接搞定。这就是实用场景,快速实现你的功能即可。
那需不需要学习各种排序算法呢?需要,因为各种排序算法用了很多不用的手段,知识去实现同一个功能,学习里面的知识和优劣对比方法是很有用。
冒泡排序
先了解一下冒泡排序
算法,它是最慢的排序算法之一,但也是最容易实现的排序算法。
之所以叫冒泡排序,是因为使用这种排序算法排序的时候,数值会像气泡一样从数组的一端漂浮到另一端。假设要将一组数字按照升序排列,较大的数值会浮动到数组的右侧,而较小的浮动到左侧,之所有有这种现象是因为算法会在数组中多次移动,比较相邻的数据进行换位。
我们先看一个简单的冒泡排序的例子,理解冒泡排序的原理:
// 对下面的字母桉顺序排列
=> E A F D B C
// 第一次排序前两位比较后变成:
=> A E F D B C
// 接着对比二三位比较顺序相同,保持不变,三四位对比互换:
=> A E D F B C
// 然后四五位对比变成
=> A E F B D C
// 最后两位对比互换数据变成:
=> A E F B C D
// 再次循环对比直到所有的顺序都正确
...
算法步骤:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。
- 针对所有的元素重复以上的步骤。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
代码实现
// 方法1
function bubbleSort(arr) {
var len = arr.length;
for(var i = 0; i < len - 1; i++) {
for(var j = 0; j < len - 1 - i; j++) {
if(arr[j] > arr[j+1]) { // 相邻元素两两对比
var temp = arr[j+1]; // 元素交换
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
// 方法2
function bubblesort(data) {
var length = data.length;
for(let outer = length; outer >= 2; --outer) {
for(let inner = 0; inner < outer - 1; ++inner) {
if (data[inner] > data[inner + 1]) {
var temp = data[inner + 1];
data[inner + 1] = data[inner];
data[inner] = temp;
};
}
}
return data;
}
选择排序
选择排序是从数组的开头开始,将第一个元素和其他元素一一对比,检查完所有元素后,将最小的元素放到数组的第一位,然后算法从第二位开始,这个算法一直进行,直到进行到数组的倒数第二个位置时,所有的数据便完成了排序。
以下对五个元素进行选择排序:初始化 E A D H B
// 第一次排序会找到最小值,并将它与列表第一个元素互换
=> A E D H B
// 接着查找第一个元素后面的最小值,并对他们进行互换
=> A B D H E
// D 已经就位,所以下一步对H和E进行互换,列表按顺序排好:
=> A B D E H
代码实现
function selectionSort(data) {
let len = data.length;
let min, temp;
for(let outer = 0; outer <= len -2; ++outer) {
min = outer;
for(let inner = outer + 1; inner <= len - 1; ++inner) {
if(data[inner] < data[min]) {
min = inner;
}
}
temp = data[outer];
data[outer] = data[min];
data[min] = temp;
}
}
插入排序
插入排序虽然代码实现没有冒泡排序和选择排序那么简单粗暴,但它的原理却是最容易理解的.玩过扑克牌的同学一下子就明白了,没玩过也不关系,插入排序其实就是最简单直观的排序算法.就好比有一副打乱的扑克牌,现在需要你按照从A到K的顺序排列,我们从杂乱的牌堆里拿出一张,放在桌子左上角,一次再拿出一张,如果比之前的大,我们就直接放在它的右侧,如果小,我们把它放在左侧,后面的依次往右侧移动,就是在合适它当前位置把其他右侧移动,空出一个位置让其插入.
代码实现
function insertSort(data) {
let temp, currIndex;
let len = data.length;
for(let outer = 1; outer <= len - 1; ++outer) {
temp = data[outer];
currIndex = outer;
while(currIndex > 0 && data[currIndex - 1] >= temp) {
data[currIndex] = data[currIndex - 1];
--currIndex;
}
data[currIndex] = temp;
}
}
这三种排序算法在复杂度上非常相似,从理论上来说他们的执行效率应该差不多,要确定这三种算法的差异,我们可以通过一个非正式的计时来测试他们排序所花费的时间。首先我们需要对一个集合进行排序,100个,1000个,甚至十万百万个元素的集合进行排序,先写一个可以生成集合的类。
function CArray(num) {
this.dataSource = [];
this.num = num;
for(let i = 0; i < num; ++i) {
this.dataSource[i] = Math.floor(Math.random() * (num + 1)) ;
}
return this.dataSource;
}
我们定义好了CArray构造函数,就可以用它生成任意长度的数组,然后用三种不同的排序方法进行排序,统计不同长度元素的数组排序所花时常。来对比这三种排序方法的效率。
// 1、对数组长度100的数组进行排序
const arr100 = new CArray(100);
console.time();
bubblesort(arr100);
console.timeEnd();
console.time();
selectionSort(arr100);
console.timeEnd();
console.time();
insertSort(arr100);
console.timeEnd();
// default: 0.01318359375 ms
// default: 0.022705078125 ms
// default: 0.001953125 ms
很显然没有显著的差异,我们将数组元素换成1000进行排序测试:
const arr1000 = new CArray(1000);
// 冒泡排序
console.time();
bubblesort(arr1000);
console.timeEnd();
console.time();
selectionSort(arr1000);
console.timeEnd();
console.time();
insertSort(arr1000);
console.timeEnd();
// default: 1.091064453125 ms
// default: 0.604248046875 ms
// default: 0.0068359375 ms
我们测试将长度换成10000和100000测试:
const arr = new CArray(10000);
// 冒泡排序
console.time();
bubblesort(arr);
console.timeEnd();
console.time();
selectionSort(arr);
console.timeEnd();
console.time();
insertSort(arr);
console.timeEnd();
// default: 108.0791015625 ms
// default: 43.179931640625 ms
// default: 0.046875 ms
// 对100000测试
const arr = new CArray(10000);
// 冒泡排序
console.time();
bubblesort(arr);
console.timeEnd();
console.time();
selectionSort(arr);
console.timeEnd();
console.time();
insertSort(arr);
console.timeEnd();
// default: 13030.64306640625 ms
// default: 3983.713134765625 ms
// default: 0.440185546875 ms
我们对不同长度的数组排序时常测试,得到的结论基本一致,选择排序和插入排序要比冒泡排序快,插入排序是三种排序算法中最快的。不过要记住,这些测试必须要经过多次的运行,最后得到的结果才能算作有效的统计。