链表定义
使用指针将一连串分散在不同内存地址的数据串联起来的数据结构
链表和数组的区别
-
数组定义
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
提问:为何要类型相同?
不同类型的数据占用的字节不同,无法利用寻址公式查询。
-
js中的数组
提问:js的数组为何可以存不同类型的数据结构?
js中的Array类是对数组的封装,支持数组的操作和动态扩容等特性。 在v8中,js的数组有两种不同的实现。fast&slow。
- fast
若数组的类型相同,且数据直接地址相差不大,利于开辟连续存储空间,则使用快数组(FixedArray)
- slow
否则使用index为键值的HashTable实现的慢数组。
const arr = new Array(10); // fast const arr = new Array([1,2,3]) // fast arr[2000] = 4; // fast -> slow const arr = [1, "2", {"key": 3}]课后:数组如何动态扩容和收缩?以及为何要扩容收缩,利弊在哪?
-
数组和链表的区别
-
数组需要开辟一串连续的存储空间,链表的数据地址可以是不连续的。
- 数组优势:利用cpu缓存
- 链表优势:内存利用率高
-
数组随机访问的时间复杂度是O(1),链表是O(n)。
- 随机访问 -> arr[3],使用寻址公式
- 查找数组中某个值是否存在的时间复杂度?
-
数组的插入删除操作是O(n),链表是O(1)。
-
数组插入需要移动插入位置往后的所有节点
-
-
链表的应用
-
ReactFiber为什么使用链表
DOM节点是多叉树结构,虽然适合递归,但是无法暂停。
dfs(root) { ... for (let i = 0; i < root.children.length;i++) { dfs(root.children[i]) } }所以使用链表结构来表述dom节点。
// 获取当前work in progress if (wip.child) { wip = wip.child return } let next = wip while (next) { if (next.sibling) { wip = next.sibling return } next = next.return } wip = null -
hashTable中的散列冲突的解决方案
-
开放寻址法
不在当前范围内
-
链表法
-
-
跳表
redis的数据存储类型
-
有序集合的操作
- 插入一个数据
- 删除一个数据
- 查找一个数据
- 按照区间查找数据 -> [3-5]
- 迭代输出有序序列
时间复杂度O(logN),查找数据使用hashTab的话是O(1),空间复杂度O(N)
- 职责链模式
我们处理某些校验,过滤等问题时,可以通过A,B,C等处理器,当Q经过A处理完后可以交给B再交给C,直到全部处理完成,每个处理器(A,B,C)只处理自己职责范围内的内容,这就是职责链模式。
职责链可以将主问题与子过滤解耦,满足代码的开闭原则和可扩展性。下面是使用链表实现职责链模式的demo:
class Handler {
private successor: Handler;
private doHandler: Function;
constructor(callback) {
this.doHandler = callback;
}
public setSuccessor(successor: Handler) {
this.successor = successor;
}
public handle() {
this.doHandler();
if (this.successor) {
this.successor.doHandler()
}
}
}
class HandlerChain {
private head: Handler = null;
private tail: Handler = null;
public addHandler(handler: Handler) {
// 设置下一个执行
handler.setSuccessor(null);
if (!this.head) {
this.head = handler;
this.tail = handler;
return;
}
this.tail.setSuccessor(handler)
this.tail = handler;
}
public handle() {
if (this.head) {
this.head.handle()
}
}
}
const handler1 = new Handler(() => console.log('handler-1'))
const handler2 = new Handler(() => console.log('handler-2'))
const chain = new HandlerChain();
chain.addHandler(handler1);
chain.addHandler(handler2);
chain.handle();
- LRU(最近最少使用策略)
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰
/** * @param {number} capacity */ var LRUCache = function (capacity) { this.map = new Map(); this.max = capacity; this.count = 0; this.head = new ListNode(-1, -1) this.tail = new ListNode(-1, -1) this.head.next = this.tail; this.tail.prev = this.head; }; /** * @param {number} key * @return {number} */ LRUCache.prototype.get = function (key) { if (!this.map.has(key)) return -1; this.moveToHead(this.map.get(key)); return this.map.get(key).val }; /** * @param {number} key * @param {number} value * @return {void} */ LRUCache.prototype.put = function (key, value) { if (this.map.has(key)) { const node = this.map.get(key) node.val = value; this.moveToHead(node) return } const node = new ListNode(key, value) this.map.set(key, node); if (this.count === this.max) { this.removeTail(); this.count--; } this.addToHead(node); this.count++; }; LRUCache.prototype.moveToHead = function (node) { this.removeNode(node); this.addToHead(node); }; LRUCache.prototype.removeTail = function () { const node = this.tail.prev; if (!node || node === this.head) { return } node.prev.next = this.tail; this.tail.prev = node.prev; this.map.delete(node.key); }; LRUCache.prototype.removeNode = function (node) { node.prev.next = node.next; node.next.prev = node.prev; }; LRUCache.prototype.addToHead = function (node) { node.prev = this.head; node.next = this.head.next; this.head.next = node; node.next.prev = node; }; /** * Your LRUCache object will be instantiated and called as such: * var obj = new LRUCache(capacity) * var param_1 = obj.get(key) * obj.put(key,value) */ function ListNode(key = null, val = null, prev = null, next = null) { this.key = key this.val = val this.prev = prev this.next = next }