剑指offer 第 17 天
第 17 天
排序(中等)
剑指 Offer 40. 最小的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]
限制:
0 <= k <= arr.length <= 100000 <= arr[i] <= 10000
解题思路:直接排序、堆、二叉排序树、快速查找
直接排序:直接调用sort方法排序,输入前k个即可。显然不算一个好方法
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] res = new int[k];
Arrays.sort(arr);
if (k >= 0) {
System.arraycopy(arr, 0, res, 0, k);
}
return res;
}
}
复杂度:时间 O(nlogn) 空间 O(logn)
堆:利用大根堆结构的性质,Java集合框架中已有 PriorityQueue 结构,建一个容量为 k 的堆,先添加 k 个数,然后每次弹出队列中最大的数,堆中保留的就是前 k 小的数
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] res = new int[k];
if (k == 0) {
return res;
}
PriorityQueue<Integer> queue = new PriorityQueue<>((v1, v2) -> v2 - v1);
for (int i = 0; i < k; i ++) {
queue.add(arr[i]);
}
for (int i = k; i < arr.length; i ++) {
if (queue.peek() > arr[i]) {
queue.poll();
queue.add(arr[i]);
}
}
for (int i = 0; i < k; i ++) {
res[i] = queue.poll();
}
return res;
}
}
复杂度:时间 O(nlogk) 空间 O(k)
二叉排序树:利用 TreeMap 结构中元素的有序性,TreeMap的key 是数字,value 是该数字的个数。遍历数组中的数字,维护一个数字总个数为 K 的 TreeMap,而后与大根堆操作类似,先添加 K 个元素,然后每次比较新来数字与 TreeMap 中最大数字的大小,保证 TreeMap 中是最小的 K 个数
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] res = new int[k];
if (k == 0) {
return res;
}
TreeMap<Integer, Integer> treeMap = new TreeMap<>();
for (int i = 0; i < k; i ++) {
treeMap.put(arr[i], treeMap.getOrDefault(arr[i], 0) + 1);
}
for (int i = k; i < arr.length; i ++) {
// k个中最大的元素及它的个数
Map.Entry<Integer, Integer> entry = treeMap.lastEntry();
if (entry.getKey() > arr[i]) {
treeMap.put(arr[i], treeMap.getOrDefault(arr[i], 0) + 1);
if (entry.getValue() == 1) {
treeMap.pollLastEntry();
}
else {
treeMap.put(entry.getKey(), entry.getValue() - 1);
}
}
}
int idx = 0;
for (Map.Entry<Integer, Integer> entry: treeMap.entrySet()) {
int temp = entry.getValue();
while (temp -- > 0) {
res[idx ++] = entry.getKey();
}
}
return res;
}
}
复杂度:时间 O(nlogk) 空间 O(k)
快速查找:直接通过快排思想切分排好第 K 小的数,那么它左边的数就是比它小的另外 K-1 个数,套用快排模板完成查找
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
// 最后一个参数表示我们要找的是下标为k-1的数
return quickSearch(arr, 0, arr.length - 1, k - 1);
}
private int[] quickSearch(int[] nums, int lo, int hi, int k) {
// 每快排切分1次,找到排序后下标为j的元素,如果j恰好等于k就返回j以及j左边所有的数;
int j = partition(nums, lo, hi);
if (j == k) {
return Arrays.copyOf(nums, j + 1);
}
// 否则根据下标j与k的大小关系来决定继续切分左段还是右段。
return j > k? quickSearch(nums, lo, j - 1, k): quickSearch(nums, j + 1, hi, k);
}
// 快排切分,返回下标j,使得比nums[j]小的数都在j的左边,比nums[j]大的数都在j的右边。
private int partition(int[] nums, int lo, int hi) {
int v = nums[lo];
int i = lo, j = hi + 1;
while (true) {
while (++i <= hi && nums[i] < v);
while (--j >= lo && nums[j] > v);
if (i >= j) {
break;
}
int t = nums[j];
nums[j] = nums[i];
nums[i] = t;
}
nums[lo] = nums[j];
nums[j] = v;
return j;
}
}
复杂度:时间 O(n) 空间 O(logn)
剑指 Offer 41. 数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
- void addNum(int num) - 从数据流中添加一个整数到数据结构中。
- double findMedian() - 返回目前所有元素的中位数。
示例 1:
输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:
输入:
["MedianFinder","addNum","findMedian","addNum","findMedian"]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]
限制:
- 最多会对
addNum、findMedian进行50000次调用。
解题思路:数组排序(超时)、二分查找、双堆
双堆:利用一个大根堆和一个小根堆分别存放一半的元素,大根堆存放小元素,小根堆存放大元素,此时大根堆的堆顶与小根堆的堆顶刚好可以算出中位数
class MedianFinder {
PriorityQueue<Integer> smallHeap, bigHeap;
public MedianFinder () {
bigHeap = new PriorityQueue<>((n1, n2) -> n2-n1);
smallHeap = new PriorityQueue<>((n1, n2) -> n1-n2);
}
// 当元素为偶数数量时,两堆顶/2即为中间数
// 当元素为奇位数量时,选一个堆顶作为中间数存放(此题解选小顶堆)
public void addNum(int num) {
// (1)说明相差>1,调整平衡性。向smallHeap添加元素达到平衡
if (bigHeap.size() != smallHeap.size()) {
if (bigHeap.peek() > num) {
smallHeap.offer(bigHeap.poll());
bigHeap.offer(num);
}
else {
smallHeap.offer(num);
}
}
// (2)完全平衡(平衡性为0)时。选择一个堆作为中间数存放。这里选的是bigHeap,所以当平衡时优先往bigHeap存
else {
// 符合性质,直接push达到平衡性
if (bigHeap.isEmpty() || smallHeap.peek() > num) {
bigHeap.offer(num);
}
// 不符合性质,对调位置
else {
bigHeap.offer(smallHeap.poll());
smallHeap.offer(num);
}
}
}
public double findMedian() {
if (bigHeap.size() == smallHeap.size()) {
return ((double) bigHeap.peek() + smallHeap.peek()) / 2;
}
else {
return bigHeap.peek();
}
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
剑指offer 第 17 天的更多相关文章
- 剑指offer 面试17题
面试17题: 题目:打印从1到最大的n位数 题:输入数字n,按顺序打印出从1到最大的n位十进制数,比如输入3,则打印出1.2.3一直到最大的3位数999. 解题思路:需要考虑大数问题,这是题目设置的陷 ...
- 剑指offer(17)树的子结构
题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 题目分析 分析如何判断树B是不是树A的子结构,只需要两步.很容易看出来这是一个递归的过程.一般在树 ...
- 剑指 offer set 17 判断一棵树是否是平衡树
总结 1. 书上给出一个简洁代码, 说是会重复遍历. 2. 框架相同, 可以写出下面的代码 class Solution { public: bool ans; int getHeight(TreeN ...
- 【剑指Offer】17、树的子结构
题目描述: 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 解题思路: 要查找树A中是否存在和树B结构一样的子树,我们可以分为两步:第一步, ...
- 【剑指Offer】剑指offer题目汇总
本文为<剑指Offer>刷题笔记的总结篇,花了两个多月的时间,将牛客网上<剑指Offer>的66道题刷了一遍,以博客的形式整理了一遍,这66道题属于相对基础的算法题目,对于 ...
- 剑指 Offer 17. 打印从1到最大的n位数
剑指 Offer 17. 打印从1到最大的n位数 Offer 17 题目解析: 暴力解法 package com.walegarrett.offer; /** * @Author WaleGarret ...
- 力扣 - 剑指 Offer 17. 打印从1到最大的n位数
题目 剑指 Offer 17. 打印从1到最大的n位数 思路1 如果有n位,那么最大值就是\(10^n-1\),即如果n是2,那么最大就到输出到99 考虑到大数情况,所以使用字符数组 还要把字符数组转 ...
- 【Java】 剑指offer(17) 在O(1)时间删除链表结点
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除 ...
- 剑指 Offer 题目汇总索引
剑指 Offer 总目录:(共50道大题) 1. 赋值运算符函数(或应说复制拷贝函数问题) 2. 实现 Singleton 模式 (C#) 3.二维数组中的查找 4.替换空格 ...
- 面试题目——《剑指Offer》
1.把一个字符串转换成整数——<剑指Offer>P29 2.求链表中的倒数第k个结点——<剑指Offer>P30 3.实现Singleton模式——<剑指Offer> ...
随机推荐
- Linux目录结构说明与基本操作
Linux系统目录如下: 详细说明如下: Linux系统文件与目录的基本操作: 一.显示文件内容命令--cat.more.less.head.tail. 1.cat命令 该命令的主要功能是用来显示文件 ...
- Spring Boot 中的异步调用
通常我们开发的程序都是同步调用的,即程序按照代码的顺序一行一行的逐步往下执行,每一行代码都必须等待上一行代码执行完毕才能开始执行.而异步编程则没有这个限制,代码的调用不再是阻塞的.所以在一些情景下,通 ...
- Java (新)将Excel数据读取到ListMap
Java (新)将Excel数据读取到ListMap Maven依赖: pom.xml <!-- excel --> <dependency> <groupId>o ...
- MySQL的卸载与安装
卸载 1.右键点击我的电脑 -->服务-->停掉MySQL的服务 2.控制面板卸载MySQL 3.删除隐藏文件夹 C:\ProgramData下的MySQL文件夹 4.删除MySQL文件夹 ...
- linux下opencv contrib安装
opencv安装 1.1 安装依赖 sudo apt-get update sudo apt-get install build-essential sudo apt-get install cmak ...
- js下载流文件
npm install js-file-download --savenpm install axios --save import axios from "axios"; imp ...
- 访问gitGub速度慢的解决
转自: https://baijiahao.baidu.com/s?id=1608100091125662190&wfr=spider&for=pc GitHub是一个面向开源及私有软 ...
- SqlServer 不能收缩 ID 为 %s 的数据库中 ID 为 %s 的文件,因为它正由其他进程收缩或为空。
SQLServer数据库通常都不建议进行SHRINKFILE操作,因为SHRINKFILE不当会造成一定的性能问题. 但是当进行了某些操作(例如某个超大的日志类型表转成分区表切换了数据文件),数据库某 ...
- nodejs res常用的返回方式
常用的返回方式有四种 res.json([status|body], [body]) 以json的形式返回数据res.render(view [, locals] [, callback]) 返回 ...
- SpringBoot的几大重要问题
1: traceID调用链 2:异步调用注解问题 import org.springframework.scheduling.annotation.Async;import org.springfra ...