《剑指offer》面试题40:最小的k个数

题目:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。


思路:

1、排序

把输入的n个整数排序,然后取前k个数;

时间复杂度:O(nlogn)

2、Partition

通过partition找到第k大的数,它的左边就是前k小的数;

时间复杂度:O(n)

3、最大堆

构建k个整数的最大堆数据结构,然后将剩余n-k个整数依次与堆顶比较,大则抛弃,小则删除堆顶并插入,最后的最大堆就是最小的k个整数;

堆是基于二叉树来实现的,因此插入和删除操作都在O(logk)时间内完成。在代码中可以通过STL中的容器来实现,如set,multiset,priority_queue等,都是基于红黑树实现的,所以是排序的。

时间复杂度:O(nlogk)

完整java代码参考如下:

package chapter5;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;

public class P209_KLeastNumbers {

    public static int partition(int[] arr,int l,int r){
        if(l>=r) return -1;

        int less=l-1;
        int more=r;
        int index=l;
        while (indexarr[r]){
                swap(arr,--more,index);
            }else {
                index++;
            }
        }
        swap(arr,more,r);
        return more;
    }

    public static void swap(int[] a,int i,int j){
        int temp=a[i];
        a[i]=a[j];
        a[j]=temp;
    }

    public static int[] findKth(int [] arr,int k){
        if(arr==null||arr.lengthk-1){
                end=index-1;
                index=partition(arr,start,end);
            }else{
                start=index+1;
                index=partition(arr,start,end);
            }
        }
        int[] res=new int[k];
        for(int i=0;ik-1){
                hi=index-1;
                index=partition(nums,lo,hi);
            }else {
                lo=index+1;
                index=partition(nums,lo,hi);
            }
        }
        return nums[k-1];
    }

    public static class MyComparator implements Comparator{
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;//将先前Integer中的自然排序(从小到大)反过来,实现从大到小;
        }
    }
    //解法二:不改变原始数组,使用优先队列,时间复杂度O(nlogk),适合海量数据
    public static int[] findKth2(int [] arr, int k) {
        if(arr == null || k > arr.length || k<=0) return null;
        //优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素)
        //元素大小的评判可以通过元素本身的自然顺序(natural ordering),也可以通过构造时传入的比较器(Comparator,类似于C++的仿函数)
        PriorityQueue maxQueue = new PriorityQueue(new MyComparator());
        //PriorityQueue的peek()和element操作是常数时间,add(), offer(), 无参数的remove()以及poll()方法的时间复杂度都是log(N)。
        for(int i =0;i

与快排相关的java实现如下:

//最重要就是partition函数的实现

public class quiksort {
    public static int partition(int[] nums,int lo,int hi){
        //将数组切分为a[lo...i-1],a[i],a[i+1...hi]
        int i=lo,j=hi+1;//左右扫面指针
        int v=nums[lo];//切分元素
        while (true){
            //扫描左右,检查扫描是否结束并交换元素
            while (less(nums[++i],v)) if(i>=hi) break;
            while (less(v,nums[--j])) if(j<=lo) break;
            if(i>=j) break;
            swap(nums,i,j);
        }
        swap(nums,lo,j);//将v=nums[j]放入正确的位置
        return j;//a[lo...j-1]<=a[j]<=a[j+1...hi]达成
    }
    public static boolean less(int a,int b){
        if(ak-1){
                hi=index-1;
                index=partition(nums,lo,hi);
            }else {
                lo=index+1;
                index=partition(nums,lo,hi);
            }
        }
        for(int i=0;ik-1){
                hi=index-1;
                index=partition(nums,lo,hi);
            }else {
                lo=index+1;
                index=partition(nums,lo,hi);
            }
        }
        return nums[k-1];
    }

    public static void sort(int[] nums){
        sort(nums,0,nums.length-1);
    }
    public static void sort(int[] nums,int lo,int hi){
        if(lo>=hi) return;
        int index=partition(nums,lo,hi);//用partition函数切分
        sort(nums,lo,index-1);//将左半部分a[lo...j-1]排序
        sort(nums,index+1,hi);//将右半部分a[j+1...hi]排序
    }

    public static void main(String[] args){
        int k=4;
        int[] n={4,5,1,6,2,7,3,8};
        int[] o=new int[k];
//        findKth(n,o,k);
//        System.out.println(findKthLargest(n,k));
        sort(n);
        for(int num:n){
            System.out.println(num);
        }
    }
}

利用java的PriorityQueue(最大堆方法)来解决,代码如下:

package Sort;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;

public class getleastnumber {
    //解法二:不改变原始数组,使用优先队列,时间复杂度O(nlogk),适合海量数据
    public static ArrayList GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList result = new ArrayList<>();
        if(input == null || k > input.length || k<=0) return result;
        //优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素)
        //元素大小的评判可以通过元素本身的自然顺序(natural ordering),也可以通过构造时传入的比较器(Comparator,类似于C++的仿函数)
        PriorityQueue maxQueue = new PriorityQueue(k,new Comparator(){//使用指定的初始容量创建一个 PriorityQueue,并根据指定的比较器对元素进行排序。
            @Override
            public int compare(Integer o1,Integer o2){
                return o2.compareTo(o1);//将先前Integer中的自然排序(从小到大)反过来,实现从大到小;
            }
        });
        //PriorityQueue的peek()和element操作是常数时间,add(), offer(), 无参数的remove()以及poll()方法的时间复杂度都是log(N)。
        for(int i =0;i input[i]){
                Integer temp = maxQueue.poll();//必须先去除队列头部的数据,以保证队列长度
                temp = null;
                maxQueue.offer(input[i]);
            }
        }
        for(Integer i : maxQueue){
            result.add(i);
        }
        return result;
    }
    public static void main(String[] args){
        ArrayList result = new ArrayList<>();
        int[] nums={4,5,1,6,2,7,3,8};
        int k=4;
        result=GetLeastNumbers_Solution(nums,k);
        for(int num:result){
            System.out.println(num);
        }
    }

}

基于排序,参考代码如下:

class Solution {
public:
    vector GetLeastNumbers_Solution(vector input, int k) {
        vector n;
        if(k > input.size()) return n;
        sort(input.begin(), input.end());
        while(k){
            n.insert(n.begin(), input[k-1]);
            k--;
        }
        return n;
    }
};

基于Partition,参考代码如下:

int Partition(int* numbers,int start,int end){
    int key=numbers[start];
    int i=start;
    int j=end;
    while(i=key)
            --j;
        if(in || n<=0 || k<=0)
        return;
    int start=0;
    int end=n-1;
    int index=Partition(input,start,end);
    while(index!=k-1){
        if(index>k-1){
            end=index-1;
            index=Partition(input,start,end);
        }
        else{
            start=index+1;
            index=Partition(input,start,end);
        }
    }
 
    for(int i=0;i

基于最大堆,参考代码如下:

typedef multiset > inSet;
typedef multiset >::iterator setIterator;
 
void GetLeastNumbers_1(const vector &data,inSet &leastNumbers,unsigned int k){
    leastNumbers.clear();
 
    if(k<1 || data.size()::const_iterator it=data.begin();
    for(;it!=data.end();it++){
        if(leastNumbers.size()

测试用例:

a.功能测试(输入的数组中有相同的数字;输入的数组中没有相同的数字)。
b.边界值测试(输入的k等于1或者等于数组的长度)。
c.特殊输入测试(k小于1;k大于数组的长度;指向数组的指针为nullptr)。

参考:

http://www.cnblogs.com/AndyJee/p/4672862.html

你可能感兴趣的:(剑指offer,笔记)