11.给定一个一维数组,找出其中最小的前K个数,要求算法的时间复杂度为O(nlogK)
solution:
借鉴堆排序的算法,通过使用最小堆选出前K个最小的数。
首先创建size=K的最小堆;堆顶为堆中最大的元素。
然后遍历数组元素,往最小堆中插入元素,如果堆的个数已经达到K。那么新插入的元素需要和堆顶元素比较。如果这个元素小于
堆顶元素,将堆顶元素移除,插入新的元素。
最后当遍历结束时可以选出最小的K个元素。由于堆是用顺序二叉树实现的,K个元素的小根堆的建立的算法时间复杂度是K,每次比
的时间复杂度是logK,总共不超过n次比较,所以算法的时间复杂度T(n)=n+nlogK=nlogK。算法的代码实现如下:
package answer;
import java.lang.reflect.Array;
public class question11 {
public void swim(int[] array, int k) { //由下至上调整二叉堆,将堆有序化
while (k > 1 && array[k / 2] < array[k]) {
int tmp = array[k / 2];
array[k / 2] = array[k];
array[k] = tmp;
k /= 2;
}
}
public static void sink(int[] array, int k, int n) {// 由上至下调整堆,使二叉堆有序化
while (2 * k < n) {
int j = 2 * k;
if (2 * k < n && array[2 * k + 1] < array[2 * k])
j++;
if (array[j] < array[k])
break;
else {
int tmp = array[k];
array[k] = array[j];
array[j] = tmp;
}
k = j;
}
}
public static void sort(int[] array) {// 堆排序的实现。所谓堆排序是借助二叉堆这种数据结构的一种排序,二叉堆是优先队列的一种实现
if (array == null)// || array.length<K) {
return;
else {
// int[] topKElements = new int[K];
for (int i = array.length / 2; i >= 1; i--) {// 构造小顶堆。从最大的非叶子结点开始
// ,由下至上的进行堆的有序化
sink(array, i, array.length);
}
int n = array.length;
while (n > 1) {// 通过构造小顶堆,堆顶的元素一定是最小的元素。
int tmp = array[1];// 每次将堆顶的元素和最后一个元素(index最大的待排序元素)进行交换
array[1] = array[n];
array[n] = tmp;
n--;
sink(array, 1, n);// 对剩下的待排序元素中被换到堆顶的元素进行由上至下的堆有序化
}
}
}
public static void getTopK(int[] array1, int K) {
if (array1 == null || array1.length < K)
return;
sort(array1);
for (int i = 1; i <= K; i++) {
System.out.print(array1[i] + " ");
}
return;
}
}
12.给定一个一维数组,找出其中最大的前K个数,要求算法的时间复杂度为O(n)
solution:
如果题目要求的算法的时间复杂度为O(nlogK),那么和第11题基本相同,不过是将小根堆换成大根堆就行了。
而本题要求的时间复杂度是线性时间复杂度,那么则需要使用快排中的分区算法(partition)
在分区算法中,选择第一个元素作为枢轴元素pivot。pivot将全部元素分成2个部分,把比pivot大的元素放在左边,而把比枢轴元素
小的放在右边,算法返回枢轴元素所在的index
介绍完partiton算法,对于本题的算法则是在partition算法的基础上递归找到下标为K-1的元素。比partition算法返回的下标小的
元素都在比这个pivot大,那么下标为0到K-1的共K个元素就是最大的前K个元素。所以本题的算法就是不断进行partition,目的是找
到下标为K-1的pivot。算法的代码实现如下:
public class question12 {
void exchange(int elem1, int elem2) { //交换函数
int tmp = elem1;
elem1 = elem2;
elem2 = tmp;
}
int partition(int[] array, int startPoint, int endPoint) {//使用快速排序的切分函数
if (array == null || startPoint < 0 || endPoint > array.length) {
return 0;
} else {
int index = 0;
int pivot = array[startPoint];
int i = startPoint, j = endPoint;
while (true) {// 找到需要交换的元素array[i],array[j]
while (array[++i] > pivot)
if (i == endPoint)
break;
while (array[--j] < pivot)
if (j == startPoint)
break;
if (i >= j)
break;
exchange(array[i], array[j]);// array[i],array[j]进行交换
}
exchange(pivot, array[j]);// 交换pivot和array[j]元素,并返回j的下标
index = j;
return index;
}
}
void getTopK(int [] array, int K){//获得前K个最小的元素
int start=0,end=array.length-1;
int index=partition(array,start,end);
while(index!=K-1){
if(index<K-1){
end=index-1;
index=partition(array,start,end);
}else{
start=index+1;
index=partition(array,start,end);
}
}
for(int i=0;i<K;i++){//输出前K个最小值
System.out.println(array[i]+" ");
}
}
}
solution:
借鉴堆排序的算法,通过使用最小堆选出前K个最小的数。
首先创建size=K的最小堆;堆顶为堆中最大的元素。
然后遍历数组元素,往最小堆中插入元素,如果堆的个数已经达到K。那么新插入的元素需要和堆顶元素比较。如果这个元素小于
堆顶元素,将堆顶元素移除,插入新的元素。
最后当遍历结束时可以选出最小的K个元素。由于堆是用顺序二叉树实现的,K个元素的小根堆的建立的算法时间复杂度是K,每次比
的时间复杂度是logK,总共不超过n次比较,所以算法的时间复杂度T(n)=n+nlogK=nlogK。算法的代码实现如下:
package answer;
import java.lang.reflect.Array;
public class question11 {
public void swim(int[] array, int k) { //由下至上调整二叉堆,将堆有序化
while (k > 1 && array[k / 2] < array[k]) {
int tmp = array[k / 2];
array[k / 2] = array[k];
array[k] = tmp;
k /= 2;
}
}
public static void sink(int[] array, int k, int n) {// 由上至下调整堆,使二叉堆有序化
while (2 * k < n) {
int j = 2 * k;
if (2 * k < n && array[2 * k + 1] < array[2 * k])
j++;
if (array[j] < array[k])
break;
else {
int tmp = array[k];
array[k] = array[j];
array[j] = tmp;
}
k = j;
}
}
public static void sort(int[] array) {// 堆排序的实现。所谓堆排序是借助二叉堆这种数据结构的一种排序,二叉堆是优先队列的一种实现
if (array == null)// || array.length<K) {
return;
else {
// int[] topKElements = new int[K];
for (int i = array.length / 2; i >= 1; i--) {// 构造小顶堆。从最大的非叶子结点开始
// ,由下至上的进行堆的有序化
sink(array, i, array.length);
}
int n = array.length;
while (n > 1) {// 通过构造小顶堆,堆顶的元素一定是最小的元素。
int tmp = array[1];// 每次将堆顶的元素和最后一个元素(index最大的待排序元素)进行交换
array[1] = array[n];
array[n] = tmp;
n--;
sink(array, 1, n);// 对剩下的待排序元素中被换到堆顶的元素进行由上至下的堆有序化
}
}
}
public static void getTopK(int[] array1, int K) {
if (array1 == null || array1.length < K)
return;
sort(array1);
for (int i = 1; i <= K; i++) {
System.out.print(array1[i] + " ");
}
return;
}
}
12.给定一个一维数组,找出其中最大的前K个数,要求算法的时间复杂度为O(n)
solution:
如果题目要求的算法的时间复杂度为O(nlogK),那么和第11题基本相同,不过是将小根堆换成大根堆就行了。
而本题要求的时间复杂度是线性时间复杂度,那么则需要使用快排中的分区算法(partition)
在分区算法中,选择第一个元素作为枢轴元素pivot。pivot将全部元素分成2个部分,把比pivot大的元素放在左边,而把比枢轴元素
小的放在右边,算法返回枢轴元素所在的index
介绍完partiton算法,对于本题的算法则是在partition算法的基础上递归找到下标为K-1的元素。比partition算法返回的下标小的
元素都在比这个pivot大,那么下标为0到K-1的共K个元素就是最大的前K个元素。所以本题的算法就是不断进行partition,目的是找
到下标为K-1的pivot。算法的代码实现如下:
public class question12 {
void exchange(int elem1, int elem2) { //交换函数
int tmp = elem1;
elem1 = elem2;
elem2 = tmp;
}
int partition(int[] array, int startPoint, int endPoint) {//使用快速排序的切分函数
if (array == null || startPoint < 0 || endPoint > array.length) {
return 0;
} else {
int index = 0;
int pivot = array[startPoint];
int i = startPoint, j = endPoint;
while (true) {// 找到需要交换的元素array[i],array[j]
while (array[++i] > pivot)
if (i == endPoint)
break;
while (array[--j] < pivot)
if (j == startPoint)
break;
if (i >= j)
break;
exchange(array[i], array[j]);// array[i],array[j]进行交换
}
exchange(pivot, array[j]);// 交换pivot和array[j]元素,并返回j的下标
index = j;
return index;
}
}
void getTopK(int [] array, int K){//获得前K个最小的元素
int start=0,end=array.length-1;
int index=partition(array,start,end);
while(index!=K-1){
if(index<K-1){
end=index-1;
index=partition(array,start,end);
}else{
start=index+1;
index=partition(array,start,end);
}
}
for(int i=0;i<K;i++){//输出前K个最小值
System.out.println(array[i]+" ");
}
}
}