数据结构之栈与队列

栈

栈遵循先入后出的原则,因此我们只能在栈顶添加或删除元素。然而,数组和链表都可以在任意位置添加和删除元素,因此栈可以视为一种受限制的数组或链表。换句话说,我们可以"屏蔽"数组或链表的部分无关操作,使其对外表现的逻辑符合栈的特性。

所以栈只能改变栈顶的数据

具体实现一个栈(链表实现):

kotlin 复制代码
/* 基于链表实现的栈 */
class LinkedListStack {
    #stackPeek; // 将头节点作为栈顶
    #stkSize = 0; // 栈的长度
​
    constructor() {
        this.#stackPeek = null;
    }
​
    /* 获取栈的长度 */
    get size() {
        return this.#stkSize;
    }
​
    /* 判断栈是否为空 */
    isEmpty() {
        return this.size === 0;
    }
​
    /* 入栈 */
    push(num) {
        const node = new ListNode(num);
        node.next = this.#stackPeek;
        this.#stackPeek = node;
        this.#stkSize++;
    }
​
    /* 出栈 */
    pop() {
        const num = this.peek();
        this.#stackPeek = this.#stackPeek.next;
        this.#stkSize--;
        return num;
    }
​
    /* 访问栈顶元素 */
    peek() {
        if (!this.#stackPeek) throw new Error('栈为空');
        return this.#stackPeek.val;
    }
​
    /* 将链表转化为 Array 并返回 */
    toArray() {
        let node = this.#stackPeek;
        const res = new Array(this.size);
        for (let i = res.length - 1; i >= 0; i--) {
            res[i] = node.val;
            node = node.next;
        }
        return res;
    }
}

当然栈也可以使用数组实现,具体的优劣如下:

1.数据的平均效率是更高的因为一般数组是一段连续的内存

2.但是数组是需要动态扩容的,所以在面临动态扩容的时候下一次操作的时间复杂度会变成O(n)

浏览器撤销与栈

浏览器撤销功能的数据结构就是栈,一般而言有两个栈一个为现有使用的一个为被撤回的,当一个撤回操作进行的时候,其栈顶元素被弹出到另外一个栈顶,此时如果用户实行反撤回,则需要从另外一个栈顶把那个元素弹出来然后回到现有使用的栈

队列

队列有区别与栈,栈是先进后出而队列是先进先出,所以实际上队列我们需要关注的不止是头而且也要关注尾。

这里附一个使用数组的队列(因为有一些地方可以优化)

kotlin 复制代码
/* 基于环形数组实现的队列 */
class ArrayQueue {
    #nums; // 用于存储队列元素的数组
    #front = 0; // 队首指针,指向队首元素
    #queSize = 0; // 队列长度
​
    constructor(capacity) {
        this.#nums = new Array(capacity);
    }
​
    /* 获取队列的容量 */
    get capacity() {
        return this.#nums.length;
    }
​
    /* 获取队列的长度 */
    get size() {
        return this.#queSize;
    }
​
    /* 判断队列是否为空 */
    isEmpty() {
        return this.#queSize === 0;
    }
​
    /* 入队 */
    push(num) {
        if (this.size === this.capacity) {
            console.log('队列已满');
            return;
        }
        // 计算队尾指针,指向队尾索引 + 1
        // 通过取余操作实现 rear 越过数组尾部后回到头部
        const rear = (this.#front + this.size) % this.capacity;
        // 将 num 添加至队尾
        this.#nums[rear] = num;
        this.#queSize++;
    }
​
    /* 出队 */
    pop() {
        const num = this.peek();
        // 队首指针向后移动一位,若越过尾部,则返回到数组头部
        this.#front = (this.#front + 1) % this.capacity;
        this.#queSize--;
        return num;
    }
​
    /* 访问队首元素 */
    peek() {
        if (this.isEmpty()) throw new Error('队列为空');
        return this.#nums[this.#front];
    }
​
    /* 返回 Array */
    toArray() {
        // 仅转换有效长度范围内的列表元素
        const arr = new Array(this.size);
        for (let i = 0, j = this.#front; i < this.size; i++, j++) {
            arr[i] = this.#nums[j % this.capacity];
        }
        return arr;
    }
}

讲讲具体优化在哪,优化在于一般而言删除数组的头元素的时间复杂度为o(n)会比较麻烦,所以这里使用了两个变量来控制位置把删除带来的影响给修正了,也就是说在这种情况下不存在真正的删除,只是通过环形数组,在即将发生越界的情况的时候,把尾指针/头指针循环回去了,但是依旧遵从先进先出这一核心思想

双向队列

双向队列是队列的加强版在原有的基础上增加了队首加入和队尾弹出的功能

相关推荐
jerrywus19 分钟前
我用 GPT-6.1-sol 做了台会讲解的 3D 售货机:60 罐饮料,一口没喝上
前端·agent·claude
孟陬2 小时前
Foobar 技术周刊#159:当看不懂代码时,如何高效排障调试。脏页陷阱:一条只读查询,写入数 TB 数据。推测解码:大模型加速利器
前端·人工智能·后端
Zhao-Jichao2 小时前
AI Prompting for Everyone 课程笔记 - P17 图像生成 Imaging Generation
前端
Highcharts.js2 小时前
智能数据分析组件Orbit 正式发布与快速上手体验教程
前端·人工智能·信息可视化·数据分析系统·highcharts·orbit·智能可视化
Dream-Y.ocean2 小时前
鸿蒙PC平台星图遥感适配实战:基于 Electron 壳方案的纯 JavaScript 遥感图像处理引擎开发
javascript·electron·harmonyos
螺丝钉tina3 小时前
[Playwright ][16]Playwright + TypeScript API 自动化测试实战:API测试
前端·javascript·typescript
小天源4 小时前
智叟云集系统 · 架构与职责分层
服务器·前端·数据库
Highcharts.js5 小时前
React 中原生支持使用 JSX 组件配置自定义表格 Highcharts Grid
前端·javascript·react.js·前端框架·jsx·highcharts·自定义表格
Java后端的Ai之路5 小时前
【AIAgent开发】-Agent工具封装成CLI(纯干货!!!)
java·javascript·人工智能·agent·cli
薛定谔的猫-菜鸟程序员6 小时前
Electron开发超详细教程(从入门到放弃)
前端·javascript·electron