常见的数据结构和算法

前端开发是否需要掌握数据结构和算法?
面试需要,尤其是想进入一些大公司
部分项目需求中需要算法处理
锻炼自己的逻辑思维
图形处理和数据可视化的必备技能
……

** 几种实用基础的数据结构详解
数据结构(data structure [ˈstrʌktʃə®] )是计算机存储、组织数据的方式;算法(algorithm [ˈælɡərɪðəm] )是解决问题的方法/步骤和策略;
1有穷性(Finiteness):必须能在执行有限个步骤之后终止
2确切性(Definiteness):每一步有确切的定义
3输入项(Input):有0个或多个输入,以刻画运算对象的初始情况
4输出项(Output):有一个或多个输出,以反映对输入数据加工后的结果
5可行性(Effectiveness):每个计算步骤都可以在有限时间内完成**

1数组结构 Array

JS中的数组结构非常的简单(已经是浏览器帮助我们进行封装处理好的)
可以存储不同的数据类型值
数组容量伴随存储的内容自动缩放
Array.prototype上提供很多供数组操作的方法

**优势:基于索引直接进行查找和获取,效率很高
弊端:进行中间插入和删除操作时,性能非常低(数组塌陷和删除中间项的优化)

栈结构 Stack:
1后进先出 LIFO(last in first out)
2只能在一端操作(顶端 front):包括增加(进栈)和删除(出栈)
3递归算法中的无限递归会出现栈溢出

自己封装一个栈结构

class Stack {
    container = [];
    // 进栈
    enter(element) {
        this.container.unshift(element);
    }
    // 出栈
    leave() {
        return this.container.shift();
    }
    // 栈的长度
    size() {
        return this.container.length;
    }
    // 获取栈中的结果
    value() {
        return this.container.slice(0);
    }
}
let sk = new Stack;

面试题:十进制转二进制

/*
 * NumberObject.toString(radix)
 * 
 * 思路:把十进制数字和2整除(因为二进制是满2进1)
 *   + 获取其余数 N%2
 *   + 获取其商数 N/2 (整除后的结果)
 * 用上一次的商数继续除以二,一直到商数为0为止;把所有的余数从尾部到顶部依次链接即可;
 */
Number.prototype.decimal2binary = function decimal2binary() {
    let sk = new Stack,
        decimalNum = this.valueOf();
    if (decimalNum === 0) return '0';
    while (decimalNum > 0) {
        sk.enter(decimalNum % 2);
        decimalNum = Math.floor(decimalNum / 2);
    }
    return sk.value().join('');
};
console.log((10).decimal2binary());
console.log((10).toString(2));

队列结构 Queue:
1先进先出 FIFO (First In First Out)
2允许在前端(front)删除,允许在后端(rear)插入
3特殊:优先级队列

面试题:击鼓传花
N个人一起玩游戏,围成一圈,从1开始数数,数到M的人自动淘汰;最后剩下的人会取得胜利,问最后剩下的是原来的哪一位?

function game(n, m) {
    let qe = new Queue;
    for (let i = 0; i < n; i++) {
        qe.enter(i + 1);
    }

    while (qe.size() > 1) {
        for (let i = 0; i < m - 1; i++) {
            qe.enter(qe.leave());
        }
        qe.leave();
    }

    return qe.value().toString();
}
console.log(game(8, 5));

2. 数组排序的几种常见方案和效率分析
*

- 大O标识法和时间复杂度*

度量一个程序的执行时间通常有两种方法:
1事后统计的方法
2事前分析估算的方法 O
Ο(1)<Ο(log2n)<Ο(n)<Ο(n2)<Ο(n3)<…<Ο(2n)

Ο(1):如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数

let a=12;
let b=13;
let temp=a;
a=b;
b=temp;

Ο(log2n):当数据增大 n 倍时,耗时增大 logn 倍(这里的 log 是以 2 为底的,比如,当数据增大 256 倍时,耗时只增大 8 倍)

let i=1;
while(i<=n){
    i*=2;
}

Ο(n):数据量的增大几倍,耗时也增大几倍

for(i=1;i<=n;i++){
    ...
}

Ο(n2):数据量增大 n 倍时,耗时增大 n 的平方倍

for(i=1;i<=n;i++){
    for(j=1;j<=n;j++){
        ...
    }
}

冒泡排序 O(n2)

/* 
 * (N-1)+(N-2)+...+1 = N*(N-1)/2 
 * => N^2/2 - N/2
 * => N^2/2 只取最高阶
 * => N^2 去除常量
 * => O(n^2)
 */
function swap(arr, i, j) {
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
    return arr;
}
Array.prototype.bubble = function bubble() {
    // 外层循环I控制比较的轮数
    for (let i = 0; i < this.length - 1; i++) {
        // 里层循环控制每一轮比较的次数J
        for (let j = 0; j < this.length - 1 - i; j++) {
            if (this[j] > this[j + 1]) {
                // 当前项大于后一项,交换位置
                swap(this,j,j+1);
            }
        }
    }
    return this;
}
let ary = [12, 8, 24, 16, 1];
ary.bubble();
console.log(ary);

选择排序 O(n2)

Array.prototype.select = function select() {
    for (let j = 0; j < this.length - 1; j++) {
        let min = j,
            temp = null;
        // 找到比当前项还小的这一项索引
        for (let i = min + 1; i < this.length; i++) {
            if (this[i] < this[min]) {
                min = i;
            }
        }
        // 让最小的项和当前首位交换位置
        swap(this,min,j);
    }
    return this;
};
let ary = [12, 8, 24, 16, 1];
ary.select();
console.log(ary);

插入排序 O(n2)

Array.prototype.insert = function insert() {
    // 1.准备一个新数组,用来存储抓到手里的牌,开始先抓一张牌进来
    let handle = [];
    handle.push(this[0]);

    // 2.从第二项开始依次抓牌,一直到把台面上的牌抓光
    for (let i = 1; i < this.length; i++) {
        // A是新抓的牌
        let A = this[i];
        // 和HANDDLE手里的牌依次比较(从后向前比)
        for (let j = handle.length - 1; j >= 0; j--) {
            // 每一次要比较的手里的牌
            let B = handle[j];
            // 如果当前新牌A比要比较的牌B大了,把A放到B的后面
            if (A > B) {
                handle.splice(j + 1, 0, A);
                break;
            }
            // 已经比到第一项,我们把新牌放到手中最前面即可
            if (j === 0) {
                handle.unshift(A);
            }
        }
    }
    return handle;
}
let ary = [12, 8, 24, 16, 1];
ary.insert();
console.log(ary);

希尔排序 O(n1.3)

Array.prototype.shell = function shell() {
    let gap = Math.floor(this.length / 2);
    while (gap >= 1) {
        for (let i = gap; i < this.length; i++) {
            while (i - gap >= 0 && this[i] < this[i - gap]) {
                swap(this, i, i - gap);
                i = i - gap;
            }
        }
        gap = Math.floor(gap / 2);
    }
};
let arr = [58, 23, 67, 36, 40, 46, 35, 28, 20, 10];
arr.shell();
console.log(arr);

快速排序 O(n*log2n)

Array.prototype.quick = function quick() {
    // 4.结束递归(当数组中小于等于一项,则不用处理)
    if (this.length <= 1) {
        return this;
    }
    // 1.找到数组的中间项,在原有的数组中把它移除
    let middleIndex = Math.floor(this.length / 2);
    let middleValue = this.splice(middleIndex, 1)[0];
    // 2.准备左右两个数组,循环剩下数组中的每一项,比当前项小的放到左边数组中,反之放到右边数组中
    let aryLeft = [],
        aryRight = [];
    for (let i = 0; i < this.length; i++) {
        let item = this[i];
        item < middleValue ? aryLeft.push(item) : aryRight.push(item);
    }
    // 3.递归方式让左右两边的数组持续这样处理,一直到左右两边都排好序为止(最后让左边+中间+右边拼接成为最后的结果)
    return quick(aryLeft).concat(middleValue, quick(aryRight));
}
let ary = [12, 8, 15, 16, 1, 24];
ary.quick();
console.log(ary);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值