1057. Stack (30)

本文介绍如何实现一种特殊的数据结构——栈,该栈除了支持常规的压栈和弹栈操作外,还能高效地返回当前所有元素的中位数。通过使用树状数组,实现了快速统计和查找中位数的功能。

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.

Input Specification:

Each input file contains one test case.  For each case, the first line contains a positive integer N (<= 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.

Output Specification:

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.

Sample Input:
17
Pop
PeekMedian
Push 3
PeekMedian
Push 2
PeekMedian
Push 1
PeekMedian
Pop
Pop
Push 5
Push 4
PeekMedian
Pop
Pop
Pop
Pop
Sample Output:
Invalid
Invalid
3
2
2
1
2
4
4
5
3
Invalid

 思路:

题目除了正常的进栈和出栈操作外增加了获取中位数的操作, 获取中位数,我们有以下方法:

   (1):每次全部退栈,进行排序,太浪费时间,不可取。

   (2):题目告诉我们key不会超过10^5,我们可以想到用数组来标记,但不支持快速的统计操作。

                   因为同一个数可能出现多次。想到用一个array来记录长度,但是还是O(N)超时,后来上网查询,要用树状数组

   (3):然后将数组转为树状数组,可以快速的统计,再配上二分就OK了。

AC参考代码:

 

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
using namespace std;

const int Max_required = 100005;
int tree_array[Max_required];

inline int lowbit(int x)
{
    return x&(-x);
}

void add(int x, int value)  //更新前n项和函数
{
    while (x < Max_required)
    {
        tree_array[x] += value;
        x += lowbit(x);
    }
}

int sum(int x)      //求前n项和函数
{
    int total = 0;
    while (x > 0)
    {
        total += tree_array[x];
        x -= lowbit(x);
    }
    return total;
}

int binary_find(int x)  //二分查找代码
{
    int low = 1, high = Max_required, mid;

    while (low <= high)
    {
        mid = (low + high) >> 1;
        int total = sum(mid);

        if (total >= x)
            high = mid - 1;
        else if (total < x)
            low = mid + 1;
    }
    return low;
}

int main()
{
    int n;
    stack<int> st;
    while (scanf("%d", &n) != EOF)
    {
        memset(tree_array, 0, sizeof(tree_array));
        char ch[30];
        while (n--)
        {
            scanf("%s", ch);
            if (strcmp("Push", ch) == 0)
            {
                int pp;
                scanf("%d", &pp);
                st.push(pp);
                add(pp, 1);
            }
            else if (strcmp("Pop", ch) == 0)
            {
                if (st.empty())
                    printf("Invalid\n");
                else
                {
                    printf("%d\n", st.top());
                    add(st.top(), -1);
                    st.pop();
                }
            }
            else if (strcmp("PeekMedian", ch) == 0)
            {
                int len = st.size();
                if (len == 0) {
                    printf("Invalid\n");
                    continue;
                }
                int res = -1;
                if (len % 2 == 1)
                    res = binary_find((len + 1) / 2);
                else
                    res = binary_find(len / 2);
                printf("%d\n", res);
            }
        }
    }
    return 0;
}

 

树状数组简单介绍:

假设数组a[1..n],那么查询a[1]+...+a[n]的时间是log级别的,而且是一个在线的数据结构,支持随时修改某个元素的值,复杂度也为log级别。

来观察这个图:

 

令这棵树的结点编号为C1,C2...Cn。令每个结点的值为这棵树的值的总和,那么容易发现:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
...
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16
这里有一个有趣的性质:
设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,
所以很明显:Cn = A(n – 2^k + 1) + ... + An
算这个2^k有一个快捷的办法,定义一个函数如下即可:

 

int lowbit(int x){
return x&(-x);
}

 

当想要查询一个SUM(n )(求a[n]的和),可以依据如下算法即可:
step1: 令sum = 0,转第二步;
step2: 假如n <= 0,算法结束,返回sum值,否则sum = sum + Cn,转第三步;
step3: 令n = n – lowbit(n),转第二步。
可以看出,这个算法就是将这一个个区间的和全部加起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值