js之算法

一,了解算法

算法是如何解决一类问题的明确规范。

算法有大致分为排序类、搜索类、集合操作类、字符串操作类、数学类、树、图、和一些其他的分类比如判断回文算法等等。

通常学习算法都会先学习排序类的冒泡排序算法,因为入门简单,但是从运行时间的角度看,这个算法效率比较底下,后面主要了解一些常用的算法。

二,排序类

2.1 冒泡排序

原理:比较相邻的两个项,如果第一个比第二个大,则交换他们。以此类推。元素向上移动到正确的顺序,就像气泡上升到表面一样。

示例

// 外循环,遍历整个数组
for (let i = 0; i < arr.length; i++) {
    // 每次内循环执行完毕,外循环的i就+1,这时最大的值就已经冒泡到数组最末尾
    // 所以内循环遍历数组的第1个到未排序的最后一个(即arr.length - 1 - i)
    for (let j = 0; j < arr.length -1 - i; j++) {
        if(arr[j] > arr[j + 1])  {
            let temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}

说明:冒泡算法的复杂度较高,因此不建议使用。

2.2 选择排序

原理:找到数据结构中的最小值,放到第一位,接着找第二小的值,放在第二位,以此类推。

示例

// 外循环,遍历整个数组
for (let i = 0; i < arr.length; i++) {
    // i从头遍历,每次内循环执行完,数组最左边一定是最小的值
    // 所以内循环从第i+1之后的i开始,比较后面的每个未比较大小的值
    for (let j = i; j < arr.length; j++) {
        if (arr[i] > arr[j]) {
            let temp = arr[j]; // 最小值变成了arr[j],赋值给一个变量
            arr[j] = arr[i];
            arr[i] = temp;
        }
    }
}

2.3 插入排序

原理:假设第1项已经排序,第2项和第一项再进行比较排序,第3项和前2项再进行比较排序,第4项再和前3项比较排序,以此类推。

示例

let j = undefined;
let temp = undefined;

// 外循环,从第2项开始
for (let i = 1; i < arr.length; i++) {
    j = i;
    temp = arr[i];

    // 依次两两判断当前项的前面n项,如果前一个值比当前的大,则交换他们,直至j为0
    while (j > 0 && arr[j - 1] > arr[j]) {
        arr[j] = arr[j - 1];
        j--;
    }
    // 找到不比后面大的值时,就塞到当前位置(也说明没有循环时,也是当前位置)
    arr[j] = temp; 
}

说明:排序小型数组时,插入排序算法比选择排序和冒泡排序性能要好。

2.4 归并排序

概念:将原始数组切分成较小的数组,直到每个小数组只有一个位置;再将小数组归并成大数组,直到最后只有一个排序完毕的大数组。

示例

// 递归拆分小数组,直到拆成一个数组只有一个元素
function mergeSort(arr) {
    if (arr.length < 2) {
        return arr;
    }

    let middleIndex = parseInt(arr.length / 2); // 获取数组最中心的索引
    let leftArr = arr.slice(0, middleIndex);
    let rightArr = arr.slice(middleIndex, arr.length);
    // 先调用递归拆分数组,再调用merge函数归并数组
    return merge(mergeSort(leftArr), mergeSort(rightArr));
}

// 递归归并数组
function merge(left, right) {
    let result = [];
    // 循环左侧小数组和右侧小数组,直到小数组长度为0
    while (left.length && right.length) {
        // 判断小数组元素值的大小,并返回小的值push到目标数组
        if (left[0] < right[0]) {
            // 小数组删除元素,同时返回当前元素(删除是为了可以退出while循环)
            result.push(left.shift()); 
        } else {
            result.push(right.shift());
        }
    }

    // 再单独写一次左侧和右侧数组循环,是为了元素数量为奇数时,两边小数组数量不一致的情况
    while(left.length){
        result.push(left.shift());
    }
    while(right.length){
        result.push(right.shift());
    }
    return result;
}

说明:归并排序算法相比于前三个排序算法,他的性能最好可以被实际使用的排序算法。

2.5 快速排序

原理:快速排序也用了与归并排序类似的分治方法,但是快速排序并不是把他们割开。他的流程是

(1)首先找一个数组中间的位置作为主元

(2)创建两个指针,左边指向第一个元素,右边指向最后一个元素。然后移动左指针直到找到比主元大的元素,移动右指针直到找到比主元小的元素,然后交换他们。重复这个过程,直到左指针超过右指针。

(3)最后对算法对划分后的小数组(比主元小的值组成的子数组,和比主元大的值组成子数组)重复之前的两个步骤,直到数组已完全排序。

示例

function quick (array,left,right) {
    var index;
    // 对半切分两个小数组,直到小数组长度为1
    if (array.length > 1) {
        // 每次循环交换完中间点两边的值,就按中间点切成两个一半长度的小数组
        index = partition(array,left,right);
        if (left < index - 1) {
            quick(array,left,index - 1); // 左侧的小数组重复
        }
        if (index < right) {
            quick(array,index,right); // 右侧的小数组重复
        }
    }
}

function partition (array,left,right) {
    var pivot = array[Math.floor((right + left) / 2)], // 取中间点
        i = left, // 分别设置左指针和右指针的值
        j = right;
    
    // 一直循环到左指针大于右指针
    while(i <= j) {
        // 左半部分直到找到比主元大的值结束循环
        while (array[i] < pivot) {
            i++; // 指针移动
        }
        // 右半部分直到找到比主元小的值结束循环
        while (array[j] > pivot) {
            j--;
        }
        // 走到这说明左半部分和右半部分循环均已结束,需要交换两个值的位置
        if (i <= j) {
            [array[i],array[j]] = [array[j],array[i]]
            i ++;
            j --;
        }
    }
    return i; // 返回左指针停止位置的索引(基本是外层数组中间点的左右)
}

quick(array, 0, array.length - 1);

说明:快速排序是最常用的排序算法。

三,搜索类

3.1 顺序搜索

原理:顺序搜索是最基本的搜索算法,但也是效率最低。主要机制是遍历数组,逐个对比。

示例

function search(array, item) {
    for (var i = 0; i < array.length; i++) {
        if (array[i] === item) {
            return i;
        }
    }
    return -1;
}

3.2 二分搜索

原理:数组已排序的前提下,选择数组中间的一个值比较,如果比中间值小,就在左半部分再次取中间值分割,以此类推;反之如果大于中间值,就在数组右半部分再次分割。

示例

function binarySearch(arr, item) {
    var minIndex = 0; // 设置最小值的索引
    var maxIndex = arr.length - 1; // 设置最大值的索引
    var midIndex = undefiend; // 设置中间值的索引
    var midVal = undefined; // 中间值的值
    while (minIndex < maxIndex) {
        midIndex = Math.floor((maxIndex + minIndex) / 2); // 取中间值索引
        midVal = arr[midIndex]; // 取中间值索引的值
        if (item > midVal) {
            minIndex = midIndex + 1; // 如果大于中间值,则修改最小值为中间值,最大值不变
        } else if (item < midVal) {
            maxIndex = midIndex - 1; // 反之,最小值不变,二分左半部分
        } else {
            return midVal; // 如果相等,则返回目标值
        }
    }
    return -1; // 最终找不到,返回-1
}

四,其他

其他类别还有很多算法,比如数学类的阶乘、斐波那契、数据结构树和图的深度遍历、广度遍历、集合相关、贪心算法等等。先不做详细了解,可参考文章JavaScript 算法与数据结构 - 掘金

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妍思码匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值