30. 找出最小的K 个数

本文介绍两种高效查找数组中最小K个数的方法:一种是基于冒泡排序思想的简单实现,时间复杂度为O(kn),但会改变原始数组;另一种是通过构建大根堆或使用优先队列维护一个大小为K的容器,实现在O(n log k)时间内找到最小K个数,不会改变原始数组。

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

题目描述:输入n 个整数,找出其中最小的K 个数。

方法 一:要求数组中最小的k个数,最容易想到的就是利用冒泡排序的思想,每一轮排序把剩余数组中最小的一个数字放到前面已排序的后面,只要进行K轮即可。
代码实现:

    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        if(input == null)
            return null;
        ArrayList<Integer> list = new ArrayList<Integer>(k);
        if(k > input.length)
            return list;
        int temp;
        for(int i = 0; i < k; i++){
            for(int j = i + 1; j < input.length; j++){
                if(input[i] > input[j]){
                    temp = input[i];
                    input[i] = input[j];
                    input[j] = temp;
                }
            }
            list.add(input[i]);
        }
        return list;
    } 

该方法的优点是,思路清晰,易于理解,时间复杂度为O(kn)。缺点就是,时间复杂度大了点,关键是改变了原有的输入数组

 

方法二:

  • 可以先创建一个大小为k的数据容器来存储最小的k个数字,从输入的n个整数中一个一个读入放入该容器中,如果容器中的数字少于k个,按题目要求直接返回空;
  • 如果容器中已有k个数字,而数组中还有值未加入,此时就不能直接插入了,而需要替换容器中的值。按以下步骤进行插入:
    1. 先找到容器中的最大值;
    2. 将待查入值和最大值比较,如果待查入值大于容器中的最大值,则直接舍弃这个待查入值即可;如果待查入值小于容器中的最小值,则用这个待查入值替换掉容器中的最大值;
    3. 重复上述步骤,容器中最后就是整个数组的最小k个数字。
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
	ArrayList<Integer> list = new ArrayList<>();
	if (input == null || k <= 0 || k > input.length) {
		return list;
	}
	int[] kArray = Arrays.copyOfRange(input,0,k);
	// 创建大根堆
	buildHeap(kArray);
	for(int i = k; i < input.length; i++) {
		if(input[i] < kArray[0]) {
			kArray[0] = input[i];
			maxHeap(kArray, 0);
		}
	}
	for (int i = kArray.length - 1; i >= 0; i--) {
		list.add(kArray[i]);
	}
	return list;
}

public void buildHeap(int[] input) {
	for (int i = input.length/2 - 1; i >= 0; i--) {
		maxHeap(input,i);
	}
}

private void maxHeap(int[] array,int i) {
	int left=2*i+1;
	int right=left+1;
	int largest=0;
	if(left < array.length && array[left] > array[i])
		largest=left;
	else
		largest=i;
		
	if(right < array.length && array[right] > array[largest])
		largest = right;

	if(largest != i) {
		int temp = array[i];
		array[i] = array[largest];
		array[largest] = temp;
		maxHeap(array, largest);
	}
}

也可以利用现有的优先队列PriorityQueue,构造最大堆,如下:

  public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> result = new ArrayList<>();
        if (input.length <= k) {
            for (int value : input) {
                result.add(value);
            }
            return result;
        }

        PriorityQueue<Integer> queue = new PriorityQueue<>(k, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        for (int i = 0; i < input.length; i++) {
            if(i >= k) {
                Integer number = queue.peek();
                if (number > input[i]) {
                    queue.poll();
                    queue.add(input[i]);
                }
            } else {
                queue.add(input[i]);
            }
        }
        List<Integer> result2 = queue.stream().collect(Collectors.toList());
        result.addAll(result2);
        return result;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值