一.题目描述
NC566连续段的中数
一个长度为n的正整数序列a1,a2,a3......an,现在要从里面取出一段连续的长度大于等于k的序列。定义一个序列的“中数”为最大的整数x,使得序列中至少一半的数字大于等于x,求这个取出来的序列的中数最大可以是多少?
二.算法(暴力)
首先题目要求取出一段连续长度大于等于k的序列,我们可以遍历所有长度等于k的序列,然后找到每一个序列的中数,然后找到中数中的最大值。对于如何找到中数我们可以想到类似中位数的概念,在一段有序的序列中类似中位数是不是就满足这里的中数的条件呢?所以对于每段长度等于k的序列我们可以进行排序,后找出其中位数,就是其中数了,所以题目的思路就很简单了,下面是完整代码:
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-1;j<n;j++){ vector<int>res; //遍历数组中所有长度大于等于k的数组 for(int r=i;r<=j;r++){ res.push_back(a[r]); } //对数字进行排序 sort(res.begin(),res.end()); int num=(j-i+1)/2; //返回下标是num/2的数 ans=max(ans,res[num]); } } return ans; } };
时间复杂度:因为要遍历数组所有的连续长度为k的子串,并且还有将每一个子串放入vector中所以,复杂度到了了,并且还要利用sort对vector进行排序,所以最后复杂度到了
空间复杂度:需要开辟长度为k的空间来求出中数
优缺点:时间复杂度很高,但是便于理解代码实现简单
三.算法(二分)
首先我们要知道找出最大的中数这个最大的数就一定出现在数组中,在开始的时候如果将数组从小到大进行排序,那么是不是就符合二分查找的单调性了?我们先确定好边界l,r,对于每一次的中值mid进行一次check,看看是否可以实现,然后进行知道最后返回二分的答案,是不是就是最大中数了??我们理清楚了二分的思路,我们将在代码注释中详细解释check函数,结合代码更容易理解,下面是完整代码:
class Solution { public: //判断序列中至少一半的数字大于等于x bool check(vector<int>& b, int num, int k){ vector<int> ans; for(int i=0;i<b.size();i++){ if(b[i]>=num){ //注意这块是存储大于等于判断值num的下标 ans.push_back(i); } } int cnt; if(k%2){ cnt=k/2+1; } else{ cnt=k/2; } for(int i=0;i<ans.size();i++){ for(int j=i+cnt-1;j<ans.size();j++){ //判断是不是大于等于至少一半以上元素 if((ans[j]-ans[i]+1)<=(j-i+1)*2){ return true; } } } return false; } int solve(int n, int k, vector<int>& a) { vector<int> b=a; sort(b.begin(),b.end()); int l=0,r=a.size()-1; while(l<=r){ int mid=(l+r)>>1; if(check(a,b[mid],k)){ l=mid+1; } else{ r=mid-1; } } return b[l-1]; } };
时间复杂度:因为要遍历数组所有的连续长度为k的子串,并且还有将每一个子串放入vector中所以,复杂度到了了,并且还要利用二分进行查找,所以最后复杂度到了
空间复杂度:需要开辟长度为n的空间来储存初始的vector
优缺点:时间复杂度相比之下比较低,但是二分代码实现不简单,需要注意细节