文章目录
1.703. 数据流中的第 K 大元素
2.剑指 Offer II 059. 数据流的第 K 大数值
设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。
示例:
输入:
[“KthLargest”, “add”, “add”, “add”, “add”, “add”]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]
解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8
kthLargest.add(4); // return 8
提示:
1 <= k <= 104
0 <= nums.length <= 104
-104 <= nums[i] <= 104
-104 <= val <= 104
最多调用 add 方法 104 次
题目数据保证,在查找第 k 大元素时,数组中至少有 k 个元素
对于求K大数类型的题可以利用堆,栈,哈希表,排序,树状数组等。例如本题就可以利用堆来做,我们可以维护一个K个大小的堆,这个堆我们定义为小根堆,每次插入的时候先将其插入,如果插入后堆的大小大于K,我们就把堆顶弹出,这样第K大数就是堆顶了。代码实现也很简单:
class KthLargest {
priority_queue<int,vector<int>,greater<>> q;
int K;
public:
KthLargest(int k, vector<int>& nums) {
K=k;
for(auto i:nums){
add(i);
}
}
int add(int val) {
q.push(val);
if(q.size()>K){
q.pop();
}
return q.top();
}
};
下面这种方法是利用树状数组,通过对区间的快速查找和修改也能快速得到答案:
class KthLargest {
#define maxn 20002
#define base 10001
int c[maxn];
int K;
int lowbit(int x){
return x&-x;
}
void treeAdd(int num){
while(num<maxn){
++c[num];
num+=lowbit(num);
}
}
int getNum(int x){
int s=0;
while(x){
s+=c[x];
x-=lowbit(x);
}
return s;
}
int getTreeval(){
int total=getNum(maxn-1);
int l=1,r=maxn-1;
int ans=0;
while(l<=r){
int mid=l+((r-l)>>1);
int cnt=total-getNum(mid-1);
if(cnt>=K){
ans=mid;
l=mid+1;
}else r=mid-1;
}
return ans;
}
public:
KthLargest(int k, vector<int>& nums) {
memset(c,0,sizeof(c));
K=k;
for(auto &i:nums){
add(i);
}
}
int add(int val) {
treeAdd(val+base);
return getTreeval()-base;
}
};
3.1471. 数组中的 k 个最强值
给你一个整数数组 arr 和一个整数 k 。
设 m 为数组的中位数,只要满足下述两个前提之一,就可以判定 arr[i] 的值比 arr[j] 的值更强:
|arr[i] - m| > |arr[j] - m|
|arr[i] - m| == |arr[j] - m|,且 arr[i] > arr[j]
请返回由数组中最强的 k 个值组成的列表。答案可以以 任意顺序 返回。
中位数 是一个有序整数列表中处于中间位置的值。形式上,如果列表的长度为 n ,那么中位数就是该有序列表(下标从 0 开始)中位于 ((n - 1) / 2) 的元素。
例如 arr = [6, -3, 7, 2, 11],n = 5:数组排序后得到 arr = [-3, 2, 6, 7, 11] ,数组的中间位置为 m = ((5 - 1) / 2) = 2 ,中位数 arr[m] 的值为 6 。
例如 arr = [-7, 22, 17, 3],n = 4:数组排序后得到 arr = [-7, 3, 17, 22] ,数组的中间位置为 m = ((4 - 1) / 2) = 1 ,中位数 arr[m] 的值为 3 。
示例 1:
输入:arr = [1,2,3,4,5], k = 2
输出:[5,1]
解释:中位数为 3,按从强到弱顺序排序后,数组变为 [5,1,4,2,3]。最强的两个元素是 [5, 1]。[1, 5] 也是正确答案。
注意,尽管 |5 - 3| == |1 - 3| ,但是 5 比 1 更强,因为 5 > 1 。
示例 2:
输入:arr = [1,1,3,5,5], k = 2
输出:[5,5]
解释:中位数为 3, 按从强到弱顺序排序后,数组变为 [5,5,1,1,3]。最强的两个元素是 [5, 5]。
示例 3:
输入:arr = [6,7,11,7,6,8], k = 5
输出:[11,8,6,6,7]
解释:中位数为 7, 按从强到弱顺序排序后,数组变为 [11,8,6,6,7,7]。
[11,8,6,6,7] 的任何排列都是正确答案。
示例 4:
输入:arr = [6,-3,7,2,11], k = 3
输出:[-3,11,2]
示例 5:
输入:arr = [-7,22,17,3], k = 2
输出:[22,17]
提示:
1 <= arr.length <= 10^5
-10^5 <= arr[i] <= 10^5
1 <= k <= arr.length
这种就是利用排序来做了,先对数字进行排序得到按照题目要求的中位数形式(不管数组长度为偶数还说奇数,中位数都是arr[(arr.size()-1)/2]
),然后从两侧开始模拟,按照题目对最强数的要求往答案数组中加入答案,这里跳出循环有三个情况
1
)
1)
1):l>r,在这种情况下k还没有用完,再看题目中的数据范围是1<=k<=arr.length,那么在这种情况下跳出循环,k必定为1。因为我们每次移动一个指针,也就相当于我们每次剔除了数组中的一个元素,如果k还剩余,那么这时候最开始的k一定是等于数组的长度的。可以再加入一个数字。
2
)
2)
2):k==0和这两个条件同时满足,这个时候就没有什么疑问了直接返回我们的ans数组.
class Solution {
public:
vector<int> getStrongest(vector<int>& arr, int k) {
sort(arr.begin(),arr.end());
int l=0,r=arr.size()-1;
int m=arr[r/2];
vector<int> ans;
while(l<r&&k--){
if(abs(arr[l]-m)>abs(arr[r]-m)){
ans.push_back(arr[l]);
l++;
}else if(abs(arr[r]-m)>abs(arr[l]-m)){
ans.push_back(arr[r]);
r--;
}else {
ans.push_back(arr[r]);
r--;
}
}
if(k==1){
ans.push_back(arr[l]);
}
return ans;
}
};
4. 923. 三数之和的多种可能
给定一个整数数组 arr ,以及一个整数 target 作为目标值,返回满足 i < j < k 且 arr[i] + arr[j] + arr[k] == target 的元组 i, j, k 的数量。
由于结果会非常大,请返回1e9 + 7
的模。
示例 1:
输入:arr = [1,1,2,2,3,3,4,4,5,5], target = 8
输出:20
解释:
按值枚举(arr[i], arr[j], arr[k]):
(1, 2, 5) 出现 8 次;
(1, 3, 4) 出现 8 次;
(2, 2, 4) 出现 2 次;
(2, 3, 3) 出现 2 次。
示例 2:
输入:arr = [1,1,2,2,2,2], target = 5
输出:12
解释:
arr[i] = 1, arr[j] = arr[k] = 2 出现 12 次:
我们从 [1,1] 中选择一个 1,有 2 种情况,
从 [2,2,2,2] 中选出两个 2,有 6 种情况。
提示:
3 <= arr.length <= 3000
0 <= arr[i] <= 100
0 <= target <= 300
对于这种类型的题目,哈希表是比较简单的一种方法
因为只需要我们求出方案数目,所以这里先对数组进行排序,然后在哈希表中加入两数之和,再去找满足条件的第三个数字的个数.
class Solution {
#define mod 1000000007
public:
int threeSumMulti(vector<int>& arr, int target) {
sort(arr.begin(),arr.end());
int n=arr.size();
int hash[305];
int ans=0;
for(int i=1;i<n-1;++i){//固定中间的数字
memset(hash,0,sizeof(hash));//每次查找先清空哈希表
for(int j=0;j<i;++j){
++hash[arr[i]+arr[j]];//hash中加入两数之和
}
for(int k=i+1;k<n;++k){
if(target-arr[k]>=0){//为了不越界
ans=(ans+hash[target-arr[k]])%mod;
}
}
}
return ans;
}
};
5.和为目标值且不重叠的非空子数组的最大数目
给你一个数组 nums 和一个整数 target 。
请你返回 非空不重叠 子数组的最大数目,且每个子数组中数字和都为 target
示例 1:
输入:nums = [1,1,1,1,1], target = 2
输出:2
解释:总共有 2 个不重叠子数组(加粗数字表示) [1,1,1,1,1] ,它们的和为目标值 2 。
示例 2:
输入:nums = [-1,3,5,1,4,2,-9], target = 6
输出:2
解释:总共有 3 个子数组和为 6 。
([5,1], [4,2], [3,5,1,4,2,-9]) 但只有前 2 个是不重叠的。
示例 3:
输入:nums = [-2,6,6,3,5,4,1,2,8], target = 10
输出:3
示例 4:
输入:nums = [0,0,0], target = 0
输出:3
提示:
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
0 <= target <= 10^6
先看代码:
class Solution {
unordered_map<int,int> hash;
public:
int maxNonOverlapping(vector<int>& nums, int target) {
int ans=0;
int n=nums.size();
for(int i=1;i<nums.size();++i){
nums[i]+=nums[i-1];
}
hash.clear();
hash[0]=1;
for(int i=0;i<n;++i){
if(hash.find(nums[i]-target)!=hash.end()){
ans++;
hash.clear();
}
hash[nums[i]]=1;
}
return ans;
}
};
看过代码之和可能你会有点懵,下面就详细解释一下:
这道题目首先要求我们要按照子数组的和来确定是否增加答案,那么排序就没法用了。于是想到了前缀和,那么前缀和的思路是什么?首先对于i
位置的元素经过我们的前缀和处理之后其存储的就是是从下标为0到下标为
i
i
i的元素之和,然后开始查找。首先先把hash[0]
标记为1,这是为了防止某个区间和正好为target。之后对于i
位置的元素,如果在哈希表中找到了
n
u
m
s
[
i
]
−
t
a
r
g
e
t
nums[i]-target
nums[i]−target,也就是说在
0
→
i
0 \to i
0→i这个区间内存在某个位置使得从他到
i
i
i这个位置的和为target,那么我们就对答案++.然后清空哈希表,这一步是为了使得区间不重叠。之后无论是否找到了符合条件的区间,都在哈希表中对当前区间进行标记。
例如这个例子:【5,1】,【4,2】,【3,5,1,4,2,-9】都符合条件,现在想象一下我们模拟的过程,当模拟到1时,【5,1】这个区间的和是等于target的,于是答案++,清空之前的哈希表并标记上当前的和,当模拟到2时,又是一种合法状态。但是对于-9,从0到这个位置的区间也是一种合法状态,但是在哈希表中并不能找到相应的映射。
这道题确实不错,很值得反复揣摩。
6.1668. 最大重复子字符串
给你一个字符串 sequence ,如果字符串 word 连续重复 k 次形成的字符串是 sequence 的一个子字符串,那么单词 word 的 重复值为 k 。单词 word 的 最大重复值 是单词 word 在 sequence 中最大的重复值。如果 word 不是 sequence 的子串,那么重复值 k 为 0 。
给你一个字符串 sequence 和 word ,请你返回 最大重复值 k 。
示例 1:
输入:sequence = “ababc”, word = “ab”
输出:2
解释:“abab” 是 “ababc” 的子字符串。
示例 2:
输入:sequence = “ababc”, word = “ba”
输出:1
解释:“ba” 是 “ababc” 的子字符串,但 “baba” 不是 “ababc” 的子字符串。
示例 3:
输入:sequence = “ababc”, word = “ac”
输出:0
解释:“ac” 不是 “ababc” 的子字符串。
提示:
1 <= sequence.length <= 100
1 <= word.length <= 100
sequence 和 word 都只包含小写英文字母。
这道题就十分简单了,简单的模拟实现 s t r s t r strstr strstr即可( k m p kmp kmp当然也可以)。
class Solution {
public:
bool match(string &sequence,string& word,int pos,int n,int m){
for(int i=pos,j=0;j<m;++i,++j){
if(sequence[i]!=word[j]){
return false;
}
}
return true;
}
int maxRepeating(string sequence, string word) {
int n=sequence.size();
int m=word.size();
int ans=0;
for(int i=0;i<n;++i){
int cur=0,pos=i;
while(1){
if(match(sequence,word,pos,n,m)){
pos+=m;
cur++;
}else break;
}
ans=max(cur,ans);
}
return ans;
}
};
7.剑指 Offer 40. 最小的k个数
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例 1
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
水题没啥好说的。。。。。。。
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
sort(arr.begin(),arr.end());
while(arr.size()>k){
arr.pop_back();
}
return arr;
}
};
8.347. 前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 10^5
k 的取值范围是 [1, 数组中不相同的元素的个数]
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
统计频率的话哈希表较为简单,然后按照频率对nums数组进行降序排序,再选取k个不重复的数字即可。
class Solution {
unordered_map<int,int> hash;
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
hash.clear();
for(auto &i:nums){
++hash[i];
}
sort(nums.begin(),nums.end(),[&](const auto& a,const auto &b){
int cnta=hash[a];
int cntb=hash[b];
if(cnta==cntb){
return a<b;//随便谁先都可以这里主要是方便返回
}else return cnta>cntb;//按照出现频率降序排序
});
vector<int> ans;
ans.push_back(nums[0]);
--k;
for(int i=1;i<nums.size()&&k;++i){
if(nums[i]==nums[i-1]){
continue;
}
--k;
ans.push_back(nums[i]);
}
return ans;
}
};