IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 算法03-堆 -> 正文阅读

[数据结构与算法]算法03-堆


前言


下面是堆

一、堆的概念以及java内部的堆的实现

  • 堆的概念是:1、是完全二叉树。2、根节点都大于或者小于左右子节点。
  • 在java中的堆的实现通过内置的任务队列类进行具体如下:
//这个默认为小顶堆
PriorityQueue<Integer> queue = new PriorityQueue<>();
//这个默认为大顶堆
PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
 });

关于堆的一些算法题

二、有关堆的算法题解

2.1 数组中的第K大个元素–力扣215题

  • 题目要求

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

  • 题解思路

给定一个数组以及一个K值,获取这个数组的第k大个元素。那么这个题目不难思考,可以使用小顶堆的做,为什么呢?我们都知道小顶堆是最小的一个元素位于对顶,那么对于我们需要取多少个数,那么就是可以理解为我们将前k个大的数都存进去了,因为每次堆顶的元素都是这个堆中的最小的元素,那么我们可以先存储K个数(也就是先定义整个堆的长度)如果存储的数大于k个我们就弹出堆顶的数,每次比较堆顶元素小于需要存进去的那个数就可以直接将堆顶元素弹出,将新的数存储,这样循环遍历整个数组就能在这个小顶堆中存储K个元素,并且堆顶元素就是第K个大的数。

  • 代码实现
class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        for (int num : nums) {
            if (queue.size()<k || queue.peek()<num){
                queue.offer(num);
                if (queue.size()>k) queue.poll();
            }
        }
        return queue.peek();
    }
}

2.2 数据流中的第K大元素–力扣703题

  • 题目要求

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。 KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。 int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

  • 题解思路

根据上面的题目要求,可以知道是找到一个数据流中的第K大元素,这个地方同样使用小顶堆的方式进行存储,声明一个小顶堆,在add()方法中编写向小顶堆中添加元素的方法,在构造函数中初始化添加的数组(数据流)以及第K个值(可以理解为整个堆的大小),这样就能够实现获取第K个大的数,方法同上。

  • 代码实现
class KthLargest {
    PriorityQueue<Integer> priorityQueue;
    int k;
    public KthLargest(int k, int[] nums) {
        priorityQueue = new PriorityQueue<>();
        this.k = k;
        for (int num : nums) {
            add(num);
        }
    }
    public int add(int val) {
        priorityQueue.offer(val);
        if (priorityQueue.size()>k){
            priorityQueue.poll();
        }
        return priorityQueue.peek();
    }
}

2.3 最后一块石头的重量-力扣1046题

  • 题目要求

有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块最重的石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

  • 题解思路

题目要求是选出最重的石头做粉碎,重量相减,然后如果有剩余就放入当石头堆中继续找最重的两块石头做粉碎,直到最后有一块石头就直接返回石头重量,没有石头就返回0。这个题就很明显使用大顶堆来做,将最大的石头放在堆顶,每次弹出两块进行比较,相减为0则继续进行,不为零则将相减得到的重量进行offer(存储)直到只有一块或者为空返回。

  • 代码实现
class Solution {
    public int lastStoneWeight(int[] stones) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });
        for (int stone : stones) {
            priorityQueue.offer(stone);
        }
        while (priorityQueue.size()>1){
            int one = priorityQueue.poll();
            int two = priorityQueue.poll();
            int pl = one-two;
            if (pl!=0){
               priorityQueue.offer(pl);
            }
        }
        return priorityQueue.isEmpty()?0:priorityQueue.poll();
    }
}

2.4 查找最小的K对数字-力扣373题

  • 题目要求

给定两个以升序排列的整数数组 nums1 和 nums2 , 以及一个整数 k 。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。
请找到和最小的 k 个数对 (u1,v1), (u2,v2) … (uk,vk) 。

  • 题解思路

给定两个数组找到最小的k对数对,在这里方法类似于上面。就是需要两个数组的嵌套循环遍历,将所有的结果都使用一遍,同样声明一个只有K大小的一个小顶堆。在声明这个小顶堆的时候就比较有讲究,每个堆中存储三个元素,数组一的一个u1与数组二的u2,还有就是两个数的和。在声明比较的小顶堆的条件是以两个数的和做比较。

  • 代码实现
class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        PriorityQueue<int[]> priorityQueue = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o2[2] - o1[2];
            }
        });
        for (int i = 0; i < nums1.length; i++) {
            for (int j = 0; j < nums2.length; j++) {
                if (priorityQueue.size()<k || nums1[i]+nums2[j]<priorityQueue.peek()[2]){
                    priorityQueue.offer(new int[]{nums1[i],nums2[j],nums1[i]+nums2[j]});
                    if (priorityQueue.size()>k)  priorityQueue.poll();
                }else break;
            }
        }
        List<List<Integer>> res = new ArrayList<>();
        while (!priorityQueue.isEmpty()){
            int[] ints = priorityQueue.poll();
            res.add(new ArrayList<Integer>(){
                {
                    this.add(ints[0]);
                    this.add(ints[1]);
                }
            });
        }
        return res;

    }
}

2.5 最小的k个数–力扣-剑指offer 40题

  • 题目要求

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

  • 题解思路

这题就比较简单了,应该是在上面的第一题,方法同理,声明一个小顶堆,大小是K,然后遍历存储,大于堆顶元素的将原来的堆顶元素弹出,在将大于的这个元素存储。遍历完成返回这个小顶堆就可以了。

  • 代码实现
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });
        for (int i : arr) {
            priorityQueue.offer(i);
            if (priorityQueue.size()>k){
                priorityQueue.poll();
            }
        }
        return priorityQueue.stream().mapToInt(i->i).toArray();
    }
}

2.6 数据流中的中位数–力扣295题 与 面试题17.20连续中值

  • 题目要求

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

  • 题解思路

如果使用的是一个有序数组存储的话会不会比较简单的获取中位数,当数量为偶数时返回中间两个数的平均值,当为奇数时,返回最中间的那个数即可。但是这里使用的是堆存储,有小顶堆存储的是中位数后面半截的数据,一个大顶堆存储的是中位数据前半截的数据。在数据的存储分配是需要进行处理,保证小顶堆的最小值比大顶堆的最小值小。这样当这个数据流的长度为偶数时就是大顶堆的堆顶元素和小顶堆的堆顶元素的平均值,为奇数时两个堆中的元素数量相减得到是位于前半段还是后半段。

  • 代码实现
class MedianFinder {
    PriorityQueue<Integer> l,r;
    /** initialize your data structure here. */
    public MedianFinder() {
        l = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });
        r = new PriorityQueue<>();
    }
    public void addNum(int num) {
        if (l.isEmpty() || l.peek()>num) l.offer(num);
        else r.offer(num);
        if (r.size()>l.size()){
            l.offer(r.poll());
        }
        if (l.size() == r.size()+2){
            r.offer(l.poll());
        }
    }
    public double findMedian() {
        int n = l.size()+r.size();
        if (n%2!=0) return l.peek();
        return (l.peek()+r.peek())/2.0;
    }
}

2.7 前k个高频单词–力扣692题

  • 题目要求

给一非空的单词列表,返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。

  • 题解思路

这个题的思路与前面的题的思路是差不多的,重点在于对相同单词的处理,在这个题中,我们先对整个单词列表进行存储(相同的得到数量)。我们通过Map集合的方式,将单词作为key,出现频率作为value。通过map集合的内置函数方法getOrDefault()将相同的Key进行统计叠加。然后同上面的获取前K个出现的单词,一样的创建一个小顶堆进行相关的操作。

  • 代码实现
class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        Map<String,Integer> map = new HashMap<>();
        for (String word : words) {
            map.put(word,map.getOrDefault(word,0)+1);
        }
        PriorityQueue<Map.Entry<String,Integer>> priorityQueue = new PriorityQueue<>(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                return o1.getValue() == o2.getValue() ? o2.getKey().compareTo(o1.getKey()):o1.getValue()-o2.getValue();
            }
        });
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            priorityQueue.offer(entry);
            if (priorityQueue.size()>k) priorityQueue.poll();
        }
        List<String> res = new ArrayList<>();
        while (!priorityQueue.isEmpty()){
            String key = priorityQueue.poll().getKey();
            res.add(0,key);
        }
        return res;
    }
}

2.8 移除石子的最大得分–力扣1753题

  • 题目要求

你正在玩一个单人游戏,面前放置着大小分别为 a、b 和 c 的 三堆 石子。
每回合你都要从两个 不同的非空堆 中取出一颗石子,并在得分上加 1 分。当存在 两个或更多 的空堆时,游戏停止。
给你三个整数 a 、b 和 c ,返回可以得到的 最大分数 。

  • 题解思路

实际上很简单,最大石子相减,得到的次数就是最大石子的最优,再将剩余的与开始的石子项比较得到小的那个,再加上最开始两堆石子的结果就可以得到相对应的值。

  • 代码实现
class Solution {
    public int maximumScore(int a, int b, int c) {
      if (a>b){
          int t = a;
          a = b;
          b = t;
      }
      if (a>c){
          int t = a;
          a = c;
          c = t;
      }
      if (b>c){
          int t = b;
          b = c;
          c = t;
      }if (a<=c-b) return a+b;
      return (a+b+c)/2;
    }
}

总结

这个是堆的一些算法,自己的题解思路记录。同时感觉自己能够在遇到算法题的时候自然而然的想到许多的数据结构思路来解决算法的相关问题。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:38:39  更:2021-08-17 15:39:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:20:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码