news 2026/9/1 4:44:04

栈和队列及习题讲解1

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
栈和队列及习题讲解1

从内存先拿到寄存器,运算完再放回内存

int ret1 = ++i;前置自增

mov eax,dword ptr [i] ; 把i从内存读到eax add eax,1 ; eax = eax +1 【先自增】 mov dword ptr [i],eax ; 把+1后的值写回i内存 mov ecx,dword ptr [i] ; 读取已经更新完的i mov dword ptr [ret1],ecx; 把**自增之后**的值赋给ret1

逻辑:先 ++,再赋值

ret1 拿到的是 i 增加完成后的新值。


int ret2 = i++;后置自增

mov eax,dword ptr [i] ; 读取i原始旧值 mov dword ptr [ret2],eax; 【先把旧值赋值给ret2】 mov ecx,dword ptr [i] ; 再读i旧值 add ecx,1 ; ecx = ecx+1 mov dword ptr [i],ecx ; 写回i,完成i自增

要实现的接口

size永远代表有效元素数量,全世界教材统一。

  • 空表:size = 0
  • 最后一个有效元素下标:size‑1
  • 新元素插在表尾:data[size++] = x;

栈的 top 为什么会有两套?

栈的top存的是数组下标,不是计数!

下标本身就有两种理解,所以诞生两套流派:

top=0:top 是下一个可存放位置的下标(等价于顺序表的 size)top=-1:

top 是当前栈顶元素的下标入栈

需要拿掉栈顶元素才能访问下一个

括号的匹配,最后也排除了左括号多的情况出栈可以和入栈顺序不同,但出队和入队顺序一定相同

/ 入队,需要二级指针 QNode**原先的

void QueuePush(QNode** pphead, QNode** pptail, QDataType x)

{

// 创建新节点

QNode* newnode = (QNode*)malloc(sizeof(QNode));

//...

if(*pphead == NULL)

{

*pphead = newnode; // 修改外部phead本身

*pptail = newnode; // 修改外部ptail本身

}

else

{

(*pptail)->next = newnode;

*pptail = newnode;

}

}

// 调用的时候要传地址

QueuePush(&phead, &ptail, 10);

成为成员之后只需要传结构体的地址

void QueuePush(Queue* pq, QDataType x)

{

QNode* newnode = (QNode*)malloc(sizeof(QNode));

//...

if(pq->phead == NULL)

{

pq->phead = newnode;

pq->ptail = newnode;

}

else

{

pq->ptail->next = newnode;

pq->ptail = newnode;

}

}

//调用:只传结构体地址,一级指针

Queue q;

QueueInit(&q);

QueuePush(&q,10);防止只有一个节点free以后ptail是野指针的问题一定要防止野指针的出现,就是phead和ptail,因为后面的接口要访问他们的成员用两个队列实现栈往空的里面插入底层结构

1. 入栈(push)—— O(1)

c

void myStackPush(MyStack* obj, int x) { if(!QueueEmpty(&obj->q1)) { QueuePush(&(obj->q1), x); // q1 非空,入 q1 } else { QueuePush(&(obj->q2), x); // q1 空,入 q2(不管 q2 是否为空) } }

✅ 两个队列都为空时,默认入 q2(因为 q1 空,走 else)


2. 出栈(pop)—— O(n),核心轮转

你的"假设法"非常经典:

c

// 先假设 q1 是空,q2 是非空 Queue* empty = &(obj->q1); Queue* nonEmpty = &(obj->q2); // 检查假设是否正确,如果 q1 非空,说明假设反了 if(!QueueEmpty(&(obj->q1))) { nonEmpty = &(obj->q1); empty = &(obj->q2); }

然后轮转:

c

// 把 nonEmpty 中除了最后一个元素外,全部搬到 empty while(QueueSize(nonEmpty) > 1) { QueuePush(empty, QueueFront(nonEmpty)); QueuePop(nonEmpty); } // 此时 nonEmpty 只剩一个元素(就是栈顶),弹出它 int top = QueueFront(nonEmpty); QueuePop(nonEmpty); return top;

3. 取栈顶(top)—— 利用队列的队尾接口

c

int myStackTop(MyStack* obj) { if(!QueueEmpty(&(obj->q1))) { return QueueBack(&(obj->q1)); // 非空队列的队尾就是栈顶 } else { return QueueBack(&(obj->q2)); } }

这里用了一个关键点:队列的队尾(back)正好对应栈顶,因为入栈时元素都在队尾追加。

多开一个空间,解决判空和判满相重合的问题解决回绕问题两种取尾的数据结果一样删除和增加都要有回环的能力

获取头的数据,比尾部简单因为尾部是有效节点的下一个位置

就是包含加减的都会比较麻烦,因为有回环的问题链表判断空很简单,但是取尾部很麻烦

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 4:43:56

Python编程指南:从零到一的全面构建

一、 引言:为什么选择?浩如烟海的编程语言里头, 它靠着简洁的语法, 依靠强大的生态, 凭借极高的开发效率, 变成了现今世界上颇受欢迎的编程语言当中的一个。不管是人工智能领域, 还是数据分析范畴, 亦或是Web后端开发方面, 又或者是自动化脚本这块, 它都…

作者头像 李华
网站建设 2026/9/1 4:41:27

3DGS 速报 · 第 6 期(2026.08.24 – 08.29)

追踪 3D Gaussian Splatting 前沿,每周一更。点击「关注」并设为星标 ⭐,第一时间拿到最新论文、工具与落地动态(本期收录窗口 2026.08.24 – 08.29)。 本期关键词:水下重建 压缩 评测基准 工具链📌 本期…

作者头像 李华
网站建设 2026/9/1 4:41:18

26年课程论文初稿工具怎么选?八款实测对号入座

期末季的课程论文压力,往往集中在初稿阶段集中爆发。选题方向模糊、文献梳理耗时、初稿结构松散,这些问题几乎每个学生都遇到过。本文选取市面上八款主流写作辅助工具进行实测,按不同需求场景归类,帮读者根据自身情况快速定位合适…

作者头像 李华
网站建设 2026/9/1 4:40:37

FDE 到底做什么、这个岗位从哪来、为什么最近几年集中爆发

本章导读:FDE(Forward Deployed Engineer,前线部署工程师)是把工程师派驻客户现场、以工程手段直接解决业务问题的岗位。本章回答四个问题:FDE 到底做什么、这个岗位从哪来、为什么最近几年集中爆发、它和售前工程师与…

作者头像 李华
网站建设 2026/9/1 4:39:14

GNSS欺骗检测:IMU+GNSS融合定位的卡方检验与Matlab实现

简介:针对全球卫星导航系统欺骗攻击与惯性测量单元融合定位需求,资料包提供了从原理分析到算法实现的完整参考,面向卫星导航、组合导航方向的研究人员、工程师及高年级学生。压缩包共包含30个文件,以13张示意图片、13个扩展标记文…

作者头像 李华
网站建设 2026/9/1 4:35:51

Java Agent开发实战:从零构建具备感知决策执行能力的智能体

在实际 Java 项目向智能化演进的过程中,Agent(智能体)开发正从一个前沿概念转变为一项可落地的工程实践。许多具备扎实 Java 背景的开发者,在面对 Agent 开发时,常常感到困惑:它和传统的微服务、定时任务或…

作者头像 李华