总结前端常用数据结构 之 栈篇【JavaScript 】
真正的大师永远都怀着一颗学徒的心。——易大师
栈!
- 什么是栈?
- 栈的增加/删除:
- 栈常用的辅助方法:
- 封装栈:
- 封装栈的实际应用例子:
- 十进制转二进制(辗转相除法):
- 十进制转任意进制:
- 栈和数组的区别:
- 栈和数组对比的应用场景:
- 栈-前端应用场景举例:
- 页面路由历史管理:
- 撤销操作:
- 递归函数调用栈:
什么是栈?
栈是一种特殊的线性数据结构,它只允许在一端进行插入和删除操作,这一端被称为栈顶,相对的另一端称为栈底。 栈按照后进先出 Last in First Out(LIFO)的原则存储数据,即最后进入栈的数据会最先被删除。【限定仅在表尾进行插入和删除操作的线性表】
栈的增加/删除:
- Stack增加使用 push() 方法
- Stack删除使用 pop() 方法
可以理解为栈是一个特殊的数组。
栈常用的辅助方法:
方法/属性 | 作用 | 示例 |
---|---|---|
length | 获取当前栈元素数量 | if (stack.length === 0) {…} |
peek() | 查看栈顶元素(需自定义实现) | function peek() { return stack.at(-1) } |
isEmpty() | 判断栈是否为空(需自定义实现) | function isEmpty() { return stack.length === 0 } |
… | … | … |
封装栈:
// push添加一个元素到栈顶
// pop出栈
// peek 返回栈顶
// isEmpty() 判断栈是否为空
// clear() 清空栈结构
// size() 栈的元素个数
// toString() 转换成普通的字符串
class Stack{
// 这里的#是私有的 es13中提到
#items = [];
pop(){
return this.#items.pop();
}
push(data){
this.#items.push(data);
}
peek(){
// return this.#items[this.items.length - 1];
return this.#items.at(-1);
}
isEmpty(){
return this.#items.length === 0;
}
clear(){
this.#items = [];
}
size(){
return this.#items.length;
}
toString(){
return this.#items.join("");
}
}
let stack = new Stack();
封装栈的实际应用例子:
十进制转二进制(辗转相除法):
接上面栈的代码:
function convert (decNumber) {
let remStack = new Stack();
let string = "";
let number = decNumber;
while (number > 0) {
remStack.push(number % 2);
number = Math.floor(number / 2);
}
while (!(remStack.isEmpty())) {
string += remStack.pop();
}
return string;
}
console.log('50转为二进制是:', convert(50));
十进制转任意进制:
function convert (decNumber, base) {
let remStack = new Stack();
let string = "";
let number = decNumber;
let baseString = "0123456789ABCDEF"
while (number > 0) {
remStack.push(number % base);
number = Math.floor(number / base);
}
while (!(remStack.isEmpty())) {
string += baseString[remStack.pop()];
}
return string;
}
convert(50, 2);
convert(50, 8);
convert(50, 16);
栈和数组的区别:
特性 | 数组 | 栈 |
---|---|---|
操作方式 | 自由增删改查(通过索引) | 仅允许push(入栈)、pop(出栈) |
访问权限 | 任意位置直接访问 | 仅能访问栈顶元素 |
底层实现 | 内存中的连续空间 | 可用数组或链表实现 |
灵活性 | 高(可动态扩容或缩容) | 低(严格遵循LIFO规则) |
栈和数组对比的应用场景:
- 数组 :适合需要快速随机访问的场景,如排序算法、矩阵运算、数据缓存等。
- 栈:适合需要回溯或反转顺序的场景,如函数调用栈、括号匹配、表达式求值、浏览器前进后退等。
栈-前端应用场景举例:
页面路由历史管理:
// 模拟浏览器后退栈
const historyStack = new Stack();
historyStack.push("/home");
historyStack.push("/detail");
historyStack.pop(); // 返回上一页
撤销操作:
const undoStack = new Stack();
const redoStack = new Stack();
function doAction(action) {
undoStack.push(action);
redoStack.items = []; // 清空重做栈
}
递归函数调用栈:
代码举例计算阶乘:n × (n-1) × … × 1
function factorial(n, stack = new Stack()) {
stack.push(`计算 factorial(${n})`);
if (n === 1) return 1;
const result = n * factorial(n - 1, stack);
stack.pop(); // 调用完成出栈
return result;
}