题解 | #连续段的中数#

连续段的中数

http://www.nowcoder.com/practice/c14a99834969462bb412cd541870b57a

题意整理:

题目给出一个正整数数组,要求求出其中的长度至少为k的连续子数组的最大中数。 一个数组的中数定义为数组中最大的满足数组中最少一半值大于等于该值的数。

对于一个已经排序完成的数组,简单的分析这个数组的中数。

对于含有奇数个数字的数组a[0:2i]a[0:2i]a[0:2i],显然a[i]为中数。对于a[i],大于等于该值的元素有i+1个,满足条件;而大于等于a[i+1]的只有i个,不满足最少一半。 对于含有偶数个数子的数组a[0:2i+1]a[0:2i+1]a[0:2i+1],其中数为a[i+1]。对于a[i+1],大于等于该数的元素有i+1个,满足条件,而对于a[i+2],大于等于该数的有i个,不满足条件。

对于上述分析,可以发现对于一个长度为len的数组a,其中数必定为a[len/2]a[len/2]a[len/2],对于奇数个元素的数组,这是数组最中间的元素;对于偶数个元素的数组,这是中间位置偏右的第一个元素。

方法一:枚举+排序

核心思想:

可以枚举每一个满足条件的区间,排序后确定中数,然后取得最大的中数即可。具体做法即枚举起点,然后从最小的满足条件的区间逐渐向后枚举到数组尾部,得到区间后对其排序并取得a[len/2]对答案进行更新。

核心代码:

class Solution {
public:
    int solve(int n, int k, vector<int>& a) {
        int ans = 0;
        for(int i = 0; i < n; ++i) {//枚举起点
            for(int j = i + k; j <= n; ++j) {//枚举终点(不包括)
                vector<int> res(a.begin() + i, a.begin() + j);//构造
                sort(res.begin(), res.end());
                int p = res[res.size() / 2];//取得中数
                ans = max(ans, p);
            }
        }
        return ans;
    }
};

复杂度分析:

时间复杂度:O(n3logn)O(n^3logn)O(n3logn)。二重循环开销为O(n2)O(n^2)O(n2),排序开销为O(nlogn)O(nlogn)O(nlogn),总开销为O(n3logn)O(n^3logn)O(n3logn)

空间复杂度:O(n)O(n)O(n),即开辟的用于排序的临时数组

方法二:枚举+双优先队列

核心思想:

方法一的时间复杂度很高,主要原因在于每次枚举区间的新终点都需要进行排序,实际上对于固定起点的区间枚举,是一个数据流中获取中位数的经典题目,可以采用双优先队列的方法,使得每个新元素加入时中位数的获取时间复杂度为O(1)而不是O(nlogn)O(nlogn)O(nlogn)

双优先队列的思想很简单,因为需要获取的只是中位数,其他数字的顺序并不重要,所以只需要保证能够准确的获取中位数即可,所以可以使用一个小顶堆和一个大顶堆,小顶堆保存较大的一半数字,大顶堆保存较小的一半,插入元素时如果不满足条件就从一个队列弹出一个元素到另一个队列。取得中位数时,根据情况在两队列的队首根据元素数量进行判断即可。 alt

核心代码:

class Solution {
public:
    priority_queue<int, vector<int>, less<int>> queMin;//存放小的一半
    priority_queue<int, vector<int>, greater<int>> queMax;//存放大的一半
    //添加数字的函数
    void addNum(int num) {
        if (queMin.empty() || num <= queMin.top()) {
            //加入较小的队列,如果队列不平衡则弹出数字到较大队列
            queMin.push(num);
            if (queMax.size() + 1 < queMin.size()) {
                queMax.push(queMin.top());
                queMin.pop();
            }
        } else {
            //加入较大的队列,如果队列不平衡则弹出数字到较小队列
            queMax.push(num);
            if (queMax.size() > queMin.size()) {
                queMin.push(queMax.top());
                queMax.pop();
            }
        }
    }
    
    int solve(int n, int k, vector<int>& a) {
        int ans = 0;
        for(int i = 0; i < n; ++i) {//枚举起点
            queMin = priority_queue<int, vector<int>, less<int>>();
            queMax = priority_queue<int, vector<int>, greater<int>>();
            for(int j = 0; j < k - 1; ++j) {
                addNum(a[i + j]);
            }
            //相当于在数据留中寻找中位数
            for(int j = i + k - 1; j < n; ++j) {
                addNum(a[j]);
                //如果较小队列长度大,那么说明有奇数个数字,中位数是较小队列首元素
                //如果两队列等长,那么说明有两个符号的数字,取得较大队列首元素(更大)
                int p= queMin.size() > queMax.size() ? queMin.top(): queMax.top();
                ans = max(ans, p);
            }
        }
        return ans;
    }
};

复杂度分析:

时间复杂度:O(n2)O(n^2)O(n2),枚举起点开销为O(n)O(n)O(n),指定起点后,其后的每个元素进入队列一次,开销为O(n)O(n)O(n),取得中位数开销为O(1)O(1)O(1),故总开销O(n2)O(n^2)O(n2)

空间复杂度:O(n)O(n)O(n),即优先队列开销

全部评论

相关推荐

07-25 10:17
仰恩大学 营销
bg双非,被挂了
投递拼多多集团-PDD等公司10个岗位
点赞 评论 收藏
分享
07-20 11:20
新疆大学 Java
Alan_01:看到都是黑马点评跟苍穹外卖我就放心了
无实习如何秋招上岸
点赞 评论 收藏
分享
07-25 11:26
清华大学 Java
打开电脑,思绪又回到了7月份刚开始的时候,感觉这个月过的如梦如幻,发生了太多事,也算是丰富了我本就是平淡的人生吧太早独立的我习惯了一切都是自己做决定,拥有绝对的决定权,而且永远不会听取别人的建议。我就是那个恋爱四年出轨的男主啦,感觉既然在牛客开了这个头,那我就要做个有始有终的人。从我出轨到结束再到和女朋友和好如初真的太像一场梦了,短短的一个月我经历了太多,也成长了很多,放下了那些本就不属于我的,找回了那些我不该放弃的。我的人生丰富且多彩,但人不能一直顺,上天总会让你的生活中出点乱子,有好有坏,让你学会一些东西,让你有成长。我和女朋友的恋爱四年太过于平淡,日常除了会制造一些小浪漫之外,我们的生活...
段哥亡命职场:不得不说,我是理解你的,你能发出来足见你是个坦诚的人,至少敢于直面自己的内心和过往的过错。 这个世界没有想象中那样非黑即白,无论是农村还是城市,在看不见的阴影里,多的是这样的事。 更多的人选择站在制高点去谩骂,一方面是社会的道德是需要制高点的,另一方面,很多人不经他人苦,却劝他人善。 大部分的我们,连自己生命的意义尚且不能明晰,道德、法律、困境,众多因果交织,人会迷失在其中,只有真的走出来之后才能看明白,可是没走出来的时候呢?谁又能保证自己能走的好,走的对呢? 可是这种问题有些人是遇不到的,不去追寻,不去探寻,也就没了这些烦恼,我总说人生的意义在过程里,没了目标也就没了过程。 限于篇幅,没法完全言明,总之,这世界是个巨大的草台班子,没什么过不去了,勇敢面对,革故鼎新才是正确,祝你早日走出来。查看图片
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务