Queue队列
定义
队列是一种First-in,First-out的数据结构,这是事件循环(Event Loop)的基础结构
题目
请实现一个餐厅叫号网页,点击取号按钮,生成一个号码,点击叫号按钮,显示“请x号就餐”
思路
- 首先选择队列作为数据结构
- 使用
push,shift来加入队列和退出队列 - 练习使用
call写法
代码
import "./styles.css";
const btnCreateNumber = document.querySelector("#createNumber");
const btnCallNumber = document.querySelector("#callNumber");
const spanNewNumber = document.querySelector("#newNumber");
const spanQueue = document.querySelector("#queue");
const divScreen = document.querySelector("#screen");
const queue = [];
let number = 0;
btnCreateNumber.onclick = () => {
number += 1;
queue.push.call(queue, number); // 等价于 queue.push(number)
spanNewNumber.innerText = number;
spanQueue.innerText = JSON.stringify(queue);
};
btnCallNumber.onclick = () => {
const n = queue.shift.call(queue); // 等价于 queue.shift()
// if n === undefined 没处理
divScreen.innerText = `请 ${n} 号就餐`;
spanQueue.innerText = JSON.stringify(queue);
};
Stack栈
定义
结构就是后进先出 Last-in,Fist-out,每个数据按顺序存放。例如,乒乓盒子中最顶层的球5,最后一个放置进去,却第一个被拿出来使用。如果想拿到最底部的乒乓球1,需要把上面的球全部取出来,让乒乓球1处于盒子顶层
题目
JS函数的调用栈就是一个栈,假设f1调用了f2, f2存在就调用f3, f3调用结束了就调用f2,f2调用结束了就回到f1。
思路
自己用笔画一下,压栈pusu和出栈pop的过程,再来写代码
代码
function f1(){let a = 1; return a + f2()}
function f2(){let b = 2; return b + f3()}
function f3(){let c = 3; return c}
f1()
LinkedList链表
定义
链表是由一组不必相连(不必相连:可以连续也可以不连续)的内存结构-节点,按特定的顺序链接在一起的抽象数据类型。
分类
Linked List单链表
单链表 由各个内存结构通过一个Next 指针链接在一起组成,每一个内存结构都存在后继内存结构(链尾除外),内存结构由数据域和Next指针域组成。
Double Linked List双向链表
双向链表:由各个内存结构通过指针Next和指针Prev 链接在一起组成,每一个内存结构都存在前驱内存结构和后继内存结构(链头没有前驱,链尾没有后继),内存结构由数据域,Prev指针域和Next指针域组成。
Circular Linked List循环链表
单向循环链表 : 由各个内存结构通过一个指针Next 链接在一起组成,每一个内存结构都存在后继内存结构,内存结构由数据域和Next指针域组成。
Double Circular Linked List双向循环链表: 由各个内存结构通过指针Next和指针Prev 链接在一起组成,每一个内存结构都存在前驱内存结构和后继内存结构,内存结构由数据域,Prev指针域和Next 指针域组成。
链表的运用
JS中的原型链就是链表的运用
let array = [1,2,3]
array._proto_ === Array.prototype
Array.prototype._prototype === Object.prototype
代码
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-valuePairs哈希表
定义
散列(hashing)是电脑科学中一种对资料的处理方法,通过某种特定的函数/算法(称为散列函数/算法)将要检索的项与用来检索的索引(称为散列,或者散列值)关联起来,生成一种便于搜索的数据结构(称为散列表)。也译为散列。旧译哈希(误以为是人名而采用了音译)。它也常用作一种资讯安全的实作方法,由一串资料中经过散列算法(Hashing algorithms)计算出来的资料指纹(data fingerprint),经常用来识别档案与资料是否有被窜改,以保证档案与资料确实是由原创者所提供
场景
假设哈希表里有一万对key-value, 比如name:'frank', age: '18... 如何读取hash[xxxx]速度最快
解决办法
- 不做任何算法的优化,
hash[xxxx]需要遍历所有的key,时间为O(n) - 如果对
key排序,使用二分法查找,时间为O(log2 n) - 用字符串对应的
ASCII数字作为索引,时间为O(1) - 对索引做除法,取其余数,时间为
O(1) - 如果冲突了,就扩充容积或者顺延
Tree树
定义
树是一种递归数据结构,包含一个或多个数据节点的集合,其中一个节点被指定为树的根,而其余节点被称为根的子节点
特点
- 除根节点之外的节点被划分为非空集,其中每个节点将被称为子树。
- 树的节点要么保持它们之间的父子关系,要么它们是姐妹节点。
- 在通用树中,一个节点可以具有任意数量的子节点,但它只能有一个父节点。下图显示了一棵树,其中节点A是树的根节点,而其他节点可以看作是A的子节点。
实际运用
- 中国的省市区,可以看成一棵树
- 公司的层级结构,可以看成一颗树
- 网页的节点,可以看出一棵树
代码
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.child.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.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);
const fn = node = {
console.log(node.data);
}
travel(tree, fn);
removeNode(tree, node3)