「小顶/大顶堆」找第k大数,找第k小丑数, 找杨氏矩阵第k小数

20 篇文章 0 订阅
1 篇文章 0 订阅

找第k小丑数:  

用小顶堆每次取顶x,判断set[]是否已有该数x,若无,把x加入set[],并把x*a,x*b,x*c 加入堆。第k进入set[]的是第k大丑数。


找第k大数:

如果所有数字可以用堆装,那么放入大顶堆里面。取到第k个就是了。

否则需要对所有数字分段计数。


给定递增的数组 a[m] 和 b[n],找第k小的a[i] + b[j]:

这等价于从一个特殊的杨氏矩阵Mat[ m ][ n ]中找第k小数, Mat[i][j] = a[i] + b[j],

杨氏矩阵 满足特征 a[i][j] <= a[i+1][j], a[i][j]<= a[i][j+1]

实现方法: 

宽搜法(bfs), 每次访问到Mat[i][j]的时候,接下来判断Mat[i][j+1] 和Mat[i+1][j]是否在小顶堆里,如果不在,将其插入到小顶堆。

时间复杂度: O(K*lg K )

#include <iostream>
#include <string.h>
#include <queue>
#include <assert.h>
#include <map>
using namespace std;
int m=1000, n=1000;
int a[1000], b[1000];

int lst[1000];
typedef pair<int, int> Pair;
struct cmp{
	bool operator ()(const Pair &l, const Pair &r){
		bool t=a[l.first]+ b[l.second] < a[r.first]+ b[r.second];
                return !t;//@error: if return t, then bigger pairs come top in priority_queue ! but we need smaller ones.
	}
}cmper;
int getK(int k){//find k-th smallest a[i]+b[j]
        assert(k <= m*n);
	memset(lst, -1, m*sizeof(int));
	/*if(m<=0 || n<=0){ // no need
		return m<=0? b[k]: a[k];
	}*/
	priority_queue<Pair, vector<Pair>, cmp> q; //small top queue

	Pair p(0, 0);
	q.push(p);
	lst[0]=0;
	int num=0;
	while(!q.empty()){
		Pair p=q.top(); q.pop();
		int i=p.first, j=p.second;
		num++;
		if(num == k) return a[i]+b[j];
		if(i+1<m && j>lst[i+1]){
			lst[i+1]=j, q.push(Pair(i+1, j));
                }
		if(j+1<n && j+1>lst[i]){
			lst[i]=j+1, q.push(Pair(i, j+1));
                }
	}
	assert(0);
	return -1;
}
int main(){
	cin>>m>>n;
	for(int i=0; i<m; i++){
		cin>>a[i];
	}
	for(int j=0; j<n; j++){
		cin>>b[j];
	}
	int k; cin>>k;
	cout<<getK(k)<<endl;
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用,给出一个组,组的第k大的的方法是基于快速排序的思路。具体步骤如下: 1. 定义一个函`quickFind`,参组`arr`、左边界`left`、右边界`right`和要的第k大的`k`。 2. 在函内部,使用快速排序的思路进行处理。首先选择一个基准值`key`,将其设为组的第一个元素`arr[left]`。 3. 使用两个指针`i`和`j`,分别指向组的左边界和右边界。 4. 在一个循环,将比基准值大的元素移到组的右边,比基准值小的元素移到组的左边。 5. 循环结束后,将基准值放到合适的位置,使得基准值的左边都是比其小的,右边都是比其大的。 6. 根据基准值的位置和k的关系,判断第k大的在基准值的左边还是右边。 - 若基准值所在的位置右边的据小于k-1个,说明第k大的在基准值的左边序列,需要对左边的据进行快排,并到第k-big-1大的据。 - 若基准值所在的位置右边的据大于k-1个,说明第k大的在基准值的右边序列,需要对右边的据进行快排并到第k大的。 - 若基准值所在的位置右边的据正好是k-1个,说明基准值就是第k大的,返回基准值。 7. 在主函调用`quickFind`函,给出组和要的第k大的。 根据引用和引用的代码实现,给定一个整组a和要的第K大的,可以使用快速排序的原理来寻第K大的。具体步骤如下: 1. 定义一个函`quickSort`,参组`arr`、起始位置`start`、结束位置`end`和要的第K大的`k`。 2. 在函内部,设置递归结束条件,若起始位置大于结束位置,则返回-1。 3. 设置两个指针`i`和`j`,分别指向起始位置和结束位置,以及一个基准值`base`,将其设为组的起始位置的元素`arr[start]`。 4. 在一个循环,将比基准值大的元素移到组的右边,比基准值小的元素移到组的左边。 5. 循环结束后,将基准值放到合适的位置,使得基准值的左边都是比其小的,右边都是比其大的。 6. 根据基准值的位置和k的关系,判断第k大的在基准值的左边还是右边。 - 若基准值的位置等于`arr.length - k`,说明到了第K大的,返回该值。 - 若基准值的位置小于`arr.length - k`,说明第K大的在基准值的右边序列,继续递归调用`quickSort`函,在右边序列第K大的。 - 若基准值的位置大于`arr.length - k`,说明第K大的在基准值的左边序列,继续递归调用`quickSort`函,在左边序列第K大的。 7. 在主函调用`quickSort`函,给出组、起始位置、结束位置和要的第K大的。 综上所述,根据以上的方法和步骤,可以组的第K大的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++——寻第k大的](https://blog.csdn.net/ldm_666/article/details/117229499)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [寻第k大的](https://blog.csdn.net/m0_37671741/article/details/108304136)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值