队列Queue : 先进先出的数组
- 先进先出的数组
- 只提供入队和出队两个操作
array.push(n)请用高级写法array.push.call(array,n)array.shift()请用高级写法array.shift.call(array)
为啥要用高级用法:array.shift(),操作对象是this,默认指定的this就是array。如果我们用高级写法,那么函数对谁操作一目了然。
- 举例:外出用餐取号和叫号就是一个队列。先取号的就会先叫号。
栈stack : 后进先出的数组
- 后进先出的数组
- 只提供压栈和弹栈两个操作。
array.push(n)请用高级写法array.push.call(array,n)array.pop()请用高级写法array.pop.call(array)
- 举例
- 坐电梯就是先进后出,后进先出
- JS函数调用栈也是先压栈的后弹栈。
阶乘函数
function f(n){
return n !== 1 ? n* f(n-1) : 1
}
f(4)

链表Linked List : 一个链一个
- 举例:原型链
array.__proto__ === Array.prototype
Array.prototype.__proto__ === Object.prototype

- 类型
①单向链表:每个节点有一个next指向下一个节点
②双向链表:每个节点有一个previous指向上一个节点
③循环链表:最后一个节点的next指向头节点
- 链表的创建和增删改查节点
const createList = value => {
return createNode(value);
};
const appendList = (list, value) => {
const node = createNode(value);
let x = list;
while (x.next) {
x = x.next;
}
// x.next === null //x 是最后一个节点
x.next = node;
return node;
};
const removeFromList = (list, node) => {
let x = list;
let p = node; // 课堂里将 p 初始化为 null,这里改为 node
while (x !== node && x !== null) { // 课堂里忘了对 null 进行处理,如果 node 不在 list 中,x 就可能为 null
p = x;
x = x.next;
}
if(x === null){ // 若 x 为 null,则不需要删除,直接 return, false 表示无法删除不在list里的节点
return false
}else if(x === p){ // 这说明要删除的节点是第一个节点
p = x.next
return p // 如果删除的是第一个节点,那么就要把新 list 的头节点 p 返回给外面,即 newList = removeFromList(list, list)
}else{
p.next = x.next;
return list // 如果删除的不是第一个节点,返回原来的 list 即可
}
};
const createNode = value => {
return {
data: value,
next: null
};
};
const travelList = (list, fn) => {
let x = list;
while (x !== null) {
fn(x);
x = x.next;
}
};
const list = createList(10);
const node2 = appendList(list, 20);
const node3 = appendList(list, 30);
const node4 = appendList(list, 40);
travelList(list, node => {
console.log(node.data);
});
哈希表key-value pairs
定义
哈希表就是一个个个键值对的组成的。
问题 : 存键值对容易,那读呢?
假设哈希表hash里有一万对key-value
比如{name: 'yy', age: 18, p1: 'property1'}
如何使得读取hash['xxx']速度最快
4个解决办法
- 不作任何优化,hash['xx'] 需要遍历所有key, O(n)
- 对key排序,使用二分查找,o(log2 n)
- 用字符串对应的ASCII数字做索引,o(1)
- 对索引做除法取余数,就可以直接取到那个key,o(1),还有个小问题就是冲突了怎么办,冲突了就顺延
树tree : 一个链多个
- 举例
- 中国的省市区,可以看成一棵树
- 公司的层级结构,可以看成一棵树
- 网页中的节点,可以看成一棵树
- 树的创建和增删改查
let tree = createTree(value)
let node = createNode(value)
addChild(tree, node)
removeChild(node1, node2)
travel(tree)
const createTree = value => {
return {
data: value,
children: null,
parent: null
};
};
const addChild = (node, value) => {
const newNode = {
data: value,
children: null,
parent: node
};
node.children = node.children || [];
node.children.push(newNode);
return newNode;
};
const travel = (tree, fn) => {
fn(tree);
if (!tree.children) {
return;
}
for (let i = 0; i < tree.children.length; i++) {
travel(tree.children[i], fn);
}
};
const find = (tree, node) => {
if (tree === node) {
return tree;
} else if (tree.children) {
for (let i = 0; i < tree.children.length; i++) {
const result = find(tree.children[i], node);
if (result) {
return result;
}
}
return undefined;
} else {
return undefined;
}
};
const removeNode = (tree, node) => {
const siblings = node.parent.children;
let index = 0;
for (let i = 1; i < siblings.length; i++) {
if (siblings[i] === node) {
index = i;
}
}
siblings.splice(index, 1);
};
const tree = createTree(10);
const node2 = addChild(tree, 20);
const node3 = addChild(tree, 30);
addChild(tree, 40);
const node5 = addChild(tree, 50);
addChild(node2, 201);
addChild(node2, 202);
addChild(node2, 203);
addChild(node2, 204);
console.log(tree);
const fn = node => {
console.log(node.data);
};
removeNode(tree, node5);
console.log(tree);