数据结构(六):堆介绍及面试常考算法

news2024/11/24 17:45:22

一、堆介绍

1、定义

堆是一种图的树形结构,被用于实现“优先队列”(priority queues)。优先队列是一种数据结构,可以自由添加数据,但取出数据时要从最小值开始按顺序取出。在堆的树形结构中,各个顶点被称为“结点”(node),数据就存储在这些结点中,堆有下列特点

(1)每个节点最多有两个子节点;

(2)堆列顺序必须从上到下,同一行从左到右;

(3)堆中某个节点的值总是不大于或不小于其父节点的值;

(4)存放数据时,一般会把新数据放在最下面一行靠左的位置。如果最下面一行没有多余             空间时,就在往下另起一行,并把数据添加到这一行的最左端。

2、堆的性质

(1)堆是一个完全二叉树

(2)堆中某个结点的值总是不大于或不小于其父节点的值;

(a) 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的         堆有二叉堆、斐波那契堆等。

(b) 一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编          号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,          则这棵二叉树称为完全二叉树

(c) 一棵深度为k且有2^k-1个结点的二叉树称为满二叉树。也就是说除了叶子节点都有2个         子节点,且所有的叶子节点都在同一层。

(3)大顶堆与小顶堆的push与pop

大顶堆的push--往已有的大顶堆中添加元素

(a)将新元素作为树的最后一个节点

(b)比较新节点与其父节点:如果新节点的值大于父节点,那么交换父节点和新节点的值,其实就是就是交换两个元素的值

(c)重复上述步骤,直到新节点比其父节点小,或者当前新节点的位置已经是根节点了,那么停止上述循环即可,此时大顶堆更新完毕。

大顶堆的pop--弹出堆的根节点,然后对堆进行调整。

(a)将堆的最后一个叶子节点移到根节点的位置

(b)从根节点开始,比较根节点和其左右子节点的元素大小,若根节点不是都比子节点大,那么根节点与其较大的一个子节点进行交换

(c)只要存在子节点,那么继续比较父节点和左右子节点的大小,直到当前节点已经是叶子节点或者它比它的左右子节点取值都大,那么停止循环,最大堆已经更新完毕

 3、优缺点及使用场景

优点:高效的插入和删除操作、有效的查找最值

缺点:不支持快速查找、不支持动态调整大小

使用场景:优先级队列、求最值、排序算法、调度算法、求中文数,总的来说,堆适合于处理需要高效地插入、删除最值的场景,但不适合需要频繁查找非最值元素的场合。

4、常用操作

push--插入元素道优先级列表中。

pop--从优先级列表中删除顶部元素。

top--返回优先级队列的顶部元素。

emplace--在优先级队列中直接构造元素。

swap--交换两个优先级队列的内容。

二、面试常考的算法

1、最小的K个数

题目:给定一个长度为n的可能有重复值的数组,找出其中不去重的最小的k个数。

示例:input:[4,5,1,6,2,7,3,8] ,k = 4;output:[1,2,3,4]

思路:求最小的K个数和求最大的K个数都可以通过维护堆的方式来进行完成。

最小的K个数:维护大顶堆,陆续往里面插入元素,当堆的长度大于K时,弹出堆顶元素,最后得到的就是K个数组中最小的元素。

最大的K个数:同理,维护小顶堆。

C++中的堆创建:

(1)std::priority_queue<int> pq; //创建大顶堆

(2)std::priority_queue<int, std::vector<int>, std::greater<int>> pq; //创建小顶堆

        参数1:指定了存储的元素类型为整数

        参数2:使用vector作为底层容器来存储元素

        参数3:std::greater<int>是一个比较函数,定义了元素的优先级比较方式。

(3)自定义优先级

        struct node
        {

            int priority;
            int value;

            friend bool operator< (node n1, node n2)
            {
                return n1.priority < n2.priority;
            }
        };

python中的堆创建:

 from queue import PriorityQueue

pq = PriorityQueue() # 默认最小堆

python中最大堆的创建:通过将元素的优先级取相反数来实现。这样,最大的元素实际上会变成优先级最高的负数,因此会被优先弹出。
        for i in array:

                # 添加元素,注意元素的优先级是取相反数

                pq.put((-i, i))

                if(pq.qsize() > k):

                        # 弹出元素

                         pq.get()

#include<vector>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

vector<int> getLeastNumbers(vector<int> array, int k){
    vector<int> res;
    priority_queue<int> heap; //大顶堆
    // priority_queue<int, vector<int>,greater<int>> heap; //小顶堆
    for(int i = 0; i < array.size(); i++){
        heap.push(array[i]);
        if(heap.size() > k) heap.pop();
    }
    while(heap.size()){
        res.push_back(heap.top());
        heap.pop();
    }
    reverse(res.begin(),res.end());
    return res;
}

int main(){
    vector<int> array = {4, 5, 1, 6, 2, 7, 3, 8};
    int k = 4;
    vector<int> res = getLeastNumbers(array, k);
    for(auto r: res){
        cout << r << ',';
    }
}

 

2、第K大的数 

题目:给定一个长度为n的可能有重复值的数组,找出其中不去重的最大的第k个数。

示例:input:[4,5,1,6,2,7,3,8] ,k = 4;output:5

思路:求最小的K个数和求最大的K个数都可以通过维护堆的方式来进行完成。

方法1:(大顶堆)同上述第一题,使用大顶堆的方式进行完成,注意到这题给的参数中有一个总数(数组的总数),利用大顶堆,他要我们求第K大,那我们反向求n-k+1小就行。 

方法2:(小顶堆),利用小顶堆,求第K大。

#include<vector>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

// 小顶堆实现
int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<int>> Q;
        for (auto x : nums) {
            Q.push(x);
            // cout << Q.top() << ",";
            if(Q.size() > k) Q.pop();
            
            
        }
        return Q.top();
    }

// 大顶堆实现
int getKth(vector<int> a, int n, int k){
    priority_queue<int> heap;  //大顶堆
    for(int i = 0; i < a.size(); i++){
        heap.push(a[i]);
        if(heap.size() > n-k+1) heap.pop();
    }
    int result = heap.top();
    return result;
}

int main(){
    vector<int> array = {4, 5, 1, 6, 2, 7, 3, 8};
    int k = 3;
    int res = findKthLargest(array, k);
    cout << res<<endl;
    int res2 = getKth(array, array.size(), k);
    cout << res2;
}

3、数据流中的中位数

题目:给定一个长度为n的可能有重复值的数组,计算这组数的中位数。

示例:input:[4,5,1,6,2,7,3,8] ,output:4.5

           input:[4,5,1,6,2,7,3,8,9] ,output:5

思路:方法1:利用堆排序将给定数组排序,排好序后在去找中位数。

          方法2:同第2题的思路,只需利用堆找到中位数索引对应的元素计算即可。

#include<vector>
#include<queue>
#include<iostream>
using namespace std;
vector<int> GetMedianInData(vector<int> arr){
    priority_queue<int> pq;
    vector<int> res;
    for(auto c: arr){
        pq.push(c);
    }
    while(pq.size()){
        res.push_back(pq.top());
        pq.pop();
    }
    return res;
}

// 小顶堆实现
int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<int>> Q;
        for (auto x : nums) {
            Q.push(x);
            // cout << Q.top() << ",";
            if(Q.size() > k) Q.pop();
            
            
        }
        return Q.top();
    }

int main(){
    // 先堆排序,再找中位数
    // vector<int> array = {4, 5, 1, 6, 2, 7, 3, 8, 9};
    // vector<int> res = GetMedianInData(array);
    // for(auto r: res){
    //     cout << r << ',';
    // }
    // double median;
    // if (res.size() % 2 == 1){
    //     median = res[res.size() / 2];
    // }
    // else{
    //     median = (double(res[res.size() / 2]) + double(res[res.size() / 2 - 1])) / 2;
    // }
    // cout << "中位数是:" << median;

    // 把中位数对应的元素利用堆排序找出来
    vector<int> array = {4, 5, 1, 6, 2, 7, 3, 8};
    int median, front, behind;
    double res;
    if (array.size() % 2 == 1){
        median = array.size() / 2;
        res = findKthLargest(array, median + 1);
    }
    else{
       front = array.size() / 2 ;
       behind = array.size() / 2 - 1;
       res = double(findKthLargest(array, behind + 1) + findKthLargest(array, front + 1))/2;
    }
    cout << "中位数是:" << res;
    
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1273449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web:ics-05(本地文件包含漏洞、preg_replace函数/e漏洞、php伪协议读取文件)

题目 打开页面显示如下 只有这个页面能打开 显示如下 用dirsearch扫一下 查看了一下&#xff0c;发现没什么用 查看页面源代码 返回了&#xff0c;写入的参数&#xff0c;猜测可能有文件包含漏洞 用php伪协议读取文件 构造payload ?pagephp://filter/readconvert.base64-en…

java源码-流程控制

1、Java流程控制 主要涉及三大流程控制&#xff1a;顺序、分支、循环 如下图&#xff1a; 1&#xff09;流程2 存在对用户名和密码的校验&#xff0c;是否为空&#xff0c;存在分支控制 2&#xff09;流程3 用户名和密码在数据库是否存在&#xff0c;存在分支控制 3&#xff0…

深入了解Java8新特性-日期时间API之TemporalAdjusters与TemporalAdjuster

阅读建议 嗨&#xff0c;伙计&#xff01;刷到这篇文章咱们就是有缘人&#xff0c;在阅读这篇文章前我有一些建议&#xff1a; 本篇文章大概10000多字&#xff0c;预计阅读时间长需要10分钟。本篇文章的实战性、理论性较强&#xff0c;是一篇质量分数较高的技术干货文章&…

Linux基础操作二:Linux系统介绍

1、系统启动过程 Linux系统的启动过程并不是大家想象中的那么复杂&#xff0c;其过程可以分为5个阶段&#xff1a; 内核的引导。运行 init。系统初始化。建立终端 。用户登录系统。 1.1、内核引导 当计算机打开电源后&#xff0c;首先是BIOS开机自检&#xff0c;按照BIOS中…

六、shell编程

详见 《shell编程超详细入门教程》

enum枚举类 - Java

枚举类 一、引入二、介绍三、实现方式1、 自定义类实现枚举小结 2、 enum关键字实现枚举 四、使用enum注意事项五、练习六、enum常用方法七、练习八、enum和接口 一、引入 要求创建季节(Season)对象&#xff0c;请设计并完成。 传统方法&#xff1a; public class Enumerati…

【秒懂JDK,JRE,JVM的关系】

&#x1f320;作者&#xff1a;TheMythWS. &#x1f387;座右铭&#xff1a;不走心的努力都是在敷衍自己&#xff0c;让自己所做的选择&#xff0c;熠熠发光。 ​ JDK与JRE与JVM的关系 先用一张图来直观感受JDK JRE JVM之间的关系&#xff1a; JDK与JRE的关系 先说JDK和JRE…

绩效考核管理项目|记录2

给界面添加筛选条件并且把搜索功能实现 这段代码写入搜索方法里面就能实现功能。 private void bingdgv(){//筛选项&#xff1a;用户名、职位代码、是否辞职string userName txtUserName.Text.Trim();int baseTypeId (int)base_cbx.SelectedValue;bool isStop isdel_ckb.Che…

Loki安装部署

Loki安装部署 1、Loki介绍 Loki 是受 Prometheus 启发由 Grafana Labs 团队开源的水平可扩展&#xff0c;高度可用的多租户日志聚合系统。开发语 言: Google Go。它的设计具有很高的成本效益&#xff0c;并且易于操作。使用标签来作为索引&#xff0c;而不是对全文进行检索&…

人工智能“排头兵”,探访福州多地 AI 智算实践

生成式 AI 在 2023 年再次引爆 IT 技术发展&#xff0c;福建作为数字中国的重要策源地&#xff0c;也是国家数字经济创新发展试验区&#xff0c;在人工智能方面拥有良好的产业基础和人才优势&#xff0c;同时近期出台的《福建省促进人工智能产业发展十条措施》&#xff0c;为福…

基于web的舞蹈选课管理系统设计与实现

基于web的舞蹈选课管理系统的设计与实现 摘 要 现如今&#xff0c;学生网上选课越来越便利&#xff0c;但是大多数在线下教育机构工作的教务人员&#xff0c;在进行教务管理的时候&#xff0c;并没有完全采用信息化管理。随着选择线下教育机构的人数不断增加&#xff0c;人工…

jQuery和JavaScript的区别

一、比较原生js和jQuery的区别 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title…

np.random.uniform() 采样得到的是一个高维立方体,而不是球体,为什么?

在代码中&#xff0c;采样是通过以下方式完成的&#xff1a; samples self.center np.random.uniform(-self.radius, self.radius, (num_samples, len(self.center))) 这里&#xff0c;np.random.uniform函数在每个维度独立地生成了一个介于-self.radius和self.radius之间的…

沉默是金,寡言为贵

​ 佛说&#xff1a;“人受一句话&#xff0c;佛受一柱香。”佛教的十善&#xff0c;其中有关口德就占了四样&#xff1a;恶口、妄语、两舌、绮语&#xff0c;可见口德是很重要的。言为心声&#xff0c;能说出真心的话&#xff0c;必然好听&#xff1b;假如说话言不由衷&#x…

Vue路由跳转页面刷新

案例使用映射路由 百度的时候各种操作就是没有注意keepAlive&#xff0c;发现那个为缓存开启之后前端有个小后台Vue生命周期函数失效。同一个页面刷新时这个keep Alive需要关闭。

热部署怎么部署

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言操作流程&#xff1a;在这里插入图片描述 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a832d83c091742eda9d9325931a89df4.png) 这里的跟上面的…

vue3+vite 批量引入局部组件及使用

目录结构 批量引入组件 例如&#xff1a;src/views/oss/components/customComponents.ts import { ref, defineAsyncComponent, markRaw } from vue;const modules import.meta.glob(./*.vue);//这告诉 TypeScript&#xff0c;components.value 是一个键为字符串、值为 define…

埃拉托色尼筛法

def is_prime(n):if n % 2 0 and n ! 2:return Falsefor i in range(3, int(math.sqrt(n) 1)):if n % i 0:return Falsereturn n ! 1def eratosthenes(n):primes []is_prime [True] * (n 1)for i in range(2, n1):if is_prime[i]:primes.append(i)# 用当前素数i去筛掉所有…

知乎禁止转载的回答怎么复制做笔记?

问题 对于“禁止转载”的回答&#xff0c;右键复制是不行的&#xff0c;ctrl-c也不行&#xff0c;粘贴之后都是当前回答的标题。稍微看了代码&#xff0c;应该是对copy事件进行了处理。不过这样真的有用吗&#xff0c;真是防君子不防小人&#xff0c;只是给收集资料增加了许多…

Spring Security 6.x 系列(7)—— 源码分析之建造者模式

一、建造者模式 WebSecurity、HttpSecurity、AuthenticationManagerBuilder 都是框架中的构建者&#xff0c;把他们放到一起看看他们的共同特点&#xff1a; 查看AuthenticationManagerBuilder的继承结构图&#xff1a; 查看HttpSecurity的继承结构图&#xff1a; 查看WebSec…