一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
我也曾是迟迟不肯开始刷算法的非科班前端开发,希望这篇文章可以帮你在算法学习初期的学习曲线平滑一点。
时间复杂度
描述最坏情况下,算法基本操作的执行时间度量,取增长最快的项 + 去掉系数。
比如时间是 n² + 5n + 20, 时间复杂度是 O(n²)。
时间复杂度判断实现方法好坏的基本标准。实际做题中即使同样是 O(n) 的时间复杂度,依然有优劣之分。耗时越少越好。
空间复杂度
算法在运行时所需存储空间的度量。描述方式和时间复杂度类似。当然也是占用空间越少越好。但时间复杂度优先级更高,故可以以空间换时间,适当牺牲空间复杂度来减少时间消耗。
栈
栈后进先出。
// 数组来表示
const stack0 = [];
// 入栈
stack0.push(1);
stack0.push(2);
// 出栈
const o1 = stack0.pop();
队列
队先进先出。
// 数组来表示
const stack0 = [];
// 入队
stack0.push(1);
stack0.push(2);
// 出队
const o1 = stack0.shift();
两个栈可以实现一个队列:入队默认全部入栈 s1,需要出队时, 把 s1 内元素全部出栈并入栈 S2,s2 出栈即为正确出队顺序。 队列将除了末位元素全部出队后重新入队,可以实现栈。
链表
链表存储不需要连续的空间,动态分配。能快速增减结点,不方便查找。
单链表只有指向后续结点的指针。
双链表同时有指向后续结点的指针 + 指向前驱结点的指针。方便向前找节点。
带头结点链表 head 指针指向头结点。
不带头结点链表 head 指针指向链表第一个结点。(反正 head 指针都指向第一个节点就对了)
链表的增减结点只需要给指针重新赋值。
eg: n1 -> n2 -> n3
若要删除 n2, 只需要对 n1.next 赋值为 n3 (即 n2.next)
若原链表是 n1 -> n3,现在要在中间加入 n2,则需要对 n1.next, n2.next 赋值。
若是双向链表,则还需考虑 prev 的赋值。理清了有哪些要重新赋值,右侧值到底是多少反而是容易写出来的。
class Node {
construction (k, v) {
this.key = k;
this.value = v;
this.next = null;
this.prev = null;
}
}
class DBLink {
construction () {
this.head = new Node();
this.tail = new Node();
this.head.next = this.tail;
this.tail.prev = this.head;
}
remove (node) {
node.prev.next = node.next;
node.next.prev = node.next;
}
addAfterHead () {
node.next = head.next;
node.prev = head;
this.head.next.prev = node;
this.head.next = node;
}
}
哈希
主要用快速查找。
const m = new Map();
// 赋值 且有序
m.set('k1', 'v1').set('k2', 'v2');
// 更新值
m.set('k1', 'v11');
// 取值
m.get("k1");
// 判断有无对应 key
m.has("k2");
// 迭代按顺序取 key,比[...k][0] 快
let k = m.keys();
console.log(k.next().value);
console.log(k.next().value);
好的,看到这里,你至少可以开始刷栈、队列、链表相关的题目啦!leetcode-cn.com/ 开始吧!