数据结构与算法 - 开个头

107 阅读2分钟

前言

数据结构与算法,相信大家在上学的时候都学过,什么,栈,队列,树,图,然后,这个算法,那个算法,如果你和我一样,有些东西已经忘得差不多了,我希望这些文章可以让我们把它再捡起来;如果你是一个老手,温故而知新吧,逛逛评论区,指出文章中的一些问题,或者分享一下你的思路和见解。
总的说来,后面的内容,暂时还没有一个大计划,写到哪就算哪吧,但大体会按照一个由简到难的顺序写,如果平常遇到一些有意思的问题,也会临时“插播”。话不多说,让我们开始吧。

一些概念

时间复杂度

时间复杂度这个概念,比较容易理解,指的就是执行一段程序所花的时间与数据量之间的关系。
举个例子,看下面这段代码:

let limit = 1e6; // 1百万
let arr = Array.from({ length: limit }, (v, k) => k);

function max(source) {
    let m = -Infinity;
    for (let i = 0; i < source.length; i++) {
        source[i] > m && (m = source[i]);
    }
    return m;
}

function getNumStr(source, index, digLimit) {
    let num = source[index];
    return num.toString().padStart(digLimit, "0");
}

max(arr); // 9999
getNumStr(arr, 99, 4); // 0099

max函数,对于n个数据,他执行了n次【对于2n,3n等也会称O(n)】计算【这里其实理解为一种操作就好,不要局限在计算这两个字上】,那么我们称时间复杂度为O(n)
getNumStr函数,对于n个数据,它执行了1次【与2n,3n同理】计算,那么我们称时间复杂度为O(1)
其它诸如O(n2),O(logn),nO(logn),等后面涉及到的时候再讲,因为对着一个真实的例子讲,你会更好的认识到它们的差别。

JS中的引用

看以下代码:

let a = false;
let b = "abc";
let c = 2;
let d = [1, 2, 3];
let e = {
    a: 1
}

其实上面出现的所有变量【a, b, c, d, e】,都在栈中,其中d和e,我们称引用类型【如果你学过C,我更倾向于把它们理解成指针,它们的值,是某个数据的地址(指向这个数据)】,对它们直接赋值会修改变量本身,而对他们的操作,实际上是对其引用【指向】的值的操作。
看以下代码:

let a = {
    arr: [1, 2],
    child: null
}

let b = {
    arr: [3, 4],
    parent: null
}

a.child = b;
b.parent = a;

a.child.arr = b.parent.arr;
a.arr.push(5);
b.parent.arr = a.child.arr;
b.arr.push(6);

a.arr.pop();
b.arr.pop();

// 输出什么?
console.log(a.arr, b.arr);

如果你已经使用过一段时间的js,你会很快得出答案是a和b的arr都是[1, 2],如果你不能推出结果,建议debugger一下,看一下每一步的执行结果。请务必理解引用的用法,这对后面的链表,树,图等数据结构的理解会有很大帮助。

下一章

链表