106 动态中位数(对顶堆)

1. 问题描述:

依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出已读入的整数构成的序列的中位数。

输入格式

第一行输入一个整数 P,代表后面数据集的个数,接下来若干行输入各个数据集。每个数据集的第一行首先输入一个代表数据集的编号的整数。然后输入一个整数 M,代表数据集中包含数据的个数,M 一定为奇数,数据之间用空格隔开。数据集的剩余行由数据集的数据构成,每行包含 10 个数据,最后一行数据量可能少于 10 个,数据之间用空格隔开。

输出格式

对于每个数据集,第一行输出两个整数,分别代表数据集的编号以及输出中位数的个数(应为数据个数加一的二分之一),数据之间用空格隔开。数据集的剩余行由输出的中位数构成,每行包含 10 个数据,最后一行数据量可能少于 10 个,数据之间用空格隔开。输出中不应该存在空行。

数据范围

1 ≤ P ≤ 1000,
1 ≤ M ≤ 99999,
所有 M 相加之和不超过 5 × 10 ^ 5。

输入样例:


1 9 
1 2 3 4 5 6 7 8 9 
2 9 
9 8 7 6 5 4 3 2 1 
3 23 
23 41 13 22 -3 24 -31 -11 -8 -7 
3 5 103 211 -311 -45 -67 -73 -81 -99 
-33 24 56

输出样例:

1 5
1 2 3 4 5
2 5
9 8 7 6 5
3 12
23 23 22 22 13 3 5 5 3 -3 
-7 -3
来源:https://www.acwing.com/problem/content/description/108/

2. 思路分析:

这道题目可以是一道很经典的题目,可以使用很多种方法来解决,可以使用平衡树来解决但是平衡树写起来太麻烦了,其中有一种比较简单一点的方法是使用对顶堆来解决,使用一个大根堆down来存储当前输入数字中较小的一半数字,小根堆up来存储当前输入数字中较大的另外一半数字(只关注中位数是多少不关注其他元素的大小关系),并且规定大根堆的元素数量最多比小根堆的元素数量最多多1,这样当输入的元素数量为奇数的时候那么大根堆的堆顶元素就是中位数,所以问题就是如何在输入数据的时候维护这两个堆呢?这两个堆满足两个性质:① 大根堆的元素数量最多比小根堆的元素数量多1,;② 大根堆维护较小的一半元素,小根堆维护最大的一半元素;我们其实在输入当前的数字x之后判断当前的数字x与down.top()的大小关系,如果down.top() >= x那么插入到down中,否则插入到up中,这样就可以满足性质②,如何满足性质①呢?其中也比较好处理,我们可以在插入元素的时候判断down与up的元素数量的关系,如果up > down,说明需要将up中的最小的元素插入到down中,如果up + 1 < down说明需要将down中最大的元素插入到up中这样就可以在维护数量的同时满足第②个性质,在插入的时候同时维护两个堆中的两个性质。因为使用的python语言,python中只有小根堆没有大根堆但是我们可以在元素前添加一个负号使得小根堆相当于是大根堆,使用heapq模块对列表进行操作就相当于是对堆进行操作。

3. 代码如下:

python(超时):

import heapq


class Solution:
    def process(self):
        t = int(input())
        while t:
            m, n = map(int, input().split())
            # 输出对应的编号以及中位数的数量
            print(m, (1 + n) // 2)
            a = list()
            # 因为每行最多输入10个数字所以需要先计算需要输入几行, 先输入所有的数据再统一处理
            for i in range((n + 10 - 1) // 10):
                a = a + list(map(int, input().split()))
            n = len(a)
            # 下面的是大根堆, 上面的是小根堆
            down, up = list(), list()
            count = 0
            for i in range(n):
                # 插入到大根堆(添加负号相当于是大根堆)
                if not down or -down[0] >= a[i]:
                    heapq.heappush(down, -a[i])
                else:
                    # 插入到小根堆
                    heapq.heappush(up, a[i])
                # 判断满足的是哪一种情况
                if len(up) > len(down):
                    heapq.heappush(down, -heapq.heappop(up))
                if len(down) > len(up) + 1:
                    heapq.heappush(up, -heapq.heappop(down))
                # 判断是否是奇数
                if i % 2 == 0:
                    print(-down[0], end=" ")
                    count += 1
                    if count % 10 == 0: print()
            t -= 1
            if count % 10: print()


if __name__ == "__main__":
    Solution().process()

c++:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        int n, m;
        scanf("%d%d", &m, &n);
        printf("%d %d\n", m, (n + 1) / 2);

        priority_queue<int> down;
        priority_queue<int, vector<int>, greater<int>> up;

        int cnt = 0;
        for (int i = 1; i <= n; i ++ )
        {
            int x;
            scanf("%d", &x);

            if (down.empty() || x <= down.top()) down.push(x);
            else up.push(x);

            if (down.size() > up.size() + 1) up.push(down.top()), down.pop();
            if (up.size() > down.size()) down.push(up.top()), up.pop();

            if (i % 2)
            {
                printf("%d ", down.top());
                if ( ++ cnt % 10 == 0) puts("");
            }
        }
        if (cnt % 10) puts("");
    }

    return 0;
}
上一篇:杨辉三角形--2021蓝桥杯Java组


下一篇:手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏07游戏输入管理