剑指offer | 面试题32:最小的k个数

  1. 干货 | 手撕十大经典排序算法

  2. 剑指offer | 认识面试

  3. 剑指offer | 面试题2:实现Singleton模式

  4. 剑指offer | 面试题3:二维数组的查找

  5. 剑指offer | 面试题4:替换空格

  6. 剑指offer | 面试题5:从尾到头打印链表

  7. 剑指offer | 面试题6:重建二叉树

  8. 剑指offer | 面试题7:用两个栈实现队列

  9. 剑指offer | 面试题8:旋转数组的最小数字

  10. 剑指offer | 面试题9:斐波那契数列

  11. 剑指offer | 面试题10:青蛙跳台阶问题

  12. 剑指offer | 面试题11:矩阵覆盖

  13. 剑指offer | 面试题12:二进制中1的个数

  14. 剑指offer | 面试题13:数值的整数次方

  15. 剑指offer | 面试题14:打印从1到最大的n位数

  16. 剑指offer | 面试题15:删除链表的节点

  17. 剑指offer | 面试题16:将数组中的奇数放在偶数前

  18. 剑指offer | 面试题17:链表中倒数第k个节点

  19. 剑指offer | 面试题18:反转链表

  20. 剑指offer | 面试题19:合并两个有序链表

  21. 剑指offer | 面试题20:判断二叉树A中是否包含子树B

  22. 剑指offer | 面试题21:二叉树的镜像

  23. 剑指offer | 面试题22:顺时针打印矩阵

  24. 剑指offer | 面试题23:包含min函数的栈

  25. 剑指offer | 面试题24:栈的压入、弹出序列

  26. 剑指offer | 面试题25:从上到下打印二叉树

  27. 剑指offer | 面试题26:二叉搜索树的后序遍历序列

  28. 剑指offer | 面试题27:二叉树中和为某一值的路径

  29. 剑指offer | 面试题28:复杂链表的复制

  30. 剑指offer | 面试题29:二叉搜索树转换为双向链表

  31. 剑指offer | 面试题30:字符串的排列

  32. 剑指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 = {45162738};
        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 == 0return 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 == 0return 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);
        }
    }
}

参考链接:

  1. https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/3chong-jie-fa-miao-sha-topkkuai-pai-dui-er-cha-sou/
  2. 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

革命尚未成功,同志仍需努力,冲冲冲


  1. 学会Spring的正确姿势!

  2. 万万没想到!Bean还有这么多东西

  3. Spring中的AOP!

  4. 聊聊Spring数据库开发

  5. Spring事务还能这样管理?

  6. 老师问我 Spring MVC 的工作流程

  7. 分享 | 后端必会的Spring MVC核心类和注解

  8. 还有人不知道?Spring MVC的数据绑定来了

  9. 开发必掌握!JSON数据交互和RESTful开发

  10. 拦截器的骚操作

  11. 捋一捋上传和下载

  12. 老师又问我MyBatis了

  13. 开发常用MyBatis的核心配置,你能看懂几个?

  14. EDG!动态SQL!牛逼!

  15. 面试官:请讲一下MyBatis是如何关联关系?

  16. 细说Spring整合Mybatis

  17. 撒花 | SSM 完结

  1. 设计模式概述

  2. 面向对象设计原则

  3. 简单工厂模式

  4. 抽象工厂模式

  5. 建造者模式

  6. 原型模式

  7. 朋友问我单例模式是什么?

  8. 面试官:啥是适配器模式?

  9. 什么是桥接模式?你可能还不知道

  10. 第十篇!组合模式

  11. 装饰模式,不难!

  12. 1024,我还在啃外观模式!

  13. 这就是享元模式!

  14. 常见的代理模式

点个在看你最好看

阅读原文

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/213338.html

联系我们
联系我们
分享本页
返回顶部