数据结构--堆 Heap

文章目录

    • 1. 概念
    • 2. 操作和存储
      • 2.1 插入一个元素
      • 2.2 删除堆顶元素
    • 3. 堆排序(不稳定排序)
      • 3.1 建堆
      • 3.2 排序
      • 3.3 思考:为什么快速排序要比堆排序性能好?两者都是O(nlogn)
    • 4. 堆应用
      • 4.1 优先级队列
      • 4.2 用堆求 Top K(前K大数据)
      • 4.3 求中位数
      • 4.4 思考:海量关键词搜索记录,求topK

1. 概念

  • 堆是一种特殊的树
    a. 堆是完全二叉树(除最后一层,其他层都是满的,最后一层节点都靠左)
    b. 每一个节点都大于等于(或者都小于等于)其子树中每个节点的值
    在这里插入图片描述

2. 操作和存储

  • 完全二叉树适合用数组存储,节省空间(不需要左右指针)
    在这里插入图片描述

2.1 插入一个元素

在这里插入图片描述

2.2 删除堆顶元素

在这里插入图片描述

/*** @description: 堆* @author: michael ming* @date: 2019/5/26 22:22* @modified by: */
#include <iostream>
#include <limits.h>
using namespace std;
class MinHeap
{int *arr;int capacity;int heap_size;
public:MinHeap(int cap){heap_size = 0;capacity = cap;arr = new int [capacity];}~MinHeap(){delete [] arr;}int heapsize(){return heap_size;}bool full(){return capacity == heap_size;}void MinHeapify(int i){Heapify(i,heap_size);}void Heapify(int i, int size){int l = left(i), r = right(i);int min = i;if(l < size && arr[l] < arr[i])min = l;if(r < size && arr[r] < arr[min])min = r;if(min != i){swap(arr[i], arr[min]);Heapify(min,size);}}int parent(int i){return (i-1)/2;}int left(int i){return 2*i+1;}int right(int i){return 2*i+2;}void delMin(){if(heap_size <= 0)return;arr[0] = arr[heap_size-1];heap_size--;MinHeapify(0);}int getMin(){return arr[0];}void insert(int val){if(heap_size == capacity){cout << "overflow!" << endl;return;}heap_size++;int i = heap_size-1;arr[i] = val;while(i > 0 && arr[parent(i)] > arr[i]){swap(arr[parent(i)], arr[i]);i = parent(i);}}void sort(){int size = heap_size;for(int i = heap_size-1; i >= 0; --i){swap(arr[i], arr[0]);Heapify(0,--size);}}void print(){for(int i = 0; i < heap_size; ++i)cout << arr[i] << " ";}
};
int main()
{MinHeap minheap(8);minheap.insert(6);minheap.insert(8);minheap.insert(12);minheap.insert(4);minheap.insert(15);minheap.insert(0);minheap.insert(5);minheap.insert(9);minheap.insert(10);minheap.delMin();cout << minheap.getMin() << endl;return 0;
}

3. 堆排序(不稳定排序)

3.1 建堆

  • 方法1:一个一个的插入这种方式
  • 方法2:从倒数第一个有叶子节点的节点开始,检查其子节点是否满足堆,依次往前,直到堆顶,建堆的复杂度O(n)
    在这里插入图片描述

3.2 排序

  • 建堆结束后,最大元素在堆顶,与最后一个元素交换(不稳定),然后对剩余的 n-1 个元素重新构建堆,重复这个过程,最后只剩1个元素,排序结束,建堆复杂度O(nlogn)
    在这里插入图片描述
    堆排序代码见:https://blog.csdn.net/qq_21201267/article/details/80993672#t7

3.3 思考:为什么快速排序要比堆排序性能好?两者都是O(nlogn)

  1. 快排数据访问方式比较友好。快排访问数据是顺序访问,堆排序是跳着访问,这样对CPU缓存是不友好的
  2. 同样的数据,堆排序中数据交换次数多余快排。快排的交换次数不会比逆序度多;堆排序第一步建堆,打乱了数据原有顺序,数据有序度降低,交换次数变多

4. 堆应用

4.1 优先级队列

  • 优先级队列用堆实现是最直接、最高效的。优先出队,就是堆顶取出
    a. 合并有序小文件
    把多个有序的小文件的第一个元素取出,放入堆中,取出堆顶到大文件,然后再从小文件中取出一个加入到堆,这样就把小文件的元素合并到大文件中了。
/*** @description: 合并有序小文件* @author: michael ming* @date: 2019/5/29 22:10* @modified by: */
#include "heap.cpp"
int main()
{int file0[10] = {11, 21, 31, 41, 51, 61, 71, 81, 91, 101};int file1[8] = {1, 2, 3, 4, 5, 6, 7, 80};int file2[9] = {13, 23, 33, 43, 53, 63, 73, 83, 93};int file3[10] = {12, 22, 32, 42, 52, 62, 72, 82, 92, 102};int file4[7] = {15, 25, 35, 45, 55, 65, 72};int len0 = 10, len1 = 8, len2 = 9, len3 = 10, len4 = 7;int i0, i1, i2, i3, i4, j;i0 = i1 = i2 = i3 = i4 = j = 0;const int new_len = len0+len1+len2+len3+len4;int bigFile[new_len];MinHeap intheap(5);intheap.insert(file0[i0]);intheap.insert(file1[i1]);intheap.insert(file2[i2]);intheap.insert(file3[i3]);intheap.insert(file4[i4]);int top;while(intheap.heapsize()){top = intheap.getMin();bigFile[j++] = top;intheap.delMin();if(i0 < len0-1 && top == file0[i0])   //可以用list做,就不用查找最小的是哪个文件的{intheap.insert(file0[++i0]);}else if(i1 < len1-1 && top == file1[i1]){intheap.insert(file1[++i1]);}else if(i2 < len2-1 && top == file2[i2]){intheap.insert(file2[++i2]);}else if(i3 < len3-1 && top == file3[i3]){intheap.insert(file3[++i3]);}else if(i4 < len4-1 && top == file4[i4]){intheap.insert(file4[++i4]);}}for(i0 = 1, j = 0; j < new_len; i0++,++j){cout << bigFile[j] << " ";if(i0%10 == 0)cout << endl;}return 0;
}

在这里插入图片描述
b. 高性能定时器
多个定时器,需要每隔很短的时间(比如1秒)扫描一遍,查询哪个任务时间到了,需要开始执行,这样有时候很多扫描是徒劳的,如果任务列表很长,扫描很耗时。采用小顶堆,最先执行的放在堆顶,就只需要在堆顶时间到时执行堆顶任务即可,避免无谓的扫描。

4.2 用堆求 Top K(前K大数据)

a. 针对静态数据(数据不变)
建立大小为K的小顶堆,遍历数组,数组元素与堆顶比较,比堆顶大,就把堆顶删除,并插入该元素到堆
b. 针对动态数据(数据不断插入更新的)
在动态数据插入的时候就与堆顶比较,看是否入堆,始终维护这个堆,需要的时候直接返回,最坏O(n*lgK)

/*** @description: 用堆求最大的k个元素* @author: michael ming* @date: 2019/5/30 0:06* @modified by: */
#include "heap.cpp"
int main()
{int i = 0;const int len = 10;int data[len] = {2, 8, 1, 7, 12, 24, 6, 10, 90, 100};MinHeap intheap(5);//求前5大元素while(!intheap.full()){if(i < len)intheap.insert(data[i++]);}while(i < len){if(data[i] > intheap.getMin()){intheap.delMin();intheap.insert(data[i]);}i++;}intheap.sort();intheap.print();return 0;
}

在这里插入图片描述

4.3 求中位数

静态数据:先排序,中间位置的数就是中位数
动态数据:动态变化,中位数位置总在变动,每次都排序的话,效率很低,借助堆可以高效的解决。
在这里插入图片描述
插入数据到某个堆里后,两个堆数据量(应相等或者大堆比小堆多1)若不满足括号要求,则将某个堆的堆顶元素移动到另一个堆,直到满足括号要求,堆化复杂度O(lgn),大堆的堆顶就是中位数,求中位数复杂度O(1)。

同理,可以求99%响应时间(就是大于前面99%数据的那个数据)
跟上面过程类似,只是大堆中保存 n x 0.99 个数据,小堆存 n x 0.01 个数据

/*** @description: 利用大小两个堆求中位数* @author: michael ming* @date: 2019/5/30 20:37* @modified by: */
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
void printVec(vector<int> v)
{for (int i = 0; i < v.size(); ++i)cout << v[i] << " ";cout << endl;
}
int main()
{const int len = 7;int staticArr[len] = {7,-1,9,0,8,77,24};//-1,0,7,*8*,9,24,77vector<int> maxheap, minheap;for(int i = 0; i < len; ++i){if(maxheap.empty()){maxheap.push_back(staticArr[i]);continue;}//----选择插入哪个堆-----if(!maxheap.empty() && staticArr[i] <= maxheap[0]){maxheap.push_back(staticArr[i]);push_heap(maxheap.begin(),maxheap.end());//默认采用 < , 大堆}else if(!maxheap.empty() && staticArr[i] > maxheap[0]){minheap.push_back(staticArr[i]);push_heap(minheap.begin(),minheap.end(),greater<int>());//小堆,采用 >}//----平衡两个堆的节点比列----if(maxheap.size() > minheap.size() && maxheap.size() - minheap.size() > 1){minheap.push_back(maxheap[0]);//大堆顶进入小堆push_heap(minheap.begin(),minheap.end(),greater<int>());pop_heap(maxheap.begin(),maxheap.end());//堆顶到末尾了maxheap.pop_back();//删除到末尾的"堆顶"}else if(maxheap.size() < minheap.size()){maxheap.push_back(minheap[0]);push_heap(maxheap.begin(),maxheap.end());//默认采用 < , 大堆pop_heap(minheap.begin(),minheap.end(),greater<int>());minheap.pop_back();}}if(maxheap.size())cout << "中位数为:" << maxheap[0] << endl;return 0;
}

stl heap操作:http://www.cplusplus.com/reference/algorithm/pop_heap/
在这里插入图片描述
对上面程序稍加改造,对动态数据进行求解中位数

/*** @description: 中位数求解,利用大小堆,动态数据插入* @author: michael ming* @date: 2019/5/30 23:13* @modified by: */
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
void printVec(vector<int> v)
{for (int i = 0; i < v.size(); ++i)cout << v[i] << " ";cout << endl;
}
int main()
{int num;vector<int> maxheap, minheap, allnum;while(cin >> num){allnum.push_back(num);if(maxheap.empty()){maxheap.push_back(num);cout << "排序后的数组:" << endl;printVec(allnum);cout << "中位数为:" << maxheap[0] << endl;continue;}//----选择插入哪个堆-----if(!maxheap.empty() && num <= maxheap[0]){maxheap.push_back(num);push_heap(maxheap.begin(),maxheap.end());//默认采用 < , 大堆}else if(!maxheap.empty() && num > maxheap[0]){minheap.push_back(num);push_heap(minheap.begin(),minheap.end(),greater<int>());//小堆,采用 >}//----平衡两个堆的节点比列----if(maxheap.size() > minheap.size() && maxheap.size() - minheap.size() > 1){minheap.push_back(maxheap[0]);//大堆顶进入小堆push_heap(minheap.begin(),minheap.end(),greater<int>());pop_heap(maxheap.begin(),maxheap.end());//堆顶到末尾了maxheap.pop_back();//删除到末尾的"堆顶"}else if(maxheap.size() < minheap.size()){maxheap.push_back(minheap[0]);push_heap(maxheap.begin(),maxheap.end());//默认采用 < , 大堆pop_heap(minheap.begin(),minheap.end(),greater<int>());minheap.pop_back();}sort(allnum.begin(),allnum.end());cout << "排序后的数组:" << endl;printVec(allnum);if(maxheap.size())cout << "中位数为:" << maxheap[0] << endl;}return 0;
}

在这里插入图片描述

4.4 思考:海量关键词搜索记录,求topK

  • 用散列表去重,并累加搜索次数
  • 再建立大小为K的小顶堆,遍历散列表,次数大于堆顶的,顶替堆顶入堆
  • (以上在散列表很大时,需要内存超过单机内存,怎么办?)
  • 建立n个空文件,对搜索关键词求哈希值,哈希值对n取模,得到该关键词被分到的文件号(0到n-1)
  • 对每个文件,利用散列和堆,分别求出topK,然后把n个topK(比如10个Top 20,200很小了吧)放在一起,出现次数最多的K(20)个关键词就是这海量数据里搜索最频繁的。

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

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

相关文章

金融时报:人工智能在银行中的应用—对全球30家大型银行的调查

原文地址&#xff1a;https://cloud.tencent.com/developer/article/1144829 &#xff08;微信公众号 点滴科技资讯&#xff09;尽管银行业对新技术感到兴奋&#xff0c;但仍采取比较谨慎的方式。德意志银行首席执行官约翰•克莱恩&#xff08;John Cryan&#xff09;曾经提出将…

百度Java三面:现场面试39题目实拍含答案!

百度一面&#xff08;现场&#xff09; 自我介绍 Java中的多态 为什么要同时重写hashcode和equals Hashmap的原理 Hashmap如何变线程安全&#xff0c;每种方式的优缺点 垃圾回收机制 Jvm的参数你知道的说一下 设计模式了解的说一下啊 手撕一个单例模式 算法题目 手撕算…

CCKS2020事理图谱应用工作:刘焕勇等.面向开放文本的逻辑推理知识抽取与事件影响推理探索

一、背景介绍 第十四届全国知识图谱与语义计算大会(CCKS 2020) 11 月 12 日至 15 日在江西南昌举行&#xff0c;CCKS&#xff08;China Conference on Knowledge Graph and Semantic Computing&#xff09;是由中国中文信息学会语言与知识计算专委会定期举办的全国年度学术会议…

ACL20 Best Paper揭晓!NLP模型评价体系或将迎来重大转折

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 北大小才女小轶昨晚ACL2020的Main Conference落下帷幕&#xff0c;今年的最佳论文颁给了这篇《Beyond Accuracy: Behavioral Testing of NLP Models with CHECKLIST》。在ACL录用的778篇论文中&#xff…

论文浅尝 | 基于多模态关联数据嵌入的知识库补全

链接&#xff1a;https://arxiv.org/pdf/1809.01341.pdf动机&#xff08;摘要&#xff09;当前的知识库补全的方法主要是将实体和关系嵌入到一个低维的向量空间&#xff0c;但是却只利用了知识库中的三元组结构 (<s,r,o>) 数据&#xff0c;而忽略了知识库中大量存在的文本…

智能投顾原理与主流产品分析

原作者 王希&#xff0c;CFA&#xff0c;中国光大银行。核心观点&#xff1a;1、智能投顾的模式是通过技术实现财富管理的流程自动化&#xff0c;为客户定制FOF产品来投资并赚取管理费。目前尚未看出大数据分析、人工智能等技术在其中发挥出关键作用。2、智能投顾在美国的快速…

POJ 1442 Black Box(大小堆,求第K小的元素)

文章目录1. 题目链接2. 题目解读3. 代码3.1 Runtime Error 代码1. 题目链接 http://poj.org/problem?id1442 2. 题目解读 可以利用大小堆&#xff0c;大堆长度从1开始&#xff0c;每次1 大堆元素都比小堆的小&#xff0c;那么大堆顶的元素就是第k小的元素 3. 代码 3.1 Run…

阿里java架构师面试128题含答案:分布式架构+Dubbo+多线程+Redis

一、Java基础和高级 1.String类为什么是final的。 2.HashMap的源码&#xff0c;实现原理&#xff0c;底层结构。 3.反射中&#xff0c;Class.forName和classloader的区别 4.session和cookie的区别和联系&#xff0c;session的生命周期&#xff0c;多个服务部署时session管理…

LightGBM——提升机器算法(图解+理论+安装方法+python代码)

原文地址&#xff1a;https://blog.csdn.net/huacha__/article/details/81057150 前言 LightGBM是个快速的&#xff0c;分布式的&#xff0c;高性能的基于决策树算法的梯度提升框架。可用于排序&#xff0c;分类&#xff0c;回归以及很多其他的机器学习任务中。 在竞赛题中&am…

这个NLP工具,玩得根本停不下来

今天推荐一个有趣的自然语言处理公众号AINLP&#xff0c;关注后玩得根本停不下来&#xff01;AINLP的维护者是我爱自然语言处理&#xff08;52nlp&#xff09;博主&#xff0c;他之前在腾讯从事NLP相关的研发工作&#xff0c;目前在一家创业公司带技术团队。AINLP公众号的定位是…

论文浅尝 | 基于Universal Schema与Memory Network的知识+文本问答

来源&#xff1a;ACL 2017链接&#xff1a;http://aclweb.org/anthology/P17-2057本文提出将 Universal schema 用于自然语言问答中&#xff0c;通过引入记忆网络&#xff0c;将知识库与文本中大量的事实信息结合起来&#xff0c;构建出一个由问答对&#xff08;question-answe…

数据结构--图 Graph

文章目录1. 概念2. 存储方法2.1 邻接矩阵 Adjacency Matrix2.2 邻接表 Adjacency List3. 图的遍历3.1 广度优先搜索BFS&#xff08;Breadth First Search&#xff09;3.2 BFS代码&#xff08;基于邻接表&#xff09;3.3 深度优先搜索DFS&#xff08;Depth First Search&#xf…

2019最新拼多多Java面试题:幻影读+分段锁+死锁+Spring Cloud+秒杀

拼多多Java一面 简短自我介绍 事务的ACID&#xff0c;其中把事务的隔离性详细解释一遍 脏读、幻影读、不可重复读 红黑树、二叉树的算法 平常用到哪些集合类&#xff1f;ArrayList和LinkedList区别&#xff1f;HashMap内部数据结构&#xff1f;ConcurrentHashMap分段锁&…

视频问答兴起,多跳问答热度衰退,92篇论文看智能问答的发展趋势

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 舒意恒&#xff08;南京大学硕士生&#xff0c;知识图谱方向&#xff09;编 | 北大小才女小轶2019年的时候&#xff0c;舒意恒Y.Shu整理了一份《2019年&#xff0c;智能问答有哪些研究方向&#xff1f;…

论文浅尝 | 知识图谱相关实体搜索

本文转载自公众号&#xff1a;南大Websoft。相关搜索&#xff08;Relevance Search&#xff09;是信息检索中的一个经典问题&#xff0c;相关搜索是指给定一个查询实体&#xff0c;返回与其相关度最高的实体&#xff08;一个类似的问题Similarity Search&#xff0c;一般来说指…

最新美团Java面试题目(共3面)

一面 线程池用过哪些&#xff0c;线程池有哪些参数&#xff0c;然后问我几个常用线程池的用法和实际场景问题。 集合框架的知识&#xff0c;hashmap&#xff0c;ArrayList&#xff0c;LinkedList源码相关知识&#xff0c;基本整个介绍了一遍&#xff0c;与hastable&#xff0c…

PersonGraphDataSet近十万的开放人物关系图谱项目

PersonGraphDataSet PersonGraphDataSet, nearly 10 thousand person2person relationship facts that build from extraction method, which can be applied to person kg search and inference applications。 人物图谱数据集&#xff0c;近十万的人物关系图谱事实数据库&am…

图Graph--寻找二度好友(BFS应用)

社交网络可以用图来表示&#xff08;查阅图的概念&#xff09;。 寻找二度好友&#xff0c;这个问题就非常适合用图的广度优先搜索BFS算法来解决&#xff0c;因为广度优先搜索是层层往外推进的。 首先&#xff0c;遍历与起始顶点最近的一层顶点&#xff0c;也就是用户的一度好…

技术动态 | TechKG:一个面向中文学术领域的大型知识图谱

作者&#xff1a;东北大学-知识图谱研究组 任飞亮TechKG 是一个面向中文、面向学术、多领域的大型知识图谱知识库&#xff0c;知识库由“东北大学-知识图谱研究组”开发完成。和已有知识图谱如 Freebase 或 YAGO 相比&#xff0c;TechKG 具有如下主要特点&#xff1a;1、是一个…

技术总结:图算法、开源工具及其在工业界的应用场景概述

知识图谱本质上是一种图结构&#xff0c;在图内部数据规模大且质量高、外部算力足够的情况下&#xff0c;充分利用好图算法&#xff0c;能够最大程度地发挥出其数据价值。实际上&#xff0c;图&#xff08;Graph&#xff09;是一个常见的数据结构&#xff0c;现实世界中有很多很…