一般看到寻找最大的k个数的题目,我们第一想法便是先使用降序排序,然后取前k个即可,这种方法确实是比较常规的,一般情况下也是可行的,但是如果数据十分庞大,则会影响使得创建的数组内存溢出等等。因此,需要对相关算法思路进行优化,以下是几种不同解法。通过不同解法比较从而更理解该算法题。
1.简单排序法
从n个数中直接先排序,然后求最大的k个,从而获取最大的k个数。代码如下:
package knum;
import java.util.*;
public class GeneralSort {
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int k=sc.nextInt();
int[] data=new int[n];
for(int i=0;i<n;i++)
data[i]=sc.nextInt();
Arrays.sort(data);
for(int j=0;j<k-1;j++)
{
System.out.print(data[--n]+" ");
}
System.out.print(data[--n]);
}
}
测试结果如下:
10 3
1 5 7 4 8 20 2 15 9 10
20 15 10
以上采用的是系统自带的排序,总的求解时间复杂度为O(n*logn+k),近似于O(n*logn)。至于排序可以换成快排或者归并排序来进行实现。
2.创建最小堆实现
以上是对所有数据都进行排序,则进行排序的数据的量可能非常大,如果k的值远小于n,则会影响效率。因此,可以借鉴堆排序的方法,通过维护最小堆来进行保留k个最大值。该方法的思路是:首先先读取k个数字,对这k个数字进行创建最小堆,将最小的数放到堆顶,然后再读取第k+1个数,比较堆顶与该值,取较大值。然后重复上述的操作,留下最后k个数即为需要求的k个最大值。时间复杂度为O(nlogk)。这种方法好处在于不必担心数组内存的溢出以及时间复杂度较一般排序法也能稍作优化。实现代码如下:
package knum;
import java.util.*;
public class HeapSort {
public static void swap(int[] data,int i,int j)
{
if(i==j)
return;
data[i]=data[i]+data[j];
data[j]=data[i]-data[j];
data[i]=data[i]-data[j];
}
public static int[] heapSort(int[] data,int[] A,int k)
{
for(int i=0;i<A.length-k;i++)//控制堆大小。
{
//每次遍历创建最小堆,并且将堆顶元素与给定数组的数字按序逐个比较,若小于相应元素,则替换堆顶元素。
createMinHeap(data,k-1);//堆化
if(data[0]<A[k+i])
data[0]=A[k+i];
print(data);
}
return data;
}
public static void createMinHeap(int[] data,int lastIndex)
{
for(int i=(lastIndex-1)/2;i>=0;i--) //先找到最后一个节点的双亲节点,然后依次往回遍历,
{ //并在过程中重新访问当前节点的子节点,目的就是将最小值移到双亲位置,依次往上,直到最小值到达堆顶
//保存当前正在判断的节点
int k=i;
//若当前节点存在
while(2*k+1<=lastIndex)
{
//smallerIndex总是记录较小节点的值,先赋值为当前判断节点的左子节点
int smallerIndex=2*k+1;
if(smallerIndex<lastIndex) //判断是否存在右子节点
{
//若右子节点存在,否则此时smallerIndex应该等于lastIndex
if(data[smallerIndex]>data[smallerIndex+1])
{
//若右子节点值比左子节点值小,则smallerIndex记录的是右子节点的值
smallerIndex++;
}
}
if(data[k]>data[smallerIndex])
{
//若当前节点值比子节点最小值大,则交换两者的值,交换后将smallerIndex值赋给k,相当于将最小值往堆顶推
swap(data,k,smallerIndex);
k=smallerIndex;
}
else
break;
}
}
}
public static void print(int[] data)
{
for(int i=0;i<data.length;i++)
System.out.print(data[i]+" ");
System.out.println();
}
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int k=sc.nextInt();
int[] A=new int[n];
int[] data=new int[k];
for(int j=0;j<n;j++)
{
A[j]=sc.nextInt();
}
for(int j=0;j<k;j++)
{
data[j]=A[j];
}
int[] ans=heapSort(data,A,k);
System.out.println("堆排序后的序列:");
for(int j=0;j<ans.length;j++)
System.out.print(ans[j]+" ");
}
}
测试结果如下:
10 3
1 5 7 4 8 20 2 15 9 10
4 5 7
8 5 7
20 8 7
7 8 20
15 8 20
9 15 20
10 15 20
堆排序后的序列:
10 15 20