学习内容
- 栈(Stack)
- 队列(Queue)
- 链表(Stack)
- 树(Tree)
- 哈希表(Hash Table)
- 图(Graph)
栈(Stack)
栈可以看成是一种受限的线性结构
相关概念
- 栈顶:允许数据进出的唯一出口
- 栈底:相对于栈顶的另一端
- 进栈:把新元素放在栈顶
- 出栈:把栈顶的元素取出
特点
- 后进先出(LIFO):指的是最后进入的元素第一个弹出栈空间
- 次序不变:栈中的各个数据之间的相对位置是不允许发生变化的(对顺序有严格的要求)
栈的实现
实现方式
- 基于数组实现栈
- 基于链表实现
栈的方法
- push: 入栈
- pop: 出栈
- peek: 查找栈顶元素
- isEmpty: 是否为空栈
- size: 返回栈的大小(元素个数)
- toString: 打印栈中元素
基于数组实现
class Stack<Element> {
items: Element[] = [];
// 入栈
push(node: Element) {
this.items.push(node);
}
// 出栈
pop(): Element | null {
return this.isEmpty() ? null : this.items.pop[this.size() - 1]
}
// 是否为空
isEmpty(): boolean {
return !!this.size();
}
// 栈的大小
size(): number {
return this.items.length;
}
// 查询栈顶元素
peek(): Element | null {
return this.isEmpty() ? null : this.items[this.size() - 1];
}
// toString 打印栈中元素
toString(flag: string): string {
return this.items.join(flag);
}
}
应用一: 进制转换
- 十进制转n进制
function converter(num: number, bs: number) {
let left = num;
const stack = new Stack();
let result: string = "";
const digits = "0123456789ABCDEF";
while(left > 0) {
stack.push(left % bs);
left = Math.floor(left / bs);
}
while (!stack.isEmpty()) {
result += digits[stack.pop() as number]; //出栈
}
// 也可以用toString 进行反转
return result;
}
应用二: 有效括号
- 思路就是前一半入栈,后一半出栈,能出栈完的就是有效括号
{ 和 }
or[ 和 ]
or( 和 )
这个算匹配上了
type back = null | string;
function isValid(s = ""): back {
const stack = new Stack();
for (let i = 0; i < s.length; i++) {
// 当前字符
const cur = s[i];
// 栈顶字符
const p = stack.peek();
if (
(cur == ")" && p == "(") ||
(cur == "]" && p == "[") ||
(cur == "}" && p == "{")
) {
stack.pop();
} else {
stack.push(cur);
}
}
return stack.isEmpty();
}
console.log(isValid('()')) // true
console.log(isValid('()[]{}')) // true
console.log(isValid('(]')) // false
console.log(isValid('([)]')) // false
console.log(isValid('{[]}')) // true
队列(Queue)
队列是除了栈之外的另外一种受限的线性结构
相关概念
- 对头: 队列的头部,队列中最先进入的元素放在此处
- 队尾: 队列的尾部,队列中最后进入的元素放在此处
特点:
- 队列是另外一种受限的线性结构,受限表现在元素必须是先进先出的
- 表现在其只允许在一端添加元素,而只在另一端删除元素
--------------------
入队 5 -> 4 -> 3-> 2 -> 1 出队
---------------------
队列的实现
- 基于js数组实现
队列的方法
- enqueue: 向队尾添加元素
- dequeue: 队头出队
- front: 查询队头元素
- isEmpty: 判断队列是否为空
- size: 队列的元素个数
- toString:打印队列中的元素
class Queue<Element> {
items: Element[] = [];
// 入队
enqueue(node: Element): void {
this.items.push(node);
}
// 出队
dequeue(): Element | null {
return this.isEmpty() ? null : this.items.shift()!;
}
// 是否为空队列
isEmpty(): boolean {
return this.items.length > 0;
}
// 查看头部元素
front(): Element | null {
return this.isEmpty() ? null : this.items[0];
}
// 返回元素个数
size(): number {
return this.items.length;
}
// 打印元素
toString(flag: string): string {
return this.items.join(flag);
}
}
应用击鼓传花
- 制定规则,每次都是报数为3的同学淘汰
function passGame<T>(stuList: T[], num: number) {
const queue = new Queue1<T>();
// 初始化数据
stuList.forEach(el => {
queue.enqueue(el);
});
// 核心逻辑
while(queue.size() > 1) {
for(let i = 0; i < num - 1; i++) {
// 把通过的学生出队列再入队列
queue.enqueue(queue.dequeue() as T);
}
// 这个是淘汰的
queue.dequeue();
}
return queue.front();
}
const stuList = [
"stu1",
"stu2",
"stu3",
"stu4",
"stu5",
"stu6",
"stu7",
]
passGame<string>(stuList, 3);
优先级队列
- 优先级队列相比于普通队列,其特殊之处表现在:队列中的每个元素都有一个优先级,新插入的新元素不一定在队尾,而是根据其优先级确定正确的位置。
- 意思就是可以搞特殊化(开后门) todo
概念
- 优先级:指的是队列中元素的权重,是排序的依据
特点
- 相比于普通队列,元素不遵守先进先出的原则,而是根据优先级/权重决定顺序
代码实现
interface IQueueElement<T> {
value: T;
priority: number;
}
// 优先级队列
class PriorityQueue<T> {
items: IQueueElement<T>[] = [];
static QueueElement = class<T> {
public priority : number;
constructor(public value: T, priority: number) {
this.value = value;
this.priority = priority;
}
}
enqueue(value: T, priority: number): void {
// 创建一个优先队列的节点
const element: IQueueElement<T> = new PriorityQueue.QueueElement(value, priority);
if(this.isEmpty()) {//如果为空直接加
this.items.push(element);
}else {
let len = this.size();
let isAdd = false;
for(let i = 0; i < len; i++) {
const curPriority = this.items[i].priority;
// 如果我的权重 > 当前的就排在这个位置
if(priority > curPriority) {
this.items.splice(i, 0, element);
isAdd = true;
break;
}
}
// 所有的循环完了都没插入就插在末尾
if(!isAdd) {
this.items.push(element);
}
}
}
// 出队
dequeue(): IQueueElement<T> | null {
return this.isEmpty() ? null : this.items.shift()!;
}
// 是否为空队列
isEmpty(): boolean {
return this.items.length > 0;
}
// 查看头部元素
front(): IQueueElement<T> | null {
return this.isEmpty() ? null : this.items[0];
}
// 返回元素个数
size(): number {
return this.items.length;
}
// 打印元素
toString(flag: string): string {
return this.items.map(item => item.value).join(flag);
}
}
链表(Stack)
树(Tree)
哈希表(Hash Table)
图(Graph)
跟着下面这个前辈学习