【PAT甲级真题】- Stack (30)

题目来源

Stack (30)

注意点

  • pop要输出值

题目描述

Stack is one of the most fundamental data structures, which is based on the principle of Last In First Out (LIFO). The basic operations include Push (inserting an element onto the top position) and Pop (deleting the top element). Now you are supposed to implement a stack with an extra operation: PeekMedian -- return the median value of all the elements in the stack. With N elements, the median value is defined to be the (N/2)-th smallest element if N is even, or ((N+1)/2)-th if N is odd.

输入描述:

Each input file contains one test case. For each case, the first line contains a positive integer N (<= 10 5 10^5 105). Then N lines follow, each contains a command in one of the following 3 formats:

Push key

Pop

PeekMedian

where key is a positive integer no more than 105.

输出描述:

For each Push command, insert key into the stack and output nothing. For each Pop or PeekMedian command, print in a line the corresponding returned value. If the command is invalid, print "Invalid" instead.

输入例子:

复制代码
17
Pop
PeekMedian
Push 3
PeekMedian
Push 2
PeekMedian
Push 1
PeekMedian
Pop
Pop
Push 5
Push 4
PeekMedian
Pop
Pop
Pop
Pop

输出例子:

复制代码
Invalid
Invalid
3
2
2
1
2
4
4
5
3
Invalid

思路简介

就是平衡搜索数 AVL 的模板

查找中位数即查找第 k 小

后续我会补上AVL的模板详解

遇到的问题

无,写完平衡树一遍过

代码

cpp 复制代码
/**
 * https://www.nowcoder.com/pat/5/problem/4110
 * multiset
 */
#include<bits/stdc++.h>
using namespace std;

// AVL 树节点结构
struct Node {
    int val;        // 节点值
    int h = 1;      // 子树高度(叶子节点高度为1)
    int sz = 1;     // 子树节点总数(包括自身)
    Node *l = nullptr;  // 左孩子
    Node *r = nullptr;  // 右孩子
    Node(int v) : val(v) {}
};

// 获取节点高度(空节点高度为0)
int h(Node* x) { return x ? x->h : 0; }

// 获取子树大小(空节点大小为0)
int sz(Node* x) { return x ? x->sz : 0; }

// 更新节点的高度和大小(必须在其孩子更新后调用)
void upd(Node* x) {
    if (!x) return;
    x->h = 1 + max(h(x->l), h(x->r));   // 高度 = 1 + 左右子树较大高度
    x->sz = 1 + sz(x->l) + sz(x->r);    // 大小 = 自身 + 左子树大小 + 右子树大小
}

// 右旋:将 y 的左孩子 x 提上来
Node* rotR(Node* y) {
    Node* x = y->l;
    y->l = x->r;    // x 的右子树变成 y 的左子树
    x->r = y;       // y 成为 x 的右孩子
    upd(y);         // 先更新 y(现在在下方)
    upd(x);         // 再更新 x(新的根)
    return x;
}

// 左旋
Node* rotL(Node* x) {
    Node* y = x->r;
    x->r = y->l;    // y 的左子树变成 x 的右子树
    y->l = x;       // x 成为 y 的左孩子
    upd(x);
    upd(y);
    return y;
}

// 计算平衡因子)
int bf(Node* x) { return h(x->l) - h(x->r); }

// 平衡操作
Node* bal(Node* x) {
    if (bf(x) > 1) {                // 左子树过高
        if (bf(x->l) < 0)           // LR 型:先左旋左孩子
            x->l = rotL(x->l);
        return rotR(x);             // LL 或 LR 最后都右旋
    }
    if (bf(x) < -1) {               // 右子树过高
        if (bf(x->r) > 0)           // RL 型:先右旋右孩子
            x->r = rotR(x->r);
        return rotL(x);             // RR 或 RL 最后都左旋
    }
    return x;   // 平衡,无需旋转
}

// 插入值为 v 的节点
Node* ins(Node* x, int v) {
    if (!x) return new Node(v);     // 空位,创建新节点
    if (v < x->val)                 // 小于当前值则递归左子树
        x->l = ins(x->l, v);
    else                            // 大于等于则递归右子树(支持重复元素放右边)
        x->r = ins(x->r, v);
    upd(x);                         // 更新高度和大小
    return bal(x);                  // 回溯时平衡
}

// 找子树最小节点(中序后继)
Node* minN(Node* x) {
    while (x->l) x = x->l;
    return x;
}

// 删除值为 v 的节点(递归)
Node* del(Node* x, int v) {
    if (!x) return nullptr;
    if (v < x->val)
        x->l = del(x->l, v);
    else if (v > x->val)
        x->r = del(x->r, v);
    else {  // 找到要删除的节点
        if (!x->l || !x->r) {           // 只有一个孩子或没有孩子
            Node* t = x->l ? x->l : x->r;
            delete x;
            return t;
        }
        // 有两个孩子:用后继节点值替换,再递归删除后继
        Node* s = minN(x->r);
        x->val = s->val;
        x->r = del(x->r, s->val);
    }
    upd(x);
    return bal(x);
}

// 查询第 k 小的元素(k 从 1 开始)
int kth(Node* x, int k) {
    int ls = sz(x->l);                  // 左子树大小
    if (k <= ls)                        // 在左子树中
        return kth(x->l, k);
    if (k == ls + 1)                    // 就是当前根
        return x->val;
    return kth(x->r, k - ls - 1);       // 在右子树中
}

// 计算中位数
int median(Node* root) {
    int n = sz(root);
    if (n == 0) return 0;               // 可自行处理空树情况
    if (n % 2 == 1)                     // 奇数个
        return kth(root, (n + 1) / 2);
    // 偶数个:取中间两个数的平均值
    return kth(root, n / 2);
}

void solve(){
    int n;cin>>n;
    stack<int>s;
    Node* root = nullptr;
    for(int i=0;i<n;++i){
        string op;
        cin>>op;
        if(op=="Push"){
            int n;
            cin>>n;
            root=ins(root,n);
            s.push(n);
        }

        if(op=="Pop"){
            if(!s.size()){
                cout<<"Invalid\n";
                continue;
            }
            int v=s.top();
            s.pop();
            root=del(root,v);
            cout<<v<<'\n';
        }

        if(op=="PeekMedian"){
            if(!s.size()){
                cout<<"Invalid\n";
                continue;
            }
            cout<<median(root)<<'\n';
        }
    }
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //fstream in("in.txt",ios::in);cin.rdbuf(in.rdbuf());
    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
相关推荐
啊嘞嘞?19 分钟前
力扣(LRU缓存)
算法·leetcode
珂朵莉MM22 分钟前
2026国信杯具身智能创新大赛-编程技能赛--本科组国赛解题报告 | 珂学家
算法·深度优先·图论
啊嘞嘞?22 分钟前
力扣(下一个排列)
算法·leetcode
李可以量化1 小时前
Redis Client 从入门到精通(一)下:redis-py 核心数据结构操作与连接管理
数据结构·redis·python·bootstrap·量化交易·qmt
疯狂打码的少年1 小时前
【数据结构】复习日:树 + 图 + 排序(整理对比表)
数据结构·笔记·算法
benchmark_cc1 小时前
Claude Code + MCP + QuantDash:打造全自动量化研究流水线的终极指南
人工智能·后端·爬虫·算法·claude·mcp·quantdash
星星.7221 小时前
2026河南萌新联赛第六场(郑州大学)补题B、D、L、J、I
数据结构·c++·算法
朝发如雪1 小时前
快速掌握Linux(2)(时间相关函数)(文件IO)
linux·python·算法
Forever Nore2 小时前
LeetCode 16 最接近的三数之和 - 双指针逼近
算法·leetcode·职场和发展
心仪久了会沦陷2 小时前
数据结构入门系列——顺序表详解:概念、分类与动态实现
c语言·数据结构·经验分享·笔记·开源