剑指offer-最小的K个数

Posted pathjh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指offer-最小的K个数相关的知识,希望对你有一定的参考价值。

题目:最小的K个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
 
分析:这是一道考察排序的题,借此重新复习下常见的排序
方法一:利用堆排序
建立并维持一个只有k个元素的最大堆,后面的元素进来时先与堆顶元素进行比较,如果比堆顶元素大,则不放进去;如果比堆顶元素小,则删掉堆顶元素,将次元素入堆,对整个堆重新进行排序,当着n个数都遍历完后,此时堆中元素便是最小的k个元素
代码参考了讨论区,java中优先队列是用堆实现的
 1 import java.util.ArrayList;
 2 import java.util.Comparator;
 3 import java.util.PriorityQueue;
 4 public class Solution {
 5     public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
 6         ArrayList<Integer>result=new ArrayList<Integer>();
 7         int n=input.length;
 8         if(k>n||k==0) return result;
 9         PriorityQueue<Integer>maxHeap=new PriorityQueue<Integer>(k,new Comparator<Integer>(){
10           @Override
11             public int compare(Integer o1,Integer o2){
12                 return o2.compareTo(o1);
13             }
14         });
15         for(int i=0;i<n;i++){
16             if(maxHeap.size()!=k){
17                 maxHeap.offer(input[i]);
18             }else if(maxHeap.peek()>input[i]){
19                 Integer tmp=maxHeap.poll();
20                 tmp=null;
21                 maxHeap.offer(input[i]);
22             }
23         }
24         for(Integer integer:maxHeap){
25             result.add(integer);
26         }
27         return result;
28     }
29 }

java优先队列回顾

java中提供了PriorityQueue,PriorityQueue是基于小顶堆实现的无界优先队列,这个优先队列中的元素可以默认自然排序(实现了Comparable接口或内建类型)或者通过提供的Comparator(比较器)在队列实例化的时进行排序。

优先队列的大小是不受限制的,但在创建时可以指定初始大小。当我们向优先队列增加元素的时候,队列大小会自动增加。如果不指定初始大小,其默认的初始大小为11。

优先队列使用实例:

首先创建一个用户类Customer,它没有提供任何类型的排序。当我们用它建立优先队列时,应该为其提供一个比较器对象。

 1 public class Customer {
 2  
 3     private int id;
 4     private String name;
 5  
 6     public Customer(int i, String n){
 7         this.id=i;
 8         this.name=n;
 9     }
10  
11     public int getId() {
12         return id;
13     }
14  
15     public String getName() {
16         return name;
17     }
18  
19 }

使用Java随机数生成随机用户对象。对于自然排序,我们使用Integer对象,这也是一个封装过的Java对象。

 1 import java.util.Comparator;
 2 import java.util.PriorityQueue;
 3 import java.util.Queue;
 4 import java.util.Random;
 5  
 6 public class PriorityQueueExample {
 7  
 8     public static void main(String[] args) {
 9  
10         //优先队列自然排序示例
11         Queue<Integer> integerPriorityQueue = new PriorityQueue<>(7);
12         Random rand = new Random();
13         for(int i=0;i<7;i++){
14             integerPriorityQueue.add(new Integer(rand.nextInt(100)));
15         }
16         for(int i=0;i<7;i++){
17             Integer in = integerPriorityQueue.poll();
18             System.out.println("Processing Integer:"+in);
19         }
20  
21         //优先队列使用示例
22         Queue<Customer> customerPriorityQueue = new PriorityQueue<>(7, idComparator);
23         addDataToQueue(customerPriorityQueue);
24  
25         pollDataFromQueue(customerPriorityQueue);
26  
27     }
28  
29     //匿名Comparator实现
30     public static Comparator<Customer> idComparator = new Comparator<Customer>(){
31  
32         @Override
33         public int compare(Customer c1, Customer c2) {
34             return (int) (c1.getId() - c2.getId());
35         }
36     };
37  
38     //用于往队列增加数据的通用方法
39     private static void addDataToQueue(Queue<Customer> customerPriorityQueue) {
40         Random rand = new Random();
41         for(int i=0; i<7; i++){
42             int id = rand.nextInt(100);
43             customerPriorityQueue.add(new Customer(id, "Pankaj "+id));
44         }
45     }
46  
47     //用于从队列取数据的通用方法
48     private static void pollDataFromQueue(Queue<Customer> customerPriorityQueue) {
49         while(true){
50             Customer cust = customerPriorityQueue.poll();
51             if(cust == null) break;
52             System.out.println("Processing Customer with ID="+cust.getId());
53         }
54     }
55  
56 }

java中比较器的用法

在Java中有两个接口来支持这两个概念(Comparable和Comparator),这两个接口都有连个需要被实现的方法。分别是: 
* java.lang.Comparable: int comparaTo(Object o1) 

该方法将该对象(this)和o1进行对比,返回一个int型的值,意义如下(大小都是逻辑上的大小): 
1. positive – 该对象比o1大 
2. zero – 该对象和o1对象一样大 
3. negative – 该对象比o1对象小

*java.util.Comparator: int compare(Object o1, Object o2) 

该方法将o1和o2进行比较,返回一个int型的值,意义如下(大小都是逻辑上的大小):

      1. positive – o1 的值比 o2大
      2. zero – o1 的值和 o2 一样大
      3. negative – o1 的值比 o2小  

java.util.Collections.sort(List)java.util.Arrays.sort(Object [])这两个方法用把指定的list按照升序排列,这时list中的元素必须实现java.lang.Comparable接口.

java.util.Collections.sort(List,Comparator)java.util.Arrays.sort(Object[], Comparator)这两个方法在能够提供Comparator时对list进行排序。

 

关于比较器的返回值的正负与排序时升序还是降序的关系之前一直比较模糊,即什么时候返回1,就是升序,什么时候返回-1,也是升序,什么时候又是降序。

实质上

 jdk官方默认是升序,是基于:int compare(Object o1, Object o2) 

< return -1   //也就是说o1<o2时,表明是升序,返回-1
= return 0
> return 1

官方的源码就是基于这个写的;可以理解为硬性规定。 
也就是说,排序是由这三个参数同时决定的。

如果要降序就必须完全相反:

< return 1 
= return 0
> return -1

给个例子说明下:

需求:设计一个学生类, 属性有姓名,年龄, 成绩,并产生一个数组,要求安照成绩从高到低,如果成绩相等则由年龄由低到高排序。

实现代码:

 1 import java.util.Arrays;
 2 class Student implements Comparable<Student>{
 3     private String name;
 4     private int age;
 5     private float score;
 6     public Student(String name, int age, float score) {
 7         this.name = name;
 8         this.age = age;
 9         this.score = score;
10     }
11     public String toString() {
12         return name + " " + age + " " + score;
13     }
14     public int compareTo(Student stu) {
15         if(this.score > stu.score)  {
16             return -1;
17         } else if(this.score < stu.score) {
18             return 1;
19         } else {
20             if(this.age > stu.age) {
21                 return 1;
22             } else if(this.age < stu.age) {
23                 return -1;
24             } else {
25                 return 0;
26             }
27         }
28     }
29 }
30 
31 public class ComparableDemo {
32     public static void main(String[] args) {
33         Student students[] = {new Student("张三", 20, 90.0f), new Student("李四", 22, 90.0f),
34                 new Student("王五", 20, 99.0f), new Student("赵六", 20, 70.0f), new Student("孙七", 22, 100.0f)};
35         Arrays.sort(students);
36         for (Student stu: students) {
37             System.out.println(stu);
38         }
39     }
40 
41 }

 

方法二:有空再补上

 

 

 

 

 

 

 

 

 

 

 

参考:java比较器原理

java比较器源码分析

java比较器排序原理

timSort排序原理

以上是关于剑指offer-最小的K个数的主要内容,如果未能解决你的问题,请参考以下文章

剑指offer--40最小的k个数

剑指offer---最小的K个数

剑指 Offer 40. 最小的k个数

最小的K个数-剑指Offer

剑指offer:最小k个数

剑指 Offer 40. 最小的k个数