第 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 <= 10000
  • 0 <= 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 天的更多相关文章

  1. 剑指offer 面试17题

    面试17题: 题目:打印从1到最大的n位数 题:输入数字n,按顺序打印出从1到最大的n位十进制数,比如输入3,则打印出1.2.3一直到最大的3位数999. 解题思路:需要考虑大数问题,这是题目设置的陷 ...

  2. 剑指offer(17)树的子结构

    题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 题目分析 分析如何判断树B是不是树A的子结构,只需要两步.很容易看出来这是一个递归的过程.一般在树 ...

  3. 剑指 offer set 17 判断一棵树是否是平衡树

    总结 1. 书上给出一个简洁代码, 说是会重复遍历. 2. 框架相同, 可以写出下面的代码 class Solution { public: bool ans; int getHeight(TreeN ...

  4. 【剑指Offer】17、树的子结构

      题目描述:   输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构)   解题思路:   要查找树A中是否存在和树B结构一样的子树,我们可以分为两步:第一步, ...

  5. 【剑指Offer】剑指offer题目汇总

      本文为<剑指Offer>刷题笔记的总结篇,花了两个多月的时间,将牛客网上<剑指Offer>的66道题刷了一遍,以博客的形式整理了一遍,这66道题属于相对基础的算法题目,对于 ...

  6. 剑指 Offer 17. 打印从1到最大的n位数

    剑指 Offer 17. 打印从1到最大的n位数 Offer 17 题目解析: 暴力解法 package com.walegarrett.offer; /** * @Author WaleGarret ...

  7. 力扣 - 剑指 Offer 17. 打印从1到最大的n位数

    题目 剑指 Offer 17. 打印从1到最大的n位数 思路1 如果有n位,那么最大值就是\(10^n-1\),即如果n是2,那么最大就到输出到99 考虑到大数情况,所以使用字符数组 还要把字符数组转 ...

  8. 【Java】 剑指offer(17) 在O(1)时间删除链表结点

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除 ...

  9. 剑指 Offer 题目汇总索引

    剑指 Offer 总目录:(共50道大题) 1. 赋值运算符函数(或应说复制拷贝函数问题) 2. 实现 Singleton 模式 (C#) 3.二维数组中的查找 4.替换空格              ...

  10. 面试题目——《剑指Offer》

    1.把一个字符串转换成整数——<剑指Offer>P29 2.求链表中的倒数第k个结点——<剑指Offer>P30 3.实现Singleton模式——<剑指Offer> ...

随机推荐

  1. FastReport报表金额数字转大写问题

    在使用FastReport报表打印的时候涉及到财务结算金额时,会用到大写,系统保存的都为数字,将数字转换为大写没有默认的系统内置函数,经过查阅资料,可通过对FastReport的页面设计代码修改实现: ...

  2. https://计算机四级

    计算机四级内容: 一,网络工程师 基本要求 1.了解大型网络系统规划.管理方法: 2.具备中小型网络系统规划.设计的基本能力: 3.掌握中小型网络系统组建.设备配置调试的基本技术: 4.掌握企事业单位 ...

  3. mac 安装 nginx 流程,并解决前端跨域问题

    mac 安装 nginx 流程 首先mac安装brew包管理工具: /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN ...

  4. Spring Boot 中处理跨域

    HTML 5中新增的跨域资源访问(Cross-Origin Resource Sharing)特性可以让我们在开发后端系统的时候决定资源是否允许被跨域访问.所谓跨域指的是域名不同或者端口不同或者协议不 ...

  5. K8SYaml文件详解

    一.K8S支持的文件格式 kubernetes支持YAML和JSON文件格式管理资源对象. JSON格式:主要用于api接口之间消息的传递 YAML格式:用于配置和管理,YAML是一种简洁的非标记性语 ...

  6. 网络游戏同步法则 -- skywind

    转载出处:http://www.skywind.me/blog/archives/112 网路的硬件也有限,而人的创造也无限,在公网平均130ms的Latency下,是不存在"完全的&quo ...

  7. heiamJava16IO流

    Java I/O流 按流的方向分 输入输出流 I表示intput(输入),是数据从硬盘文件读入到内存的过程,称之输入,负责读. O表示output(输出),是内存程序的数据从内存到写出硬盘文件的过程, ...

  8. Tomcat总体架构和启动流程

    Tomcat大家都知道,这个没什么好描述的,我们先看Tomcat的总体架构 1.总体架构 架构一步一步增加组件,先来个最原始的 === Server:Tomcat的整体服务,负责接收和处理请求.其拥有 ...

  9. 更新Vue-cli4与Eslint插件自动保存

    Vue-cli更新 此更新为Vue-cli3更新为4,可使用node来查看本机的安装版本,vue -V,此版本(cli4)需要node v8.9 或更高版本 (推荐 v10 以上),查看node版本可 ...

  10. mysql创建函数时提示1418。可选关闭二进制日志或者设置log_bin_trust_function_creators=1

    报错详情如下:1418--This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration a ...