前端知识总结之常用排序和算法

1、Array.sort(function)(JavaScript原生排序算法)
参数:比较函数(可选)
若无参数,则按照首字母的ASCII码排序,比较函数的作用为确定排序
function(value1,value2){
if (value1 > value2) {
return 1;
}else if (value1 < value2) {
return -1
}else {
return 0
}
}
按数组中对象的某一属性排序:
function compared(property){
return function(a,b){
let value1 = a[property];
let value2 = b[property];
return value2 - value1;
}
}
array.sort(compared(‘property’));
2、冒泡排序
原理:从第一个元素开始依次同相邻元素比较,小于则交换,直到比较完最后一个元素,否则停止,完成一个元素的冒泡行为。循环进入下一元素。
核心算法:
for(let i=0;i<arr.length;i++){
let mix = arr[i];
for(let j=i+1;j<arr.length;j++){
if(mix>arr[j]){
[arr[i],arr[j]] = [arr[j],arr[i]];//交换相邻值
}
}
}
3、选择排序
原理:每次选择最大的元素,依次至于末尾。
核心算法:
let len = arr.length;
for(let i=0;i<len;i++){
let maxIndex = i;
for(let j=0;j<len-i;j++){
if(arr[maxIndex]<arr[j]){
maxIndex = j;//记录最大值索引
}
}
[arr[len-1-i],arr[maxIndex]] = [arr[maxIndex],arr[len-1-i]];//调整最大值位置
}
4、插入排序
原理:从第二个元素开始,依次向前插入(插入时前面为有序数列),直到最后一个元素。
核心代码:
let len = arr.length;
for(let i=1;i<len;i++){
for(let j=0;j<i;j++){
if(arr[i]<arr[j]){
[arr[j],arr[i]] = [arr[i],arr[j]];
}
}
}
5、快速排序
原理:选取一个基准元素,以此分为两组,大于基准元素和小于基准元素组。然后递归两个子数组。最后把数组连接起来。
function quickSort(arr){
let len = arr.length;
if(len<=1){//递归出口
return arr;
}
let mid = Math.floor(len/2)
,left = []
,right = [];
arr.forEach((item)=>{
if(item>arr[mid]){
left.push(item)
}else {
right.push(item)
}
})
let _left = quickSort(left)
,_right = quickSort(right);
retrun left.concat(arr[mid],right)
}
各算法的性能测试:(测试数据来源https://blog.csdn.net/shuaige…)
数据结果如下
冒泡排序耗时26000ms左右
选择排序耗时5800ms左右
插入排序耗时10600ms左右
归并排序耗时80-100ms
快速排序
cutoff5—>30-50ms
cutoff
10 —>30-60ms
cutoff50 ---->40-50ms
cutoff
3效果不错—>30-50ms,30ms出现的机会很多
cutoff==0时(即不在分割长度短的时候转为插入排序),效果依然不错,30-50ms,30ms出现的很多
堆排序耗时120-140ms
JavaScript提供的原生排序耗时55-70ms
结论
快速排序效率最高,cutoff取3效果最好(没有悬念)
原生排序竟然是第二快的排序算法!诸位同学参加笔试的时候,在没有指明必须要用哪种排序算法的情况下,如果需要排个序,还是用原生的yourArr.sort(function(a,b){return a-b})

  1. 冒泡排序
  2. 插入排序
  3. 希尔排序
  4. 归并排序
  5. 快速排序
  6. 选择排序
  7. 奇偶排序
    总结
    前言:在前端大全中看到这句话,以此共勉。基础决定你可能达到的高度, 而业务决定了你的最低瓶颈
    其实javascript算法在平时的编码中用处不大,不过不妨碍我们学习它,学习一下这些算法的思想,锻炼一下自己的思维模式。

冒泡排序
原理:
从第一个元素开始,往后比较,遇到比自己小的元素就交换位置

特点:
交换的次数最多,所以它的性能是最差的
代码实现:
function bubbleSort(arr){
var len=arr.length;
for(var i=0;i<len;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;
}
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(bubbleSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]
插入排序
原理:
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据
如图所示

在插入排序中,数组会被划分为两种,“有序数组块”和“无序数组块”,
第一遍的时候从”无序数组块“中提取一个数20作为有序数组块。
第二遍的时候从”无序数组块“中提取一个数60有序的放到”有序数组块中“,也就是20,60。
第三遍的时候同理,不同的是发现10比有序数组的值都小,因此20,60位置后移,腾出一个位置让10插入。
然后按照这种规律就可以全部插入完毕。
下面是一张gif图

特点:
插入算法把要排序的数组分成两部分:
第一部分包含了这个数组的所有元素,但将第一个元素除外(让数组多一个空间才有插入的位置).
第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中
比冒泡排序快一点
代码实现:
//插入排序
//假定当前元素之前的元素已经排好序,先把自己的位置空出来,
//然后前面比自己大的元素依次向后移,直到空出一个"坑",
//然后把目标元素插入"坑"中
function insertSort(arr){
// 从第二个元素开始,因为要留出一个坑
for(var i=1;i<arr.length;i++){
var x=arr[i];
for(var j=i-1;arr[j]>x;j–){ //后挪空出位置 .
arr[j+1]=arr[j];
}
if(arr[j+1]!=x){
arr[j+1]=x;
}
}
return arr;
}
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(insertSort(arr,2)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]
希尔排序
原理:
希尔排序也叫 递减增量排序算法,是插入排序的一种神龟进化版。
什么叫递减增量呢,就是定义一个间隔序列,例如是5,3,1。第一次处理,会处理所有间隔为5的,
下一次会处理间隔为3的,最后一次处理间隔为1的元素。也就是相邻元素执行标准插入排序。
步骤如下:
1、先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。
2、所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序。
3、取第二个增量d2<d1重复上述的分组和排序,
4、直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

这里增量的取法如下:
第一次增量的取法为: d=count/2;
第二次增量的取法为: d=(count/2)/2;
最后一直到: d=1;
看上图观测的现象为:
d=3时:将40跟50比,因50大,不交换。
将20跟30比,因30大,不交换。
将80跟60比,因60小,交换。
d=2时:将40跟60比,不交换,拿60跟30比交换,此时交换后的30又比前面的40小,又要将40和30交换,如上图。
将20跟50比,不交换,继续将50跟80比,不交换。
d=1时:这时就是前面讲的插入排序了,不过此时的序列已经差不多有序了,所以给插入排序带来了很大的性能提高。
特点:
 由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,
相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
 打个比方,我原来的数组是[5,4,3,2,1]的,这样一打乱就全部重新排了。
代码实现:
function shellSort(arr){
var gap=Math.floor(arr.length/2);
while(gap>0){
for(var i=gap;i<arr.length;i++){
temp=arr[i];
for(var j=i;j>=gap&&arr[j-gap]>temp;j-=gap){
arr[j]=arr[j-gap];
}
arr[j]=temp;
}
gap=Math.floor(gap/2);
}
return arr;
}
var arr = [2,3,6,4,2,1,90,100,20,5];
console.log(shellSort(arr)); //[1, 2, 2, 3, 4, 5, 6, 20, 90, 100]
归并排序
原理:
归并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
有以下几个步骤:
1、把长度为n的输入序列分成两个长度为n/2的子序列;
2、对这两个子序列继续分为m/2的子序列,一直分下去
3、将两个排序好的子序列合并成一个最终的排序序列。

再来一张静态图,比较好理解

这里需要补充是,归并中对数组的分割是从上往下的,归并中数组的比较是从下往上的。
特点:
速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列.
属于分治思想,递归归并
代码实现:
/* 排序并合并*/
function merge(left, right) {
var re = [];
while(left.length > 0 && right.length > 0) {
if(left[0] < right[0]) {
      // 如果左边的数据小于右边的数据,将左边的数据取出,放到新数组那里
re.push(left.shift());
} else {
re.push(right.shift());
}
}
/* 当左右数组长度不等.将比较完后剩下的数组项链接起来即可 /
return re.concat(left).concat(right);
}
function mergeSort(arr) {
if(arr.length == 1){
return arr;
}
/
首先将无序数组划分为两个数组 /
var mid = Math.floor(arr.length / 2);
var left = arr.slice(0, mid);
var right = arr.slice(mid);
/
递归分别对左右两部分数组进行排序合并 */
return merge(mergeSort(left), mergeSort(right));
}
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(mergeSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]
快速排序
原理:
1、在数据集之中,选择一个元素作为"基准"(pivot)。比如选择下面数字45

2、所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。

3、对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

特点:速度最快。和归并排序不同的是,归并排序是先分为两组再继续排,而快速排序是边分边排
代码实现:
// 大致分三步:
// 1、找基准(一般是以中间项为基准)
// 2、遍历数组,小于基准的放在left,大于基准的放在right
// 3、递归
function quickSort(arr){
//如果数组<=1,则直接返回
if(arr.length<=1){
return arr;
}
var pivotIndex=Math.floor(arr.length/2);
//找基准,并把基准从原数组删除
var pivot=arr.splice(pivotIndex,1)[0];
//定义左右数组
var left=[];
var right=[];

//比基准小的放在left,比基准大的放在right
for(var i=0;i<arr.length;i++){
if(arr[i]<=pivot){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
//递归
return quickSort(left).concat([pivot],quickSort(right));
}
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(quickSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]
选择排序
原理:在要排序的一组数中,选出最小的一个数与第一个位置的数交换,然后剩下的数当中找出最小的与第二个位置的数交换,如此循环直到倒数第二个数和最后一个数为止。

静态图:

特点:可以说是冒泡排序的衍生品,效率比较一般般
代码实现:
// 在无序区中选出最小的元素,然后将它和无序区的第一个元素交换位置。
function selectSort(arr){
length = arr.length;
for (var i = 0; i < length; i++){ // 循环数组
var _min = arr[i]; // 把每一次的 数组里面的数字记录下来
var k = i; // 记录下来索引
for (var j = i + 1; j < length; j++){ // 当前的数字与后一个数字相比较
if (_min > arr[j]){ //当前的数 大于 后面一个数的话
_min = arr[j]; // 就讲后面 的数值 保存下来
k = j; /// 保存索引
}
}
arr[k] = arr[i]; // 进行交换位置
arr[i] = _min;
}
return arr;
}
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(selectSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]
奇偶排序
原理:
选取所有奇数列的元素与其右侧相邻的元素进行比较,将较小的元素排序在前面;
选取所有偶数列的元素与其右侧相邻的元素进行比较,将较小的元素排序在前面;
重复前面两步,直到所有序列有序为止。
如下图所示:

gif图:

特点:奇数和偶数序列交替比较
代码实现:
function oddEvenSort(arr){
//swaped用来控制循环是否要继续,如果左边的都比右边的小,则退出循环,返回排好的数组
var swaped=true;
var k=0;
while(swaped){
if(k>0){
swaped=false;
}
for(var i=k;i<arr.length-1;i+=2){
if(arr[i]>arr[i+1]){
// 如果左边的数字比右边的大,两者交换位置
arr[i]=[ arr[i+1], arr[i+1]=arr[i] ][0];
swaped=true;
}
}
k=[1,0][k]; //奇数和偶数之间的转行
}
return arr;
}
var arr=[2,3,6,4,2,1,90,100,20,5];
console.log(oddEvenSort(arr)); // [1, 2, 2, 3, 4, 5, 6, 20, 90, 100]

未完待续‘。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值