Java实现TopK问题的方法

作者:早就戒了 时间:2023-11-10 20:32:14 

面试中会经常遇到手撕代码的情况,而求TopK的是经常遇到的题目。下面我就用Java来实现。主要通过两种方法实现,快排思想以及堆排序的思想,两者的复杂度为O(NlogK)。

基于快排的TopK实现:


import java.util.Arrays;

/**
* 使用快排实现的TopK问题 Title: Description: Company:
*
* @author 郑伟
* @date 2018年4月10日下午9:28:15
*/
public class TopK_PartitionSort {

public static void main(String[] args) {

int[] num = { 2, 20, 3, 7, 9, 1, 17, 18, 0, 4 };
   partitionSort(num, 0, num.length - 1, 3);
   System.out.println(Arrays.toString(num));
 }

public static void partitionSort(int[] nums, int low, int high, int K) {
   if (low < high) {
     int pointKey = partitionSortCore(nums, low, high);
     if (K - 1 == pointKey)//TopK问题的核心,就是如果返回的下标为K-1,说明已经排序好了K个最大/最小的数,但是之间的顺序是不确定的
       return;
     partitionSort(nums, low, pointKey - 1, K);
     partitionSort(nums, pointKey + 1, high, K);
   }
 }

/**
  * 快排的核心
  *
  * @param nums
  * @param low
  * @param high
  * @return 返回排序好以后的位置
  */
 public static int partitionSortCore(int[] nums, int low, int high) {
   // 以第一个座位标志位来比对
   int pivotkey = nums[low];
   while (low < high) {
     // 从pivotkey往最后一个位置比较
     while (low < high && pivotkey <= nums[high]) {
       --high;
     }
     // 开始交换pivotkey和nums[high]
     int temp = nums[low];
     nums[low] = nums[high];
     nums[high] = temp;
     // 此时nums[high]对应于pivotkey
     while (low < high && pivotkey >= nums[low]) {
       ++low;
     }
     // 找到比pivotkey大的书了,那就交换
     temp = nums[low];
     nums[low] = nums[high];
     nums[high] = temp;
     // 这时,pivotkey对应于nums[low]
   }
   return low;// 返回pivotkey对应的正确位置
 }

}

其实整个代码和快排一样,就是多了一个下标位置的判断,if (K - 1 == pointKey),这是核心,也就是为什么复杂度为NlogK。如果看不懂,可以先去理解快排的实现。

堆排序实现TopK:


/**
* 使用堆排序实现的TopK问题 Title: Description: Company:
*
* @author 郑伟
* @date 2018年4月11日上午9:28:15
*/
public class TopK_HeapSort {

public static void main(String[] args) {
   int[] num = { 2, 20, 3, 7, 9, 1, 17, 18, 0, 4 };
   heapSort(num,3);
   System.out.println(Arrays.toString(num));
 }

/**
  * 堆排序
  *
  * @param num
  */
 private static void heapSort(int[] num, int K) {
   for (int i = num.length / 2 - 1; i >= 0; i--) {
     adjustMin(num, i, num.length);// 调整0~num.length-1的数据
   }
   // 如果要实现topK,就在这里执行
   for (int j = num.length - 1; j >= 0 && K > 0; j--,K--) {
     // 交换最后一个
     swap(num, 0, j);
     // 再次调整0~j-1的数据
     adjustMin(num, 0, j);
   }
   //使用最大堆,K=3,输出[9, 7, 3, 2, 4, 1, 0, 17, 18, 20],最大的三个值17,18,20
   //使用最小堆,K=3,输出[3, 4, 9, 7, 20, 18, 17, 2, 1, 0],最小的三个值2,1,0
 }

/**
  * 交换栈顶和最后一个元素
  *
  * @param num
  * @param i
  * @param j
  */
 private static void swap(int[] num, int i, int j) {
   int tem = num[i];
   num[i] = num[j];
   num[j] = tem;
 }

/**
  * 调整为大顶堆
  *
  * @param num
  * @param root_index
  */
 private static void adjust(int[] num, int root_index, int length) {
   //
   int root = num[root_index];
   for (int j = root_index * 2 + 1; j < length; j = j * 2 + 1) {
     // 最大的儿子
     if (j + 1 < length && num[j] < num[j + 1]) {
       j = j + 1;// 指向了最大的儿子
     }
     if (root < num[j]) {
       num[root_index] = num[j];
       root_index = j;// 标记换了哪一个位置
     } else {
       break;// 已经是大顶堆了,不需要调整了
     }
   }
   num[root_index] = root;
 }

/**
  * 小顶堆
  *
  * @param num
  * @param root_index
  * @param length
  */
 private static void adjustMin(int[] num, int root_index, int length) {
   //
   int rootValue = num[root_index];
   for (int k = root_index * 2 + 1; k < length; k = k * 2 + 1) {
     if (k + 1 < length && num[k] > num[k + 1])
       k = k + 1;// K指向最小的子节点
     if (num[k] < rootValue) {
       num[root_index] = num[k];
       root_index = k;// 和k换了一下位置
     } else {
       break;// 本身不需要再调整了
     }
   }
   num[root_index] = rootValue;
 }
}

算法核心思想:与一般的堆排序不同的是,TopK只需要堆尾与堆顶交换K次就好,不需要全部交换一遍。

来源:https://blog.csdn.net/qq_37169817/article/details/79893200

标签:Java,TopK
0
投稿

猜你喜欢

  • Java HtmlEmail 邮件发送的简单实现代码

    2023-04-14 21:29:25
  • 解决Android Studio突然不显示logcat日志的问题

    2021-06-09 01:13:13
  • C#操作txt文件,进行清空添加操作的小例子

    2023-05-24 14:06:07
  • Flutter模仿实现微信底部导航栏流程详解

    2023-06-21 11:46:12
  • 详解Spring-boot中读取config配置文件的两种方式

    2021-07-04 15:52:55
  • Java中五种不同方法的创建对象

    2021-07-25 01:11:37
  • 谈谈你可能并不了解的java枚举

    2023-11-09 21:08:55
  • JavaCV实现照片马赛克效果

    2023-04-27 15:55:14
  • java http token请求代码实例

    2022-09-28 18:23:19
  • 从java源码分析线程池(池化技术)的实现原理

    2021-12-24 00:01:24
  • 解析C#设计模式之单例模式

    2021-12-15 17:41:12
  • SpringBoot Mybatis Plus公共字段自动填充功能

    2022-09-01 12:22:33
  • java中超过long范围的超大整数相加算法详解(面试高频)

    2022-09-15 11:22:05
  • 关于Jedis的用法以及Jedis使用Redis事务

    2023-06-28 07:22:56
  • Spring整合Junit的使用详解

    2022-11-20 18:33:17
  • 浅谈java 增强型的for循环 for each

    2023-03-30 18:51:05
  • java调用淘宝api联网查询ip归属地

    2022-06-11 12:21:20
  • Android中的深度链接技术实战

    2023-09-20 11:27:18
  • 通过Html网页调用本地安卓(android)app程序代码

    2022-05-10 10:14:34
  • SpringBoot读取资源目录中JSON文件的方法实例

    2023-04-26 02:00:42
  • asp之家 软件编程 m.aspxhome.com