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

一、堆介绍

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;
    
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《opencv实用探索·五》opencv小白也能看懂的图像腐蚀

1、图像腐蚀原理简单理解&#xff1a; 腐蚀是形态学最基本的操作&#xff0c;都是针对白色部分&#xff08;高亮部分&#xff09;而言的。即原图像中高亮部分被蚕食&#xff0c;得到比原图更小的区域。 2、图像腐蚀的作用&#xff1a; &#xff08;1&#xff09;去掉毛刺&…

【软件测试】白盒测试和黑盒测试

一、软件测试基本分类 一般地&#xff0c;我们将软件测试活动分为以下几类&#xff1a;黑盒测试、白盒测试、静态测试、动态测试、手动测试、自动测试等等。 黑盒测试 黑盒测试又叫功能测试、数据驱动测试或给予需求规格说明书的功能测试。这种测试注重于测试软件的功能性需…

基数排序及利用数组简化解题

红豆不堪看&#xff0c;满眼相思泪 本文主要是帮助大家熟练掌握利用数组进行有关判断的题目&#xff0c;看完本文后在之后的刷题中都可以利用这种思想&#xff0c;当然举例中的题目利用该种方法可能不是最优解&#xff0c;但绝对是你看到题目不用思考太多就可以做出来的方法&am…

Python入门06布尔值

目录 1 什么是布尔值2 怎么生成布尔值3 在控制程序中使用布尔值4 数据过滤、排序和其他高级操作总结 1 什么是布尔值 首先我们要学习一下布尔值的定义&#xff0c;布尔值是一种数据类型&#xff0c;它只有两个可能的值&#xff1a;True&#xff08;真&#xff09;或 False&…

悠络客受邀出席2023上海区域零售(餐饮)数字化运营实战沙龙研讨会

11月23日&#xff0c;由中国零售&#xff08;餐饮&#xff09;CIO俱乐部、《智慧零售与餐饮》主办的2023上海区域零售&#xff08;餐饮&#xff09;数字化运营实战沙龙研讨会在上海召开&#xff0c;悠络客合伙人兼销售副总裁张勇作为演讲嘉宾受邀出席了本次大会。 本次研讨会汇…

【紫光同创PCIE教程】——使用官方驱动在Windows下进行DMA读写操作/PIO读写操作

本原创教程由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处&#xff08;www.meyesemi.com) 紫光同创官方主推的是在linux系统下开发驱动和上层软件&#xff0c;相应地&#xff0c;官方提供了在linux一个基于GTK2…

Python链式调用技巧:代码流畅无缝连接

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 链式调用是一种编程风格&#xff0c;它允许将多个方法调用连接在一起&#xff0c;形成一个连贯的操作链。在Python中&#xff0c;链式调用常常用于使代码更简洁、易读&#xff0c;尤其在处理数据处理和函数式编程…

AntDB“超融合+流式实时数仓”——打造分布式数据库新纪元

&#xff08;一&#xff09; 前言 据统计&#xff0c;在信息化时代的今天&#xff0c;人们一天所接触到的信息量&#xff0c;是古人一辈子所能接收到的信息量的总和。当今社会中除了信息量“多”以外&#xff0c;人们对信息处理的“效率”和“速度”的要求也越来越高。譬如&a…

二叉树题目:祖父结点值为偶数的结点和

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;祖父结点值为偶数的结点和 出处&#xff1a;1315. 祖父结点值为偶数的结点和 难度 5 级 题目描述 要求 给定二…

笔记二十六、React中路由懒加载的扩展使用

26.1 在路由中配置懒加载 lazy routes/index.jsx 代码 import {Navigate} from "react-router-dom"; import Home from "../components/Home"; import About from "../components/About"; // import Classify from "../components/Home/c…

VirtualBox 7.0.8(虚拟机软件)

VirtualBox是一款开源的虚拟机软件&#xff0c;它是使用Qt编写&#xff0c;在Sun被Oracle收购后正式更名成Oracle VM VirtualBox。它可以在VirtualBox上安装并且执行Solaris、Windows、DOS、Linux、OS/2 Warp、BSD等系统作为客户端操作系统。使用者可以在VirtualBox上安装并且运…

对话汪源:数智时代为企业构建新的竞争力,和网易数帆的“为与不为”

CodeWave在内的诸多“主演”正在重新演绎网易数帆&#xff0c;在网易数帆的新故事里&#xff0c;做专业、底层、核心的工具&#xff0c;是其成长至今最核心的底色。 作者|斗斗 编辑|皮爷 出品|产业家 “我希望在中间层能构建一个好的生态。”网易汪源的这句话&#xff0c;让…

【Openstack Train安装】六、Keystone安装

OpenStack是一个云计算平台的项目&#xff0c;其中Keystone是一个身份认证服务组件&#xff0c;它提供了认证、授权和目录的服务。其他OpenStack服务组件都需要使用Keystone来验证用户的身份和权限&#xff0c;并且彼此之间需要相互协作。当一个OpenStack服务组件接收到用户的请…

五、shell - 算术运算符

目录 1、简介 2、例子 ​​​​​​​1、简介 Shell 和其他编程一样&#xff0c;支持包括&#xff1a;算术、关系、布尔、字符串等运算符。原生 bash 不支持简单的数学运算&#xff0c;但是可以通过其他命令来实现&#xff0c;例如expr。expr 是一款表达式计算工具&#xff…

人工智能的技术研究与安全问题的深入讨论

人工智能&#xff08;AI&#xff09;作为当今世界技术领域的热门话题&#xff0c;吸引了广泛的关注和研究。本文将探讨人工智能技术的最新研究进展&#xff0c;并着重讨论与人工智能安全相关的问题。 引言 人工智能技术的迅速发展为我们的生活带来了翻天覆地的变化。随着科技的…

12月7-8日泰国曼谷,Flat Ads与你相约Affilliate World Asia

12月7-8日,Flat Ads将参加在泰国曼谷举办的Affiliate World Asia Conference,与众多行业人士共话全球流量领域新洞察,探讨行业现状与未来趋势。 据悉,Affiliate World Asia(以下简称AWA)是全球瞩目的移动互联网联盟超级盛会,也是亚洲区域内最大规模的互联网流量大会。这一展会为…

无公网IP环境如何远程访问本地内网搭建的Emby影音库服务器

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力应用场景之一&…

发朋友圈的重要性和黄金时间

一、为什么发朋友圈要选择时间发&#xff1f; 1. 增加曝光率&#xff1a;通过定时自动发朋友圈&#xff0c;可以让更多的人看到你的动态。 2. 提高互动率&#xff1a;定时自动发朋友圈可以保持你的朋友圈活跃度&#xff0c;提高与粉丝的互动率。 3. 增强品牌形象&#xff1a…

LeetCode(44)存在重复元素 II【哈希表】【简单】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 存在重复元素 II 1.题目 给你一个整数数组 nums 和一个整数 k &#xff0c;判断数组中是否存在两个 不同的索引 i 和 j &#xff0c;满足 nums[i] nums[j] 且 abs(i - j) < k 。如果存在&#xff0c;返回 true &#xf…

Egg.js的方法扩展

Extend-application 方法扩展 eggjs的方法的扩展和编写 Egg.js可以对内部的五种对象进行扩展&#xff0c;以下是可扩展的对象、说明、this指向和使用方式。 application对象方法拓展 按照Egg的约定&#xff0c;扩展的文件夹和文件的名字必须是固定的。比如要对application扩…