1.根据快排中使用的分治法partition函数进行判断。
A.如果partition返回值index
B.如果partition返回值index>=k,那么在索引low -> index-1使用分治法。
C.直到index = k - 1,返回下标为k-1数字。
时间复杂度O(n),空间复杂度O(1)。但是这种方法改变了数组中数字的位置。。
public class Main {
public static void topk1(int[] input, int k) {
if(input.length == 0){
System.out.println("input.length == 0!!");
return;
}
if(k>input.length){
System.out.println("k > input.length!!");
return;
}
int index = partition(input, 0, input.length-1);
while(index != k - 1){
if(index > k-1)index = partition(input, 0, index-1);
if(index < k-1)index = partition(input, index+1, input.length-1);
}
for (int i = 0; i < k; i++) {
System.out.println(input[i]);
}
}
public static int partition(int[] input, int start ,int end) {
int pivot = input[start];
int low = start, high = end;
while(low < high){
while(lowpivot)low++;
if(low
2.使用最小堆来寻找最大的K个数。
A.我们首先需要创建一个大小为K的堆,如果我们想要空间复杂度为O(1)的话,我们可以在原数组中的前k个空间建堆。但是这样的话我们改变了原始数组的数字的位置。如果我们不想改变原始数组中的数字的位置,我们可以创建一个K个大小的容器,但是空间复杂度是O(k)。
B.然后我们对从第k+1个数到最后一个数子的每一个数字 nums[i] 都和最小堆进行比较,如果大于堆顶元素就将堆顶元素用nums[i]替换。
C.调整最小堆。
时间复杂度O(nlogk),空间复杂度O(k)(不改变数组元素位置)或O(1)(改变数组元素位置)
public class Main {
//---------------------------------最小堆-----------------------------------------
public static void topk3(int[] input, int k) {
if(input.length == 0){
System.out.println("input.length == 0!!");
return;
}
if(k>input.length){
System.out.println("k > input.length!!");
return;
}
buildMinHeap(input, k);
for (int i = k; i < input.length; i++) {
if(input[i] > input[0]){
swap(input, i, 0);
MinHeapFixedDown(input, k, 0);
}
}
for (int i = 0; i < k; i++) {
System.out.println(input[i]);
}
}
public static void buildMinHeap(int[] input, int k) {
for(int i = k/2-1;i >= 0;i--){
MinHeapFixedDown(input, k, i);
}
}
public static void MinHeapFixedDown(int[] input, int k, int i) {
int left = 2*i+1, right = left + 1;
if((left < k && input[i] > input[left]) || ((right < k && input[i] > input[right]))){
if(right < k &&input[left] > input[right]){
swap(input, i, right);
MinHeapFixedDown(input, k, right);
}else {
swap(input, i, left);
MinHeapFixedDown(input, k, left);
}
}
}
private static void swap(int[] input, int i, int j) {
int temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}
3.二分法寻找前K个数。
如果我们想既不改变数组元素位置,也不想开辟空间怎么办呢?我们可以使用二分法。
时间复杂度O(nlog(max-min)),空间复杂度O(1).
public class Main {
public static void topk3(int[] input, int k) {
//判断特殊的输入
if(input.length == 0){
System.out.println("input is null!!!");
return;
}
if(k>input.length){
System.out.println("k must be below length!!!");
return;
}
//找最大值和最小值
int low = Integer.MAX_VALUE;
int high = Integer.MIN_VALUE;
for (int i = 0; i < input.length; i++) {
if(input[i] > high)high = input[i];
if(input[i] < low)low = input[i];
}
//count用来计算从大于mid小于high的数字的个数
int count = 0;
//二分法核心代码
while(true){
int mid = (low + high)/2;
count = count(input, mid, high);
if(count > k)low = mid+1;
else if(count < k){
k = k - count;
high =mid - 1;
//mid = low;
}
else {
for (int i = 0; i < input.length; i++) {
if(input[i]>=mid)System.out.println(input[i]);
}
return;
}
}
}
//计算数组中两个数字之间数字的个数
public static int count(int[] input, int start, int end) {
int count = 0;
for(int i = 0;i < input.length;i++)
if(input[i] >= start && input[i]<= end)
count++;
return count;
}
}