最小的K个数

 第一种方法就是快排,最重要的是partition函数会写

import java.util.ArrayList;
public class Solution {
    public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList aList = new ArrayList();
        if(input.length == 0 || k > input.length || k <= 0)
            return aList;
        int low = 0;
        int high = input.length-1;
        int index = Partition(input,k,low,high);
        while(index != k-1){
            if (index > k-1) {
                high = index-1;
                index = Partition(input,k,low,high);
            }else{
                low = index+1;
                index = Partition(input,k,low,high);
            }
        }
        for (int i = 0; i < k; i++)
            aList.add(input[i]);
        return aList;
    }
     
    int Partition(int[] input,int k,int low,int high){
        int pivotkey = input[k-1];
        swap(input,k-1,low);
        while(low < high){
            while(low < high && input[high] >= pivotkey)
                high--;
            swap(input,low,high);
            while(low < high && input[low] <= pivotkey)
                low++;
            swap(input,low,high);
        }
        return low;
    }
 
 
    private void swap(int[] input, int low, int high) {
        int temp = input[high];
        input[high] = input[low];
        input[low] = temp;
    }
}

第二种,堆排序,海量数据下使用,用优先级队列构造出最大堆,然后不断更新最大堆,每次只和堆顶比,如果比堆顶小,删除堆顶,新数入堆。但是这里利用集合并不好,手写最大堆会比这个更优,因为在超过k个数的时候,优先级队列需要poll和offer操作,poll会下沉恢复堆有序(从堆顶往下一个个比较,相当于把堆顶往下沉,然后到合适位置,堆顶下沉只会赋值一次,并不是下沉的时候比较交换),offer会上升恢复堆有序(从堆底往上一个个比较,相当于把堆底往上浮,堆底上浮只会赋值一次到合适位置,并不是上浮的时候比较交换),而如果手写堆实现的话,仅仅只需要将堆顶元素替换再下沉,就没有了上升恢复堆有序的环节。如果是100W个数找最小的5个数,假如情况比较糟糕,每次都需要更新最大堆堆顶,如果那么使用PriorityQueue将要多做999995(99W近100W)次上升恢复堆有序的操作。可以看一下PriorityQueue的源码就知道。

并且最后迭代的时候要么foreach要么iterator,本质就是iterator迭代。为什么不用for循环去list.add(queue.poll())?虽然也可以出结果,但是queue的poll方法会有下沉恢复堆有序操作,而iterator不会,仅仅是遍历数组。最后返回的ArrayList是满足要求的数字但不一定有序(因为数组堆不一定有序),返回这个ArrayList,最后判题系统应该会排序后来判断结果对不对。

 

 

链接:https://www.nowcoder.com/questionTerminal/6a296eb82cf844ca8539b57c23e6e9bf
来源:牛客网

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.PriorityQueue;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        // [4,5,1,6,2,7,3,8],0
        if (input == null || k > input.length || k <= 0)    return list;
        PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>(){
            public int compare(Integer i1, Integer i2) {
                return i2.compareTo(i1);
            }
        });
        int len = input.length;
        for (int i = 0; i < len; ++i) {
            if (queue.size() != k) {
                queue.offer(input[i]);
            } else if (queue.peek() > input[i]) {
                queue.poll();
                queue.offer(input[i]);
            }
        }
        Iterator<Integer> it = queue.iterator();
        while (it.hasNext()) {
            list.add(it.next());
        }
        return list;
    }
}

 

手写最大堆:

链接:https://www.nowcoder.com/questionTerminal/6a296eb82cf844ca8539b57c23e6e9bf
来源:牛客网

import java.util.ArrayList;
 
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        // [4,5,1,6,2,7,3,8],0
        if (input == null || k > input.length || k <= 0)
            return list;
        int[] target = new int[k];
        int len = input.length;
        for (int i = 0; i < len; ++i) {
            if (i < k) {
                target[i] = input[i];
                heapInsertSiftUp(target, i, target[i]);
            } else {
                if (target[0] > input[i]) { // 最大堆下沉
                    target[0] = input[i];
                    siftDown(target, 0, target[0]);
                    // 相比优先级队列,这里不会offer操作(里面有上浮),少了一步上浮调整,效率高了不止一丁点
                }
            }
        }
        for (int i = 0; i < k; ++i) {
            list.add(target[i]);
        }
        return list;
    }
 
    private void heapInsertSiftUp(int[] target, int index, int x) {
        while (index > 0) {
            int parent = (index - 1) >>> 1;
            if (greater(x, target[parent])) {
                target[index] = target[parent]; // 往下拉,避免直接上浮覆盖前面的值
                index = parent;
            } else {
                break;
            }
        }
        target[index] = x;
    }
 
    private boolean greater(int i, int j) {
        return i > j;
    }
 
    private void siftDown(int[] target, int k, int x) {
        int half = target.length >>> 1;
        while (k < half) {
            int child = (k << 1) + 1; // 默认先左孩子
            int big = target[child];
            int right = child + 1;
            if (right < target.length && greater(target[right], big)) {
                big = target[right];
                child = right; // 可以直接一步big = target[child = right];
            }
            if (greater(x, big)) // x比子节点中的最大值还大,已经是大顶堆了
                break; // 往上拉不动了,准备退出把最初堆顶的结点赋值到上一个结点
            target[k] = big; // 往上拉
            k = child;
        }
        target[k] = x;
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值