【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;
}
相关推荐
落樱弥城5 分钟前
FlashAttention 完全解析:从 Roofline 诊断到 CUDA 实现
人工智能·算法
-dzk-5 分钟前
【链表】LC 25.K 个一组翻转链表
数据结构·链表
FelixZhang02814 分钟前
不懂金融或不会编程,如何开始量化?从策略搭建到回测验证
人工智能·python·算法·数学建模·均值算法·自然语言处理·金融
careathers22 分钟前
【数据结构】基于比较的排序算法 - 插入排序
java·数据结构·排序算法
MicrosoftCloud30 分钟前
数据结构 07(上)|查找:顺序、折半、分块,ASL 到底怎么
数据结构·查找·顺序查找·折半查找·分块查找·平均查找长度·asl
Mortalbreeze36 分钟前
0 - 1 BFS 算法详解:从基本原理到经典题目建模
数据结构·c++·算法·图论·宽度优先
飞Link2 小时前
什么是 PX4?一文了解 PX4 在空地协同中的作用
算法·无人机
一去不复返的通信er9 小时前
力扣-图论-岛屿数量
算法·leetcode·图论
程序 代码狂人12 小时前
切片表与分区表的区别(数据开发面试版)
java·大数据·开发语言·数据仓库·算法