基础数据结构
算法:串联整体逻辑的逻辑流 - 其依赖底层的数据结构
数据结构:数组-列表 栈-队列 哈希 树
- 数组 & 链表 [1,2,3,4]
链表的头为head,尾为null
-
共性 --> 展示上都是一串元素的顺序集合
-
不同 数组: 存储上都是以顺序存储的方式,即保存的时候一定是地址连续存储的,相邻两个元素是连续的,并可以通过index定位到。
链表: 储存上元素地址不一定连续,不可以通过index获取某个值,但存在next指针指向下一个值。好处是可以知道下一个值会指向哪里。
总结:
查找:数组连续,效率高。可以迅速定位到数组中某一个节点位置;而链表则需要通过前一个元素指向下一个地址,需要前后依赖,效率低。 插入:数组中元素插入会引起后被插入位后所有元素索引的改变;而链表只需要改变某一个节点的next。(如在2,3间插入,改变2的next指针指向当前元素,再将当前元素的next指向3即可)
面试题: 实现链表/链表类 思路:head => node1 => node2 => ... => null
链表类结构:
class LinkList {
constructor () {
this.length = 0;
// 默认没有元素,那么头就指向尾
this.head = null; // 可以用作链表是否为空的判断
}
// 一般会有的方法
getElementAt(position) {
} // 返回索引对应的元素
append (element) {} // 添加节点
insert (position, element) {} // 指定位置添加节点
removeAt (position) {} // 删除指定位置元素
indexOf (element) {} // 查找元素索引
// remove 直接删除
// isEmpty 是否为空
// size 当前链表大小
// getHead 获取当前链表的头
// clear/reset 重制
}
具体实现:
getElementAt(position) {
// 1. 边缘/安全检测
if (position < 0 || position >= this.length) return null;
// 本地变量
let _current = this.head; // 保存当前的头
// 查找当前链表,是否有当前的节点
for (let i = 0; i < position; i ++) {
_current = _current.next;
}
return _current;
}
// 组装标准链表节点的辅助类
class Node {
constructor(element) {
this.element = element;
// 默认不指向任何地方
this.next = null;
}
}
// 生成复杂元素node
append(element) {
let node = new Node(element);
// 添加时两种情况:链表为空或者非空
// 链表为空
if (this.head = null) {
this.head = node;
} else {
// 不为空,找到尾巴
let _current = this.getElementAt(this.length - 1);
_current.next = node;
}
this.length ++;
}
insert(position, element) {
if (position < 0 || position > this.length) return false;
let node = new Node(element);
if (position === 0) {
node.next = this.head;
this.head = node;
} else {
let previous = this.getElementAt(position - 1);
node.next = previous.next;
previous.next = node;
}
this.length ++;
return true;
}
removeAt(position) {
if (position < 0 || position > this.length) return false;
let _current = this.head;
if (position === 0) {
this.head = _current.next;
} else {
let previous = this.getElementAt(position - 1);
_current = previous.next;
previous.next = _current.next;
}
this.length --;
return _current.element;
}
indexOf(element) {
let _current = this.head;
for (let i = 0; i < this.length; i++) {
if (current.element === element) return i;
current = current.next;
}
return -1;
}
双向链表 head <=> node1 <=> node2 <=> ... <=> null(tail) 多了一个tail和prev 面试:实现一个双向链表 思路:单向链表做继承
class DoubleLink extends LinkList {
// ...
}
- 栈 & 队列
执行顺序不同:
- 栈: 先入后出。类似一个盒子,放入123,拿出321 (队列:先入显出。流水线
面试题:实现一个栈
class Stack {
constructor() {
this.items = [];
}
// 添加新元素到栈
push(element) {
this.items.push(element);
}
// 移除栈顶元素
pop() {
return this.items.pop();
}
// 获取栈顶元素
getPeak() {
return this.items[this.items.length -1];
}
// 判断空
isEmpty() {
return this.items.length === 0;
}
clear() {
this.items = [];
}
size() {
return this.items.length;
}
}
- 堆 heap JS中有两种存储方式:stack & heap 普通类型数据:在栈内直接存储值 高级引用类型:在栈内存储内存地址,地址指向堆,堆中存放具体的值
为什么这么存放?
对象和数组的数据大小较大,性能考量 函数的执行也在栈内
JS 执行 及 异步
面试题:判断括号有效性?(自闭合)
// input: '{}[]' => true, '{{}[]' => false, '[{{}}]' true
const isValid = function (s: string) {
// 涉及使用数据结构 - 栈
// 原因:栈的结构,单向,先放入一个,再放入一个,如果能匹配到,拿出;如果匹配不上,继续往下放
const stack = new Stack();
// 检查对应关系
const map = {
'}': '{',
']': '[',
')': '('
}
for(let i = 0; i < s.length; i++) {
// 1. 逐个入栈
const char = s[i];
stack.push(char);
if (stack.size() < 2) continue;
// 2. 判断最后两个是否配对
const theLastOne = stack.items[stack.size -1];
const theLastTwo = stack.items[stack.size -2];
if (map[theLastOne] === theLastTwo) {
stack.pop();
stack.pop();
}
}
// 3. 衡量闭合有效性
return stack.size() === 0;
};
- 哈希:快速匹配定位 密码、罗马文、回文
面试:罗马文转数字 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 VI 6 IV 4
const MAP = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000
}
const romanToInt = function(s: string) {
let len = s.length;
let res = 0;
let max = 0;
while(len--) {
let num = MAP[s[len]];
// 特殊情况 IV 颠倒值不直接相加,用大的
if (max > num) {
res -= num;
continue;
}
max = num;
res += num;
}
return res;
}
- 树 基础概念: 查找方式:深度优先、广度优先 查找顺序:3种遍历方式
- 前序遍历:中左右 5-4-1-2 6-7-8
- 中序遍历:左中右 1-4-2-5 7-6-8
- 后序遍历:左右中 1-2-4 7-8-6 5
树结构
遍历顺序实现:
// 前序遍历 中左右
const PreOrder = function(node) {
if (node !== null) {
console.log(node);
PreOrder(node.left);
PreOrder(node.right);
}
}
// 中序遍历 左中右
const InOrder = function(node) {
if (node !== null) {
InOrder(node.left);
console.log(node);
InOrder(node.right);
}
}
// 后续遍历 左右中
const PostOrder = function(node) {
if (node !== null) {
PostOrder(node.left);
PostOrder(node.right);
console.log(node);
}
}
查找最大值、偶数层、拍平--Array.flat()