-
干货 | 手撕十大经典排序算法
-
剑指offer | 认识面试
-
剑指offer | 面试题2:实现Singleton模式
-
剑指offer | 面试题3:二维数组的查找
-
剑指offer | 面试题4:替换空格
-
剑指offer | 面试题5:从尾到头打印链表
-
剑指offer | 面试题6:重建二叉树
-
剑指offer | 面试题7:用两个栈实现队列
-
剑指offer | 面试题8:旋转数组的最小数字
-
剑指offer | 面试题9:斐波那契数列
-
剑指offer | 面试题10:青蛙跳台阶问题
-
剑指offer | 面试题11:矩阵覆盖
-
剑指offer | 面试题12:二进制中1的个数
-
剑指offer | 面试题13:数值的整数次方
-
剑指offer | 面试题14:打印从1到最大的n位数
-
剑指offer | 面试题15:删除链表的节点
-
剑指offer | 面试题16:将数组中的奇数放在偶数前
-
剑指offer | 面试题17:链表中倒数第k个节点
-
剑指offer | 面试题18:反转链表
-
剑指offer | 面试题19:合并两个有序链表
-
剑指offer | 面试题20:判断二叉树A中是否包含子树B
-
剑指offer | 面试题21:二叉树的镜像
-
剑指offer | 面试题22:顺时针打印矩阵
-
剑指offer | 面试题23:包含min函数的栈
-
剑指offer | 面试题24:栈的压入、弹出序列
-
剑指offer | 面试题25:从上到下打印二叉树
-
剑指offer | 面试题26:二叉搜索树的后序遍历序列
-
剑指offer | 面试题27:二叉树中和为某一值的路径
-
剑指offer | 面试题28:复杂链表的复制
-
剑指offer | 面试题29:二叉搜索树转换为双向链表
-
剑指offer | 面试题30:字符串的排列
-
剑指offer | 面试题31:数组中出现次数超过一半的数字
欢迎关注千羽的公众号 程序员千羽
“
Leetcode : https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/
“
GitHub : https://github.com/nateshao/leetcode/blob/main/algo-notes/src/main/java/com/nateshao/sword_offer/topic_32_getLeastNumbers/Solution.java
最小的k个数
“
题目描述 :输入整数数组
arr
,找出其中最小的k
个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。难度:简单
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
方法一:快排
本题使用排序算法解决最直观,对数组 arr
执行排序,再返回前 k 个元素即可。使用任意排序算法皆可。
快速排序原理:
快速排序算法有两个核心点,分别为 “哨兵划分” 和 “递归” 。
哨兵划分操作: 以数组某个元素(一般选取首元素)为 基准数 ,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。
“
如下图所示,为哨兵划分操作流程。通过一轮 哨兵划分 ,可将数组排序问题拆分为 两个较短数组的排序问题 (本文称之为左(右)子数组)。
递归: 对 左子数组 和 右子数组 递归执行 哨兵划分,直至子数组长度为 1 时终止递归,即可完成对整个数组的排序。
复杂度分析:
- 时间复杂度O(N logN): 库函数、快排等排序算法的平均时间复杂度为O(N log N)。
- 空间复杂度O(N) : 快速排序的递归深度最好(平均)为O(logN),最差情况(即输入数组完全倒 序)为O(N)。
package com.nateshao.sword_offer.topic_32_getLeastNumbers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;
/**
* @date Created by 邵桐杰 on 2021/12/5 19:48
* @微信公众号 程序员千羽
* @个人网站 www.nateshao.cn
* @博客 https://nateshao.gitee.io
* @GitHub https://github.com/nateshao
* @Gitee https://gitee.com/nateshao
* Description: 剑指 Offer 40. 最小的k个数
* 题目描述:输入 n 个整数,找出其中最小的 K 个数。
* 思路:先将前 K 个数放入数组,进行堆排序,若之后的数比它还小,则进行调整
*/
public class Solution {
public static void main(String[] args) {
int[] input = {4, 5, 1, 6, 2, 7, 3, 8};
int k = 4;
ArrayList<Integer> list = GetLeastNumbers_Solution(input, k);
list.stream().forEach(lists -> System.out.print(lists + " "));
System.out.println("======================");
int[] numbers = getLeastNumbers(input, k);
for (int number : numbers) {
System.out.print(number + " ");
}
}
/***
* 方法一:快排
* @param arr
* @param k
* @return
*/
public static int[] getLeastNumbers(int[] arr, int k) {
quickSort(arr, 0, arr.length - 1);
return Arrays.copyOf(arr, k);
}
private static void quickSort(int[] arr, int leftIndex, int rightIndex) {
// 子数组长度为 1 时终止递归
if (leftIndex >= rightIndex) return;
// 哨兵划分操作(以 arr[left] 作为基准数)
int left = leftIndex, right = rightIndex;
while (left < right) {
while (left < right && arr[right] >= arr[leftIndex]) right--;
while (left < right && arr[left] <= arr[leftIndex]) left++;
swap(arr, left, right);
}
swap(arr, left, leftIndex);
// 递归左(右)子数组执行哨兵划分
quickSort(arr, leftIndex, left - 1);
quickSort(arr, left + 1, rightIndex);
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
/************************** 堆 ********************************/
/**
* 方法二:
* 保持堆的大小为K,然后遍历数组中的数字,遍历的时候做如下判断:
* 1. 若目前堆的大小小于K,将当前数字放入堆中。
* 2. 否则判断当前数字与大根堆堆顶元素的大小关系,如果当前数字比大根堆堆顶还大,这个数就直接跳过;
* 反之如果当前数字比大根堆堆顶小,先poll掉堆顶,再将该数字放入堆中。
* @param arr
* @param k
* @return
*/
public int[] getLeastNumbers2(int[] arr, int k) {
if (k == 0 || arr.length == 0) return new int[0];
// 默认是小根堆,实现大根堆需要重写一下比较器。
Queue<Integer> queue = new PriorityQueue<>((v1, v2) -> v2 - v1);
for (int num: arr) {
if (queue.size() < k) {
queue.offer(num);
} else if (num < queue.peek()) {
queue.poll();
queue.offer(num);
}
}
// 返回堆中的元素
int[] res = new int[queue.size()];
int idx = 0;
for(int num: queue) {
res[idx++] = num;
}
return res;
}
/******************** 计数排序 ***********************/
public int[] getLeastNumbers3(int[] arr, int k) {
if (k == 0 || arr.length == 0) return new int[0];
// 统计每个数字出现的次数
int[] counter = new int[10001];
for (int num: arr) {
counter[num]++;
}
// 根据counter数组从头找出k个数作为返回结果
int[] res = new int[k];
int idx = 0;
for (int num = 0; num < counter.length; num++) {
while (counter[num]-- > 0 && idx < k) {
res[idx++] = num;
}
if (idx == k) break;
}
return res;
}
/************************** 剑指offer *********************/
/**
* 大根堆(前 K 小) / 小根堆(前 K 大)
* @param input
* @param k
* @return
*/
public static ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
ArrayList<Integer> list = new ArrayList<>();
if (input == null || k <= 0 || k > input.length) {
return list;
}
int[] kArray = Arrays.copyOfRange(input, 0, k);
// 创建大根堆
buildHeap(kArray);
for (int i = k; i < input.length; i++) {
if (input[i] < kArray[0]) {
kArray[0] = input[i];
maxHeap(kArray, 0);
}
}
for (int i = kArray.length - 1; i >= 0; i--) {
list.add(kArray[i]);
}
return list;
}
public static void buildHeap(int[] input) {
for (int i = input.length / 2 - 1; i >= 0; i--) {
maxHeap(input, i);
}
}
private static void maxHeap(int[] array, int i) {
int left = 2 * i + 1;
int right = left + 1;
int largest = 0;
if (left < array.length && array[left] > array[i]) largest = left;
else largest = i;
if (right < array.length && array[right] > array[largest]) largest = right;
if (largest != i) {
int temp = array[i];
array[i] = array[largest];
array[largest] = temp;
maxHeap(array, largest);
}
}
}
参考链接:
- https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/3chong-jie-fa-miao-sha-topkkuai-pai-dui-er-cha-sou/
- https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/jian-zhi-offer-40-zui-xiao-de-k-ge-shu-j-9yze/
END
革命尚未成功,同志仍需努力,冲冲冲
-
学会Spring的正确姿势!
-
万万没想到!Bean还有这么多东西
-
Spring中的AOP!
-
聊聊Spring数据库开发
-
Spring事务还能这样管理?
-
老师问我 Spring MVC 的工作流程
-
分享 | 后端必会的Spring MVC核心类和注解
-
还有人不知道?Spring MVC的数据绑定来了
-
开发必掌握!JSON数据交互和RESTful开发
-
拦截器的骚操作
-
捋一捋上传和下载
-
老师又问我MyBatis了
-
开发常用MyBatis的核心配置,你能看懂几个?
-
EDG!动态SQL!牛逼!
-
面试官:请讲一下MyBatis是如何关联关系?
-
细说Spring整合Mybatis
-
撒花 | SSM 完结
-
设计模式概述
-
面向对象设计原则
-
简单工厂模式
-
抽象工厂模式
-
建造者模式
-
原型模式
-
朋友问我单例模式是什么?
-
面试官:啥是适配器模式?
-
什么是桥接模式?你可能还不知道
-
第十篇!组合模式
-
装饰模式,不难!
-
1024,我还在啃外观模式!
-
这就是享元模式!
-
常见的代理模式
点个在看你最好看
阅读原文
声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/213338.html