7-18 寻找大富翁(25 分) (堆排序)

本文介绍了一种算法实现,该算法能够从给定的人群中快速找出资产排名前几位的大富翁。通过构建最大堆的数据结构,文章详细展示了如何进行元素的交换、堆的建立与维护以及大富翁资产的输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

7-18 寻找大富翁(25 分)

2015年胡润研究院的调查显示,截至2014年9月,个人资产在600万元以上高净值人群达290万人。假设给出N个人的个人资产值,请快速找出资产排前M位的大富翁。

输入格式:

输入首先给出两个正整数N106)和M10),其中N为总人数,M为需要找出的大富翁数;接下来一行给出N个人的个人资产值,以百万元为单位,为不超过长整型范围的整数。数字间以空格分隔。

输出格式:

在一行内按非递增顺序输出资产排前M位的大富翁的个人资产值。数字间以空格分隔,但结尾不得有多余空格。

输入样例:

8 3
8 12 7 3 20 9 5 18

输出样例:

20 18 12

code:
 
#include <stdio.h>
#include <string.h>
int h[1000100];
int n,m;
void swap(int x,int y){//交换两个值
	int t;
	t = h[x];
	h[x] = h[y];
	h[y] = t;
}
//向下调整函数
void siftDown(int i){
	int t,flag = 0;//t用来暂时保存较大的节点坐标,flag判断是否继续向下调整
	while(i*2<=n&&flag==0){
		if(h[i]<h[i*2])
		   t = i*2;
		else
		   t = i;
		if(i*2+1<=n){//如果有右儿子,在进行比较
			if(h[t]<h[i*2+1]){//这里要用h[t]比较,因为t是已经比较了的较大的
				t = i*2+1;
			}
		}
		if(t!=i){//如果不相同需要交换
			swap(i,t);
			i = t;
		}
		else//如果相同则说明不用继续调整
		    flag = 1;
	}
}
//建立堆
void creat(){
	int i;
	for(i = n/2; i >= 1; i--){//从第一个非叶子节点开始一层一层向下调整
		siftDown(i);
	}
}
//删除元素
int pop(){
	int t = h[1];//保存h[1]作为返回值
	h[1] = h[n];//把最后一个元素放在顶上进行调整
	siftDown(1);
	n--;//开始忘了这句话,删除一个点后这个点的总数应该减少一个
	return t;
}
int main(){
	scanf("%d%d",&n,&m);
	int i;
	for(i = 1; i <= n; i++){
		scanf("%d",&h[i]);
	}
	creat();
	int flag = 0;
	if(m>n)m=n;//注意如果m比n大,只能输出n个
	while(m--){
		int rich;
		rich = pop();
		if(flag!=0)printf(" ");
		printf("%d",rich);
		flag = 1;
	}
	return 0;
}


<think>好的,我需要解决用户关于PTA平台“寻找大富翁”问题的Java实现或解题思路。首先,我得理解题目要求。题目要求从N个人中找出资产前M位的富翁,其中N可能很大(到10^6),而M最多是10。这种情况下,效率很重要。 根据提供的引用,特别是引用[4]和[5],提到当M较小的时候,可以采用维护一个大小为M的最小堆,或者先填满M个元素,之后每个新元素如果比堆顶大就替换,然后调整。这样可以保证时间复杂度是O(N log M),比完全排序的O(N log N)更高效,特别是当M远小于N时。 不过用户提到的是Java实现,所以需要考虑Java中的数据结构。Java的PriorityQueue默认是最小堆,但可以通过比较器改为最大堆。但在这个问题中,可能需要维护一个最小堆,以便快速替换最小的元素。例如,保持堆的大小为M,当新元素比堆顶大时,替换堆顶并调整堆。 另外,引用[3]和[5]指出输入可能很大,N到10^6,所以在读取输入时要注意效率,不能用Scanner,而应该用BufferedReader来加快读取速度。特别是处理大量数据时,输入输出效率很关键。 解题步骤大致如下: 1. 读取N和M。 2. 处理特殊情况,比如当M为0或者N为0的情况。 3. 如果N <= M,直接排序输出即可。 4. 否则,使用一个最小堆,初始填充前M个元素。 5. 遍历剩余的元素,每个元素与堆顶比较,如果更大就替换堆顶,并调整堆。 6. 最后将堆中的元素排序(降序)输出。 在Java中,可以用PriorityQueue实现最小堆。不过要注意,默认的PriorityQueue是最小堆,所以比较器需要正确设置。或者,可以自定义比较器让小的元素在堆顶,这样每次替换的时候,堆顶是最小的,方便比较。 另外,处理输入的时候需要注意,由于输入可能有多个空格隔,需要用split方法割,并且处理可能的多个空格。或者使用流式处理,逐个读取数字。 还有,题目中提到输出是前M个富翁的资产,按非递增顺序排列。所以最后需要将堆中的元素取出并逆序排序。 需要注意的是,当N小于M时,应该输出所有N个元素的降序排列,而不是补零或者其他处理。 可能的问题点包括输入处理效率、堆的正确维护,以及最后的排序步骤。此外,Java的PriorityQueue的默认行为是否符合需求,是否需要自定义比较器。 例如,创建PriorityQueue时,使用自然顺序的话是最小堆,堆顶是最小元素。这正是需要的,因为当新元素比堆顶大时,替换掉堆顶,然后重新调整堆,保持堆的大小为M。这样遍历完所有元素后,堆中保存的就是最大的M个元素。 最后,将堆中的元素取出,由于是最小堆,需要逆序排序后输出。可以用一个数组来保存结果,然后反转数组,或者使用Collections.reverse方法。 现在需要考虑代码的结构: - 读取输入,使用BufferedReader。 - 处理输入中的N和M,注意可能的错误情况。 - 读取资产数组,可能很大,所以用长整型。 - 根据N和M的大小选择处理方式。 - 使用优先队列维护前M大的元素。 - 最后处理输出。 例如,代码的大致结构可能是: import java.io.*; import java.util.*; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] firstLine = br.readLine().split(" "); int N = Integer.parseInt(firstLine[0]); int M = Integer.parseInt(firstLine[1]); if (M <= 0 || N <= 0) { System.out.println(); return; } long[] assets = new long[N]; String[] parts = br.readLine().split("\\s+"); for (int i = 0; i < N; i++) { assets[i] = Long.parseLong(parts[i]); } // 处理逻辑 if (M > N) M = N; PriorityQueue<Long> minHeap = new PriorityQueue<>(); for (int i = 0; i < M; i++) { minHeap.offer(assets[i]); } for (int i = M; i < N; i++) { if (assets[i] > minHeap.peek()) { minHeap.poll(); minHeap.offer(assets[i]); } } List<Long> result = new ArrayList<>(minHeap); Collections.sort(result, Collections.reverseOrder()); // 输出 StringBuilder sb = new StringBuilder(); for (int i = 0; i < result.size(); i++) { sb.append(result.get(i)); if (i != result.size() - 1) { sb.append(" "); } } System.out.println(sb.toString()); } } 但需要注意,当N <= M时的情况,此时应该对整个数组排序,取降序的前N个元素。例如: 如果N <= M,则直接对数组进行降序排序,输出前N个。 例如: if (N <= M) { M = N; Arrays.sort(assets); // 然后逆序 // 转换为列表然后反转 List<Long> list = new ArrayList<>(); for (long num : assets) { list.add(num); } Collections.sort(list, Collections.reverseOrder()); // 然后输出 } else { // 使用堆处理 } 或者更高效的是,直接排序数组然后取前M个元素逆序。 不过对于N非常大的情况,比如1e6,Arrays.sort可能会使用快速排序,时间复杂度是O(N log N),而如果M很小的话,堆方法更高效。但根据题目中的条件,当N <= M时,直接全排序即可。而题目中的M<=10,所以在一般情况下,当N > M时,用堆方法更高效。 另外,在输入处理中,split("\\s+")可以处理多个空格的情况,这样割后的数组不会有空字符串。 但需要注意,当输入行可能非常长时(例如1e6个数字),split方法可能效率不高,或者导致内存问题。此时,可以考虑使用StreamTokenizer或者逐个读取的方式。但Java中处理这种情况可能比较麻烦,而题目中的输入可能允许用split,因为PTA的测试用例可能不会让行超过内存限制。不过对于N=1e6,用split会导致生成一个长度为1e6的字符串数组,这在Java中可能会占用较多内存,但通常PTA的题目会考虑到这一点,允许通过。 综上,正确的Java实现应该采用优先队列维护前M个最大的元素,最后进行逆序输出。同时处理输入输出的效率问题。</think>### PTA 寻找大富翁 Java 实现思路 #### 核心思路 1. **输入处理**:使用 `BufferedReader` 高效读取大规模数据,注意处理空格隔的资产值。 2. **堆维护**:利用最小堆(`PriorityQueue`)动态维护前 $M$ 大元素: - 堆大小固定为 $M$,新元素若大于堆顶则替换并调整堆。 3. **时间复杂度优化**:时间复杂度为 $O(N \log M)$,优于完全排序的 $O(N \log N)$[^4]。 #### 代码实现 ```java import java.io.*; import java.util.*; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] firstLine = br.readLine().split(" "); int N = Integer.parseInt(firstLine[0]); int M = Integer.parseInt(firstLine[1]); if (M <= 0 || N <= 0) { System.out.println(); return; } long[] assets = Arrays.stream(br.readLine().split("\\s+")) .mapToLong(Long::parseLong) .toArray(); M = Math.min(M, N); // 修正M值 if (N <= M) { Arrays.sort(assets); reverseArray(assets); printResult(assets, M); return; } // 最小堆维护前M大元素 PriorityQueue<Long> minHeap = new PriorityQueue<>(); for (int i = 0; i < M; i++) { minHeap.offer(assets[i]); } for (int i = M; i < N; i++) { if (assets[i] > minHeap.peek()) { minHeap.poll(); minHeap.offer(assets[i]); } } // 转换为数组并降序输出 Long[] result = minHeap.toArray(new Long[0]); Arrays.sort(result, Collections.reverseOrder()); printResult(result, M); } private static void reverseArray(long[] arr) { int left = 0, right = arr.length - 1; while (left < right) { long temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; left++; right--; } } private static void printResult(Long[] arr, int M) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < M; i++) { sb.append(arr[i]); if (i != M - 1) sb.append(" "); } System.out.println(sb); } private static void printResult(long[] arr, int M) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < M; i++) { sb.append(arr[i]); if (i != M - 1) sb.append(" "); } System.out.println(sb); } } ``` #### 关键步骤说明 1. **输入修正**:若 $M > N$,修正 $M = N$ 避免越界。 2. **堆操作**:通过最小堆筛选出最大的 $M$ 个元素,堆顶始终为当前最小值。 3. **结果输出**:对堆中元素降序排序后输出,满足题目要求的非递增顺序[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值