前端常用排序详解

博客介绍了排序算法和递归相关知识。排序方面,提到JS数组sort方法,重点讲解冒泡、选择、插入排序的原理、动图展示及代码实现,还分析插入排序时间复杂度。递归部分说明其是自己调用自己,阐述递归步骤,并给出对象深度克隆和数组扁平化的例子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序

JS本身数组的sort方法,可以满足日常业务操作中很多的场景了。 但很多时候我们还需要知道选择排序 冒泡排序 和快速排序 的方式。

冒泡排序

文字例子:

E A D B H //原始序列
A E D B H //经过一次计算(A、E交换)
A D E B H //再次计算(D、E交换)
A D B E H //再次计算(B、E交换)
//将上面所有的循环再循环 arr.length-2次
A B D E H //最终结果
复制代码

动图:

代码实现

function bubleSort(arr) {
    var len = arr.length;
    //比较多少个循环,内部循环一次少一次
    for (let outer = len ; outer >= 2; outer--) {
        //前后比较,从第1个开始
        for(let inner = 0; inner <=outer - 1; inner++) {
            if(arr[inner] > arr[inner + 1]) {
                let temp = arr[inner];
                arr[inner] = arr[inner + 1];
                arr[inner + 1] = temp;
                //[arr2[0],arr2[1]] = [arr2[1],arr2[0]]  //这一步可用ES6解构赋值实现位置交换
            }
        }
    }
    return arr;
}
复制代码

选择排序

选择排序是从数组的开头开始,将第一个元素和其他元素作比较,检查完所有的元素后,最小的放在第一个位置,接下来再开始从第二个元素开始,重复以上一直到最后。

动图:

外层循环从0开始到length-1, 然后内层比较,最小的放开头,代码:

function selectSort(arr) {
    var len = arr.length;
    for(let i = 0 ;i < len - 1; i++) {
        //从第i个开始,比较第i个和第j个的大小,小于第i个就交换,知道不小于,再累加i
        for(let j = i ; j<len; j++) {
            if(arr[j] < arr[i]) {
                [arr[i],arr[j]] = [arr[j],arr[i]];
            }
        }
    }
    return arr
}
复制代码

插入排序

插入排序核心–扑克牌思想: 就想着自己在打扑克牌,接起来一张,放哪里无所谓,再接起来一张,比第一张小,放左边,继续接,可能是中间数,就插在中间….依次。

动图:

原理:

首先将待排序的第一个记录作为一个有序段。 从第二个开始,到最后一个,依次和前面的有序段进行比较,确定插入位置。

function insertSort(arr) {
    for(let i = 1; i < arr.length - 1; i++) {  //外循环从1开始,默认arr[0]是有序段
        for(let j = i; j > 0; j--) {  //j = i,将arr[j]依次插入有序段中进行比较,第j个大于第i个就终端换下一个
            if(arr[j] < arr[j-1]) {
                [arr[j],arr[j-1]] = [arr[j-1],arr[j]];
            } else {
                continue;
            }
        }
    }
    return arr;
}
复制代码

其实和冒泡排序很相似,只是将换位置放到了最后一步。

乍一看,好像插入排序速度还不慢,但是要知道: 当序列正好逆序的时候,每次插入都要一次次交换,这个速度和冒泡排序是一样的,时间复杂度O(n²); 当然运气好,前面是有序的,那时间复杂度就只有O(n)了,直接插入即可。

排序算法平均时间复杂度最坏时间复杂度空间复杂度否稳定
冒泡排序O(n²)O(n²)O(1)
选择排序O(n²)O(n²)O(1)不是
插入排序O(n²)O(n²)O(1)

还有一些更高级的排序算法,但是稳定性并不高,这里就不在写了。

递归

递归,其实就是自己调用自己。

递归步骤:

寻找出口,递归一定有一个出口,锁定出口,保证不会死循环 递归条件,符合递归条件,自己调用自己。

例子: 实现对一个对象(object)的深度克隆:

//所谓深度克隆,就是当对象的某个属性值为object或array的时候,要获得一份copy,而不是直接拿到引用值
function deepClone(origin,target) {  //origin是被克隆对象,target是我们获得copy
    var target = target || {}; //定义target
    for(var key in origin) {  //遍历原对象
        if(origin.hasOwnProperty(key)) {
            if(Array.isArray(origin[key])) { //如果是数组
                target[key] = [];
                deepClone(origin[key],target[key]) //递归
            } else if (typeof origin[key] === 'object' && origin[key] !== null) {
                target[key] = {};
                deepClone(origin[key],target[key]) //递归
            }
            target[key] = origin[key];
        }
    }
    return target;
}
复制代码

这个代码不够简洁,改造一下:

function deepClone(source) {
  let sourceCopy = source instanceof Array ? [] : {};
  for (let item in source) {
    sourceCopy[item] = typeof source[item] === 'object' ? deepClone(source[item]) : source[item];
  }
  return sourceCopy;
}
复制代码

例子:实现一个Array flat()方法,将嵌套数组扁平化。

var arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1, 2, 3, 4]
//实现
Array.prototype.flat = function() {
    var arr = [];
    this.forEach((item,idx) => {
        if(Array.isArray(item)) {
            arr = arr.concat(item.flat()); //递归去处理数组元素
        } else {
            arr.push(item)   //非数组直接push进去
        }
    })
    return arr;   //递归出口
}
复制代码

本文中的图片来源:visualgo.net/zh

转载于:https://juejin.im/post/5d08602af265da1b8811ddb4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值