javascript数据结构与算法-栈结构
发布日期:2021-05-14 11:06:54 浏览次数:16 分类:精选文章

本文共 3192 字,大约阅读时间需要 10 分钟。

JavaScript 数据结构与算法(三)栈结构

1. 栈结构概述

数组是一个线性结构,能够在任意位置插入和删除元素。然而,栈是一种受限制的线性结构,具有特殊的操作规则——后进先出(LIFO),即新添加的元素总是在栈顶,移除时则是从栈顶开始。(LIFO - Last In, First Out)

栈的主要特点是先进后出,后进先出。其核心操作包括进栈和出栈,分别对应于arr.push()和arr.pop()。栈的特性使其在处理函数调用、递归以及数据优先级等场景中非常有用。下面将详细探讨栈的实现和应用。

2. 栈常见操作

栈的基本操作包括:

-
push(): 将新元素压入栈顶。 -
pop(): 移除栈顶元素并返回该元素。 -
peek(): 读取栈顶元素而不修改栈结构。 -
isEmpty(): 检查栈是否为空。 -
size(): 返回栈中元素的个数。 -
toString(): 将栈内容转换为字符串表示。

这些操作对应于数组的push、pop、peek等方法,但栈的操作有限制,仅允许在栈顶进行。

3. 栈在程序中的应用

栈的应用领域包括:

- **函数调用栈**: 函数在调用时将自身压入栈底,每次调用子函数时将父函数的上下文压入栈。例如,函数调用顺序A→B→C(深度优先)会按照C→B→A弹出处理。 - **递归**: 递归函数通过不断压入自身引用至栈顶实现调用,直到满足终止条件,避免深度过度导致栈溢出。 - **后缀表达式计算**: 栈常用于处理后缀表达式,通过一次性压入数据并按需弹出结果。

4. 栈操作合法性判断

题目:6,5,4,3,2,1按顺序进入栈,哪个出栈序列是非法的?

选项包括A、B、C、D四个选项。 分析结果显示选项C的出栈顺序3,4,6,5,2,1是非法的,因为按照原有入栈顺序6→5→4→3→2→1,只有选项C的部分出栈步骤高于入栈元素的实际深度。具体来说,3的出栈需先有4和5在栈内,而这些元素在选项C中未被正确保留。因此,选项C的出栈顺序不符合栈操作规则。

5. 基于数组实现栈结构

在JavaScript中,使用数组实现栈非常简单。以下是一个实现栈的类:

class StackArray {
constructor() {
this.items = [];
}
push(element) {
this.items.push(element);
}
pop() {
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
clear() {
this.items = [];
}
toString() {
return this.items.toString();
}
}

该实现通过数组的基本操作实现栈功能,操作简单且性能较好。栈的主操作包括push、pop、peek等,用户可以根据需要扩展其他功能。

6. 栈的应用-十进制转二进制

利用栈实现十进制转二进制的算法:依次除以2,将余数存储在栈中。最后从栈底读取并倒序排列即可得到二进制表示。但需注意算法细节和优化。以下是一个实现示例:

function dec2bin(dec) {
const stack = new StackArray();
while (dec > 0) {
stack.push(dec % 2);
dec = Math.floor(dec / 2);
}
let binaryString = '';
while (!stack.isEmpty()) {
binaryString += stack.pop().toString();
}
return binaryString;
}

该实现首先推导余数到栈中,最后反转栈中的元素顺序组合成二进制字符串。该算法的时间复杂度为O(log N),其中N为输入数值,适用于较大数值的转换。算法的几何效率因栈操作而有所优势。

7. 栈结构的实现与保护

7.1 使用ES2015的Symbol实现类

Symbol在ES2015中引入后,为栈实现提供了更高的安全性。通过Symbol作为栈元素存储键,可以确保属性是私有化的。以下是一个实现示例:

const _items = Symbol('items');
class Stack {
constructor() {
this[_items] = [];
}
push(element) {
this[_items].push(element);
}
pop() {
return this[_items].pop();
}
peek() {
return this[_items][this[_items].length - 1];
}
isEmpty() {
return this[_items].length === 0;
}
size() {
return this[_items].length;
}
clear() {
this[_items] = [];
}
toString() {
return this[_items].toString();
}
}

此实现通过隐藏的Symbol键存储栈元素,保护了内部数据结构,防止外部干扰。这样的实现方式比传统对象实现更具安全性,适用于需要高安全性的场景。

7.2 使用WeakMap实现栈结构

WeakMap结合了对象引用和弱键的特性,适用于需要高效垃圾回收的场景。以下是一个基于WeakMap实现栈的示例:

class Stack {
constructor() {
this.map = new WeakMap();
}
push(element) {
this.map.set(element, element);
}
pop() {
return this.map.getAndRemove(true)?.value;
}
peek() {
return this.map.get(this.map.size - 1)?.value;
}
isEmpty() {
return this.map.size === 0;
}
size() {
return this.map.size;
}
clear() {
this.map.clear();
}
}

此实现通过WeakMap的键值对存储栈元素,键为对象或数组,值可为任意类型。由于WeakKey仅存储弱引用,无需额外释放内存,简化了管理流程。此外,当引用释放时,WeakMap会自动移除对应的记录,保持内存健康。

8. 栈结构的核心优势

栈作为一种基础数据结构,具有以下核心优势:

-
LIFO特性: 异或程序流程控制问题。 -
轻松管理上下文: 函数调用栈确保每个函数调用都有独特的上下文环境。 -
高效内存管理: 先进后出的特性确保内存利用 compact。

了解这些特性有助于在实际开发中更好地选择和使用栈结构,提升应用程序的效率和性能。

上一篇:JavaScript高级程序设计第四版学习记录-第八章对象、类与面向对象编程(二)(继承 / 类)
下一篇:JavaScript高级程序设计第四版学习记录-第八章对象、类与面向对象编程(一)(对象)

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2025年04月26日 04时39分26秒