
一、前言
1. 线性结构

2. 线性结构 之 数组结构 - Array

数组应该很熟悉了吧,这里就不多说,直接看文档 : MDN_Array
二、线性结构 之 栈结构 - Stack
1. 概念

栈(stack),它是一种受限的线性结构,后进先出(LIFO) => 也叫先进后出(FILO)
其限制是仅允许在 表的一端 进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底
LIFO(last in first out)表示就是后进入的元素, 第一个弹出栈空间
向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素
从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素
栈常见的操作 :
push(element):添加一个新元素到栈顶位置
pop():移除栈顶的元素,同时返回被移除的元素
peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)
isEmpty():如果栈里没有任何元素就返回true,否则返回false
size():返回栈里的元素个数。这个方法和数组的length属性很类似
2. 栈结构实现 - 数组
JS实现
class arrayStack {
// 私有数据
#dataList = [];
push(item) {
this.#dataList.push(item);
}
pop() {
return this.#dataList.pop();
}
peek() {
return this.#dataList[this.#dataList.length - 1];
}
size() {
return this.#dataList.length;
}
isEmpty() {
return this.#dataList.length === 0;
}
}
// export default arrayStack
// 测试代码
const s1 = new arrayStack();
s1.push(1);
s1.push('2');
s1.push({name:123});
console.log(s1.peek()); // {name:123}
console.log(s1.pop()); // {name:123}
console.log(s1.pop()); // '2'
console.log(s1.pop()); // 1
console.log(s1.pop()); // undefined
console.log(s1.size()); // 0
console.log(s1.isEmpty()); // true
TS实现
interface starStack<T> {
// 入栈
push(item: T): void;
// 出栈
pop(): T | undefined;
// 看栈顶元素
peek(): T | undefined;
// 栈个数
size(): number;
// 是否为空
isEmpty(): boolean;
}
class arrayStack<T> implements starStack<T> {
// 私有数据
private dataList: T[] = [];
push(item: T): void {
this.dataList.push(item);
}
pop(): T | undefined {
return this.dataList.pop();
}
peek(): T | undefined {
return this.dataList[this.dataList.length - 1];
}
size(): number {
return this.dataList.length;
}
isEmpty(): boolean {
return this.dataList.length === 0;
}
}
// export default arrayStack
// 测试代码
const s1 = new arrayStack<number>();
s1.push(1);
s1.push(2);
s1.push(3);
console.log(s1.peek()); // 3
console.log(s1.pop()); // 3
console.log(s1.pop()); // 2
console.log(s1.pop()); // 1
console.log(s1.pop()); // undefined
console.log(s1.size()); // 0
console.log(s1.isEmpty()); // true
3. 栈结构实现 - 链表
4. 面试题 - 十进制转其他进制
分析
以十进制 转 二进制为例
对2进行求余,最后从下往上连起来即是答案

代码
import Stack from './IStack';
/**
* @param decimal 十进制数字
* @param conversionType 转换进制类型
* @returns 转换后结果
*/
const baseConversion = (decimal: number, conversionType: number): string => {
// 1. 创建栈
const stack = new Stack<number>();
// 2. 判断是否求余成功
while (decimal > 0) {
stack.push(decimal % conversionType);
decimal = Math.floor(decimal / conversionType);
}
// 3. 组装数据
let binary = '';
while (!stack.isEmpty()) {
binary += stack.pop()?.toString();
}
// 4. 返回
return binary;
};
// 十进制 转 二进制
console.log(baseConversion(100, 2)); // 1100100
// 十进制 转 八进制
console.log(baseConversion(100, 8)); // 144
// 十进制 转 十六进制
console.log(baseConversion(100, 16)); // 64
5. 面试题 - 有效的括号

分析
解这道题目主要的问题就是一一对应,典型的用栈来解决
当遇到左括号的时候,立马把右括号放进栈中
当遇到右括号的时候,从栈顶与之对比,如果不一样,则不是有效的括号
栈实现
interface starStack<T> {
// 入栈
push(item: T): void;
// 出栈
pop(): T | undefined;
// 看栈顶元素
peek(): T | undefined;
// 栈个数
size(): number;
// 是否为空
isEmpty(): boolean;
}
class Stack<T> implements starStack<T> {
// 私有数据
private dataList: T[] = [];
push(item: T): void {
this.dataList.push(item);
}
pop(): T | undefined {
return this.dataList.pop();
}
peek(): T | undefined {
return this.dataList[this.dataList.length - 1];
}
size(): number {
return this.dataList.length;
}
isEmpty(): boolean {
return this.dataList.length === 0;
}
}
const strMap: {
'{': string;
'(': string;
'[': string;
} = {
'{': '}',
'(': ')',
'[': ']'
};
const isValid = (str: string): boolean => {
if (str.length % 2 === 1) return false;
const stack = new Stack<string>();
for (let i = 0, length = str.length; i < length; i++) {
const c: string = str[i];
switch (c) {
case '{':
case '(':
case '[':
stack.push(strMap[c]);
break;
default:
if (stack.isEmpty() || c !== stack.peek()) return false;
stack.pop();
}
}
return stack.isEmpty();
};
const hasCorrespond = (c: string, str: string): boolean => {
return str.indexOf(c) !== -1;
};
数组实现
const strMap = {
'{': '}',
'(': ')',
'[': ']'
};
const isValid = (str: string): boolean => {
// 如果是奇数,直接返回
if (str.length % 2 === 1) return false;
const stack = []
for (let i = 0, length = str.length; i < length; i++) {
const c: string = str[i];
switch (c) {
case '{':
case '(':
case '[':
// 如果是左边的,直接推入对应的右边的字符进入栈中
stack.push(strMap[c]);
break;
default:
// 如果来到这,说明是右边字符
// 如果此时栈内为空,说明没有匹配的左边字符,直接返回false
// 如果和栈顶元素不一样,返回false
if (!stack.length || c !== stack[stack.length -1]) return false;
// 如果和栈顶元素一样,出栈,进行下一次循环
stack.pop();
}
}
// 结束循环后,如果栈内还有数据,说明没匹配完,返回false,否则返回true
return !stack.length;
};
6. 面试题 - 包含min函数的栈

解法一
使用辅助栈 : 辅助栈和主栈时刻保持同样的个数
class MinStack {
// 主栈
private stack: number[] = [];
// 最小栈
private minStack: number[] = [];
// 入栈
push(x: number): void {
// 主栈直接推入,保持正确的顺序
this.stack.push(x);
// 获取最小栈的栈顶元素
const n = this.minStack[this.minStack.length - 1];
// 当栈为空时,直接传入
if (n === undefined) {
this.minStack.push(x);
} else {
// 判断传入的值是否比之前的值小
const res = n > x ? x : n;
this.minStack.push(res);
}
}
// 出栈,同时出栈
pop(): void {
this.stack.pop();
this.minStack.pop();
}
// 返回主栈的栈顶元素
top(): number {
return this.stack[this.stack.length - 1];
}
// 返回最小栈的栈顶元素
min(): number {
return this.minStack[this.minStack.length - 1];
}
}
解法二
使用辅助栈 : 辅助栈只保存比之前栈顶元素小的值,当主栈和辅助栈数值相等时,弹出辅助栈

class MinStack {
// 主栈
private stack: number[] = [];
// 最小栈
private minStack: number[] = [];
// 入栈
push(x: number): void {
// 主栈直接推入,保持正确的顺序
this.stack.push(x);
// 获取最小栈的栈顶元素
const n = this.minStack[this.minStack.length - 1];
// 当栈为空时,直接传入
if (n === undefined) {
this.minStack.push(x);
} else {
// 判断栈顶元素大于等于传入的值,则入栈
if (n >= x) {
this.minStack.push(x);
}
}
}
// 出栈
pop(): void {
// 主栈直接出栈,
const res = this.stack.pop();
// 如果出栈的元素和最小栈栈顶元素相同,则最小栈出栈
if (res === this.minStack[this.minStack.length - 1]) {
this.minStack.pop();
}
}
// 返回主栈的栈顶元素
top(): number {
return this.stack[this.stack.length - 1];
}
// 返回最小栈的栈顶元素
min(): number {
return this.minStack[this.minStack.length - 1];
}
}
7. 面试题 - 用栈实现队列

class MyQueue {
// 创建入栈
private enStack: number[];
// 创建出栈
private outStack: number[];
constructor() {
this.enStack = [];
this.outStack = [];
}
push(x: number): void {
// 推入元素到入栈
this.enStack.push(x);
}
pop(): number {
// 如果出栈有值,直接返回
if (this.outStack.length) return this.outStack.pop()!;
// 把入栈的元素倒入出栈中
while (this.enStack.length) {
this.outStack.push(this.enStack.pop()!);
}
// 返回顶部元素
return this.outStack.pop()!;
}
peek(): number {
// 如果出栈有值,直接返回
if (this.outStack.length) return this.outStack[this.outStack.length - 1];
// 把入栈的元素倒入出栈中
while (this.enStack.length) {
this.outStack.push(this.enStack.pop()!);
}
// 返回顶部元素
return this.outStack[this.outStack.length - 1];
}
empty(): boolean {
// 当两个栈都为空时,返回true
return this.enStack.length === 0 && this.outStack.length === 0;
}
}