剑指offer-51.最小的k个数(209)

51.最小的k个数(209)
  • 题目描述:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

  • 思路:

    方法一:使用快排的分区函数,只需获取第k小的元素即可获取最小的k个数。第k个位置的数以及之前的数就为最小的k个数。 此种方法会改变数组中元素的顺序。时间复杂度O(n)

    方法二:维护一个大小为k的最大堆,然后将数组中的元素和最大堆的最大值比较大小,若比最大值小,则替换并重新构造最大堆。当数组中的元素都入堆完后,堆中的元素就为最小的K个数。时间复杂度O(nlogk)

  • 代码:

    package _51.最小的k个数;
    
    import java.lang.reflect.Array;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    
    /**
     * 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
     * @author Administrator
     *
     */
    public class GetLeastNumbers {
    	/**
    	 * 方法一:会改变原数组中数字的顺序,并且获取的最小的k个数不一定是有序的。
    	 * 使用快排的分区函数求第k小的数,那么k位置之前的数就是最小的k个数,但是不能保证这些数之间是有序的。
    	 * @param input
    	 * @param k
    	 * @return
    	 */
        public static ArrayList<Integer> getLeastNumbers_Solution1(int [] input, int k) {
        	ArrayList<Integer> list = new ArrayList<>();
        	if(input == null || input.length == 0 || k <= 0) return list;
        	
            int left = 0;
            int right = input.length - 1;
            int index = paritition(input,left,right);
            while(index != (k-1)){
            	if(index > (k-1)){
            		index = paritition(input,left,index-1);
            	}
            	else{
            		index = paritition(input,index+1,right);
            	}
            }
            //循环结束后,第k-1处的数字已经确定,相应k-1以及之前的数字就是最小的k个数
            for(int i = 0; i < k; i++){
            	list.add(input[i]);
            }
            
            return list;
        }
        /**
         * 快排的分区函数
         * @param array
         * @param left
         * @param right
         * @return
         */
        public static int paritition(int[] array,int left,int right){
        	int pivot = array[left];
        	while(left < right){
        		while((left<right) && array[right] >= pivot)
        			right--;
        		array[left] = array[right];
        		while((left<right) && array[left] < pivot)
        			left++;
        		array[right] = array[left];
        	}
        	array[right] = pivot;
        	return right;
        }
        
        /**
         * 方法二:维护一个大小为k的最大堆。不会改变原数组,获取的最小k个数之间也不是有序的。
         * @param input
         * @param k
         * @return
         */
        public static ArrayList<Integer> getLeastNumbers_Solution2(int [] input, int k) {
        	ArrayList<Integer> list = new ArrayList<>();
        	if(input == null || input.length == 0 || k <= 0 || k > input.length)
        		return list;
        	
        	int[] heap = new int[k];
        	//1、向堆中添加元素
        	for(int i = 0; i < k; i++){
        		heap[i] = input[i];
        	}
        	//2、构造一个完全二叉树(将数组看成是完全二叉树的顺序存储)
        	//3、初始化最大堆
        	for(int i = heap.length/2 - 1; i >= 0; i--){//从最后一个分支结点开始构造
        		shif(heap,i,heap.length-1);
        	}
        	
        	//4.将数组中剩余元素和最大堆的最大值比较,如果比最大值小,则替换最大值并重新构造最大堆。
        	for(int i = k; i < input.length; i++){
        		if(input[i] < heap[0]){
        			heap[0] = input[i];
        			shif(heap,0,heap.length-1);
        		}
        	}
        	//5.返回结果
        	for(int i = 0; i < heap.length; i++){
        		list.add(heap[i]);
        	}
        	return list;
        }
        
        /**
         * 根结点的左子树和右子树已是大根堆,将孩子的结点的最大值与根结点比较,
         * 若根结点较小,则交换,这有可能破坏下一级的堆,继续采用上述方法构造下一级的堆。
         * @param array
         * @param low
         * @param high
         */
        public static void shif(int[] array,int low,int high){
        	//数组元素是从0开始的:根结点i;左孩子结点2*i+1;右孩子结点2*i+2;最后一个分支结点 array.length/2-1
        	int i = low;
        	int j = 2*i+1;//指向结点i的左孩子结点
        	
        	while(j <= high){
        		if(j<high && array[j] < array[j+1])
        			j++;
        		if(array[j] > array[i]){
        			array[i] = array[i] + array[j];
        			array[j] = array[i] - array[j];
        			array[i] = array[i] - array[j];
        			i = j;
            		j = 2*i+1;
        		}
        		else break;
        	}
        }
        
        
        public static void main(String[] args) {
    		int[] array ={1,3,6,5,4,2};
    		ArrayList<Integer> list = getLeastNumbers_Solution2(array,6);
    		System.out.println(list);
    	}  
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值