00数据结构与算法刷题之【堆】篇

简介: 00数据结构与算法刷题之【堆】篇

前言


除了去年11月份以及今年近几月的算法刷题之外,只有在当时20年蓝桥杯准备的时候才刷过一些题,在当时就有接触到一些动归、递归回溯、贪心等等,不过那会也还是一知半解,做的题目也特别少,因为考虑到之后面试有算法题以及数据结构算法对于一个程序员十分重要,我也开始了刷题之路。


我目前的学习数据结构与算法及刷题路径:


1、学习数据结构的原理以及一些常见算法。


2、代码随想录:跟着这个github算法刷题项目进行分类刷,在刷题前可以学习一下对应类别的知识点,而且这里面每道题都讲的很详细。


3、牛客网高频面试101题:牛客网—面试必刷101题,在刷的过程中可以在leetcode同步刷一下。


4、接下来就是力扣上的专栏《剑指offer II》、《程序员面试金典(第 6 版)》…有对应的精选题单来对着刷即可。


5、大部分的高频面试、算法题刷完后,就可以指定力扣分类专栏进行一下刷题了。


刚开始刷的时候真的是很痛苦的,想到去年一道题可能就需要好几小时,真的就很难受的,不过熬过来一切都会好起来,随着题量的增多,很多题目你看到就会知道使用什么数据结构或者算法来去求解,并且思考对应的时间空间复杂度,并寻求最优解,我们一起加油!


我的刷题历程


截止2022.8.18:



1、牛客网101题(其中1题是平台案例有问题):



2、剑指offerII:



力扣总记录数:


加油加油!


剑指offer


剑指 Offer 41. 数据流中的中位数【中等】


学习资料:LeetCode刷题力扣题解 | 剑指 Offer 41. 数据流中的中位数 | 思路讲解及Python3代码实现


题目链接:剑指 Offer 41. 数据流中的中位数


题目内容:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。


思路:


1、小顶堆与大顶堆


复杂度分析:


时间复杂度:add操作时O(logn)、find是O(1)
class MedianFinder {
    //定义小顶堆和大顶堆
    //大顶堆:3             小顶堆:  4
    //       2                     5
    //       1                     6 
    private Queue<Integer> minQueue = new PriorityQueue<>();//小顶堆,小的在顶部,存储较大值
    private Queue<Integer> maxQueue = new PriorityQueue<>((x, y) -> y - x);//大顶堆,大的在顶部,存储较小值
    /** initialize your data structure here. */
    public MedianFinder() {
    }
    public void addNum(int num) {
        //若是两边数量不相等,插入到小顶堆中
        if (minQueue.size() != maxQueue.size()) {
            //先放入到大顶堆(先放入到小顶堆,再放入到大顶堆)
            minQueue.offer(num);
            maxQueue.offer(minQueue.poll());
        }else {
            //初步是放入到小顶堆中(先放入到大顶堆,再放入到小顶堆)
            maxQueue.offer(num);
            minQueue.offer(maxQueue.poll());
        }
    }
    public double findMedian() {
        //若是数量不一致,就直接返回小顶堆的元素,因为首先插入进入的是小顶堆,顶部的就是数据流中的中间数
        if (minQueue.size() != maxQueue.size()) {
            return minQueue.peek() * 1.0;
        }
        //数量相等,那么两个堆同时相加/2
        return (minQueue.peek() + maxQueue.peek()) / 2.0;
    }
}


牛客网


最小的k个数【中等】


题目链接:最小的K个数


题目内容:给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。


**思路1:优先队列(大顶堆)**存储k个值(由大到小),然后后面的值不断的与大顶堆的最大值作比较。


复杂度分析:


时间复杂度:O(nlogk)
空间复杂度:O(k)
import java.util.ArrayList;
//需要加上这个import
import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> res = new ArrayList<>();
        if (k == 0 || input == null || input.length == 0) {
            return res;
        }
        //借助优先队列(大到小排序)
        PriorityQueue<Integer> queue = new PriorityQueue<>((o1,o2)->o2.compareTo(o1));
        for (int i = 0;i < k;i++) {
            queue.offer(input[i]);
        }
        for (int i = k;i < input.length;i++) {
            if (queue.peek() > input[i]) {
                queue.poll();
                queue.offer(input[i]);
            }
        }
        while (!queue.isEmpty()){
            res.add(queue.poll());
        }
        return res;
    }
}



思路2:排序。使用快排,然后返回前k个元素即可。


复杂度分析:


时间复杂度:O(nlogn)
空间复杂度:O(1)
import java.util.ArrayList;
//需要加上这个import
import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> res = new ArrayList<>();
        if (k == 0 || input == null || input.length == 0) {
            return res;
        }
        //快排:O(nlogn)
        Arrays.sort(input);
        for (int i = 0;i < k;i++) {
            res.add(input[i]);
        }
        return res;
    }
}


数据流中的中位数【中等】


题目链接:数据流中的中位数


题目内容:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使GetMedian()方法获取当前读取数据的中位数。


思路1:使用一个集合来进行插入排序。


问题:在leetcode中超时了。

复杂度分析:


时间复杂度:Insert操作是O(n),get是O(1)
空间复杂度:O(n)
import java.util.*;
public class Solution {
    private ArrayList<Integer> list = new ArrayList<>();
    //插入操作
    public void Insert(Integer num) {
        if (list.size() == 0) {
            list.add(num);
        }else {
            int i = 0;
            for (;i < list.size();i++) {
                if (list.get(i) > num){
                    break;
                }
            }
            list.add(i, num);
        }
    }
    public Double GetMedian() {
        int size = list.size();
        //奇、偶个数情况
        if (size % 2 == 1) {
            return (double)list.get(size / 2);
        }else {
            int a = list.get(size / 2 - 1);
            int b = list.get(size / 2);
            return (double)(a + b) / 2;
        }
    }
}



滑动窗口的最大值【中等】


题目链接:滑动窗口的最大值


题目内容:给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。


思路1:借助优先队列来实现。【牛客网超时】


复杂度分析:


时间复杂度:O(n*m)
空间复杂度:O(n)
import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> res = new ArrayList<>();
        //越大的优先
        PriorityQueue<Integer> queue = new PriorityQueue<>((o1, o2)->{
            if (o1 > o2) {
                return -1;
            }else if (o1 < o2) {
                return 1;
            }
            return 0;
        });
        for (int slow = 0, fast = 0; fast < num.length; fast++) {
            queue.add(num[fast]);
            //fast表示数组的坐标位置
            if (fast >= (size - 1)) {
                //出队最大的元素
                res.add(queue.peek());
                //移除元素
                queue.remove(num[slow]);
                slow++;
            }
        }
        return res;
    }
}



思路2:双端队列来进行。【推荐】


时间复杂度:O(n),数组长度为n,只遍历一遍数组。
空间复杂度:O(m),窗口长度m,双向队列最长时,将窗口填满。
import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> res = new ArrayList<>();
        if (num.length < size) {
            return res;
        }
        //双端队列
        //1、第一个窗口
        ArrayDeque<Integer> queue = new ArrayDeque<Integer>();
        for (int i = 0; i < size; i++) {
            while (!queue.isEmpty() && num[i] > queue.peekLast()) {
                queue.pollLast();
            }
            queue.add(num[i]);
        }
        res.add(queue.peek());
        //2、之后窗口
        //后面循环遍历 (1、看当前i-size的元素值是否与当前元素值相同,相同则出队。2、每次来while循环,若是队尾元素<即将入队的元素,那么进行pollLast())
        for (int i = size; i < num.length; i++) {
            //①由于是移动新的一位,那么就要看是否需要出队
            if (queue.peek() == num[i - size]) {
                queue.pollFirst();
            }
            //2、尝试过滤掉在当前队列中<当前元素的
            while (!queue.isEmpty() && num[i] > queue.peekLast()) {
                queue.pollLast();
            }
            queue.addLast(num[i]);
            res.add(queue.peek());
        }
        return res;
    }
}


相关文章
|
1月前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
43 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
1月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
95 16
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
92 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
2月前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
103 1
|
3月前
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
70 5
【数据结构】优先级队列(堆)从实现到应用详解
|
2月前
|
存储 算法 调度
数据结构--二叉树的顺序实现(堆实现)
数据结构--二叉树的顺序实现(堆实现)
|
2月前
|
存储 算法 分布式数据库
【初阶数据结构】理解堆的特性与应用:深入探索完全二叉树的独特魅力
【初阶数据结构】理解堆的特性与应用:深入探索完全二叉树的独特魅力
|
2月前
|
数据可视化 搜索推荐 Python
Leecode 刷题笔记之可视化六大排序算法:冒泡、快速、归并、插入、选择、桶排序
这篇文章是关于LeetCode刷题笔记,主要介绍了六大排序算法(冒泡、快速、归并、插入、选择、桶排序)的Python实现及其可视化过程。
23 0
|
2月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆
|
2月前
|
存储 算法 Java
【用Java学习数据结构系列】用堆实现优先级队列
【用Java学习数据结构系列】用堆实现优先级队列
38 0