LeetCode刷题记录--K大数

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 0i这个区间内存在某个位置使得从他到 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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值