Mergeable Stack ZOJ - 4016

Mergeable Stack

ZOJ - 4016

题目链接



这道题真的很坑爹,一开始做的时候感觉很简单的一道关于栈操作的题,然后就用了stl里的stack结果爆内存了,然后改了三次还是不行,后来还用了string字符串,先是超时后来又爆内存,大概是因为stl里面的容器内存不够时是二倍扩展,每次二倍就指数增长了,然后题目再恰好弄个数据让你一扩展乘2就超过题目要求内存了,真的坑。
后来听说有同学自己写的链式栈过了,因为是用一个申请一个内存,然后写了个答案错误妈的不改了。再后来看了大佬的代码,感觉很巧妙。

思路是把所有数字都存在一个数组a中,然后用三个数组top,Next,bottom分别记录栈顶栈底和栈顶下一个数字在a数组的下标数字,这样最多只用四个3e5的数组不会超内存也不回超时
code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,q;
const int maxn = 300005;
int top[maxn],Next[maxn],a[maxn],bottom[maxn];
//a数组记录所有的数,top记录每个栈的栈顶元素在a中的下标,
//Next记录每个栈栈顶的下一个元素在a中的下标,bottom记录栈底元素在a中的下标
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        int cnt = 0;//a数组的下标
        memset(top,0,sizeof(top));
        memset(Next,0,sizeof(Next));
        memset(bottom,0,sizeof(bottom));//初始化
        while(q--){
            int op;
            scanf("%d",&op);
            if(op == 1){
                int s,v;
                scanf("%d%d",&s,&v);
                a[++cnt] = v;//元素放入数组a
                if(bottom[s] == 0)//如果这个栈栈底为空,栈底等于这个元素在a中的下标
                    bottom[s] = cnt;
                Next[cnt] = top[s];//这个下标cnt下一个元素指向原本的栈顶元素在a中下标
                top[s] = cnt;//更新栈顶
            }
            if(op == 2){
                int s;
                scanf("%d",&s);
                if(top[s]){//如果栈不空
                    printf("%d\n",a[top[s]]);//输出a中这个位置的元素
                    top[s] = Next[top[s]];//指向下一个,相当于弹出栈顶
                    if(top[s] == 0) bottom[s] = 0;//如果空了,bottom也变为空
                }
                else//如果栈空输出empty
                    printf("EMPTY\n");
            }
            if(op == 3){
                int s,t;
                scanf("%d%d",&s,&t);
                if(top[t] != 0){
                    if(bottom[s] == 0) bottom[s] = bottom[t];//如果s是空的,s的底就是t的底
                    Next[bottom[t]] = top[s];//t的底部指向s的顶
                    top[s] = top[t];//s的顶变成了t的顶
                }
                bottom[t] = top[t] = 0;//t移动后清空
            }
        }
    }
    return 0;
}


### 栈(LIFO)流程图设计与顺序规则说明 #### 1. 栈(LIFO)的流程图 以下是一个使用 Mermaid 图形语言绘制的栈(LIFO)操作流程图,展示了压入和弹出的操作逻辑: ```mermaid sequenceDiagram participant User as 用户 participant Stack as 栈 Note over User,Stack: 初始状态:栈为空 [] User->>Stack: push(3) activate Stack Stack-->>User: 栈状态 [3] deactivate Stack User->>Stack: push(6) activate Stack Stack-->>User: 栈状态 [3, 6] deactivate Stack User->>Stack: pop() activate Stack Stack-->>User: 返回值 6, 栈状态 [3] deactivate Stack User->>Stack: pop() activate Stack Stack-->>User: 返回值 3, 栈状态 [] deactivate Stack ``` 此流程图清晰地展示了栈的操作过程,包括压入(`push`)和弹出(`pop`)。每次压入操作将元素添加到栈顶,而弹出操作则从栈顶移除最近压入的元素[^1]。 --- #### 2. 压入和弹出顺序的决定规则 栈的压入和弹出顺序由其“后进先出”(LIFO)特性决定。具体规则如下: - **压入顺序**:新元素始终被添加到栈顶。例如,如果栈中已有元素 `[3]`,压入 `6` 后,栈状态变为 `[3, 6]`。 - **弹出顺序**:总是从栈顶移除最近压入的元素。例如,对于栈 `[3, 6]`,弹出操作会首先返回 `6`,然后返回 `3`[^1]。 这种顺序规则确保了栈的操作具有明确的时间顺序依赖性,即最后压入的元素最先被弹出。 --- #### 3. 栈的实现代码示例 以下是一个简单的 Python 实现,展示如何通过代码实现栈的 LIFO 特性: ```python class Stack: def __init__(self): self.items = [] def push(self, item): """向栈中压入一个元素""" self.items.append(item) def pop(self): """从栈中弹出一个元素""" if not self.is_empty(): return self.items.pop() def is_empty(self): """检查栈是否为空""" return len(self.items) == 0 # 测试栈操作 stack = Stack() stack.push(3) stack.push(6) print("第一次弹出:", stack.pop()) # 输出 6 print("第二次弹出:", stack.pop()) # 输出 3 ``` 在上述代码中,`push` 方法用于将元素压入栈顶,`pop` 方法用于从栈顶移除元素,且遵循 LIFO 规则。 --- #### 4. 栈的应用场景 栈的 LIFO 特性使其广泛应用于各种场景,例如函数调用栈、表达式求值、浏览器历史记录回退等。在表达式求值中,栈可以用来处理中缀表达式转换为后缀表达式的过程,其中运算符的生效顺序与后缀表达式的生成顺序一一对应[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值