【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;
}
相关推荐
huang57914719 分钟前
哈希算法的抗碰撞机制与安全性增强策略4
算法
林浩杨_40 分钟前
SIGIR 2026|南京大学:Video-GAR:“通过生成 Query 来验证视频语义理解”的生成增强范式
论文阅读·人工智能·算法
Logic1011 小时前
C语言/数据结构位运算题解:异或XOR找出货船中的“独特载货量“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
Elsa️7461 小时前
算法一周刷题总结
c++·算法
小小程序猴11 小时前
深圳乘路资讯AI培训怎么样?课程靠谱吗?——一套课程可信度评估模型与实证分析
人工智能·算法
云上先途2 小时前
标签化服务适合哪些人?常见适用场景一次讲清
大数据·人工智能·算法·音视频
彧azz2 小时前
DFS与BFS:图遍历的两大核心算法
数据结构·学习·算法·深度优先·广度优先
宣宣猪的小花园.2 小时前
【机器学习】损失函数与梯度下降:机器如何通过“犯错”不断变好
人工智能·算法·机器学习
青山木3 小时前
Hot 100 --- 最长递增子序列
java·数据结构·算法·leetcode·动态规划
土司大王3 小时前
LeetCode hot100——33.搜索旋转排序数组:Java 二分模板与 O(log n) 实现
数据结构·算法·leetcode