问题来源

此题来自于Hackerrank中的QHEAP1问题,考查了对堆结构的充分理解。成功完成此题,对最大堆或者最小堆的基本操作实现就没什么太大问题了。

问题简述

实现一个最小堆,对3种类型的输入能给出正确的操作:

  • “1 v” - 表示往堆中增加一个值为v的元素
  • “2 v” - 表示删去堆中值为v的元素
  • “3” - 表示打印出堆中最小的那个元素

注意:题目保证了要删的元素必然是在堆中的,并且在任何时刻,堆中不会有重复的元素。
输入格式:
第1行的值表示共有q个操作,然后再接下来的q行中,每行都有上述3中操作中的任意一种。
比如:

******************输入*******************
5
1 4
1 9
3
2 4
3
******************输出*******************
4
9

问题分析

对于一个最小堆来说,其满足的性质是只要每个子树中的父亲节点的元素小于其左孩子节点和右孩子节点的元素即可,比如下图所示的这样:


图1:最小堆示例图
没错,最小堆根部的元素必然是树中最小的那个元素。除了满足上述的条件之外,最小堆还必须是一颗 完全二叉树。也正是由于这个完全二叉树的性质,最小堆是可以用数组来实现的,比如上图的这个最小堆就可以表示成

data = { 5, 8, 20, 10, 35, 12, 50, 15, 16 };

从树结构中不难看出索引之间有关系

⎧⎩⎨leftChild=2⋅parent+1rightChild=2⋅parent+2parent=(child−1)/2\begin{cases}leftChild = 2 \cdot parent + 1 \\ rightChild = 2 \cdot parent + 2 \\ parent = (child - 1) / 2\end{cases}
这是我们在更新堆信息时最重要的公式,第3个式子的除法是取整的,所以左右孩子都一样。
如果只需要满足操作”1 v”和操作”3”的话,上述这些就已经完全够用了,难点在于这里需要我们对堆中指定的元素进行删除”2 v”。讲道理,这并不是一个最小堆所应该有的操作,最小堆只要管住最小的那个值就好了,其他的结构怎么样,最小堆并不关心。不过,既然题目故意这么出了,要来刁难我们,我们也只能迎难而上了。
借助于索引堆的想法,我们用一个哈希表来记录每一个元素在堆中的索引位置,这样,我们在删除时只需要 O(1)O(1)的复杂度就可以找到要删除的元素了,而删除的过程是 O(log(n))O(log(n))的复杂度。
还是以上面那组数据为例,我们希望记录的是如下的信息。

data = { 5, 8, 20, 10, 35, 12, 50, 15, 16 };
mp = {{20, 2}, {35, 4}, {8, 1}, {5, 0}, {16, 8}, {50, 6}, {12, 5}, {10, 3}, {15, 7}};

哈希表中元素的顺序完全无所谓,只要元素中的对应关系正确即可,所以上面的这个是我乱编的,具体的顺序和插入元素的顺序有关系。

代码展示

最后,来展示一下完整的代码,把下面这个代码直接复制粘贴到题目中去Submit是没问题的。

#include <vector>
#include <iostream>
#include <unordered_map>using namespace std;class myHeap{
private://作为堆的数组vector<int> data;//用于存放<元素值,在堆中的索引>的哈希表unordered_map<int, int> mp;//堆中元素的个数int size;private://上移操作,将元素小的顺着树结构往上移void _shiftUp(int index){if (index >= size || index < 0)return;while (index != 0){int newIndex = (index - 1) / 2;if (data[newIndex] > data[index]){//更新哈希表中存放的索引mp[data[newIndex]] = index;mp[data[index]] = newIndex;//更新堆中元素的位置swap(data[newIndex], data[index]);index = newIndex;}elsebreak;}return;}//下移操作,将元素大的顺着树结构往下移void _shiftDown(int index){if (index >= size || index < 0)return;while (index * 2 + 1 < size){int newIndex = index * 2 + 1;//选择左节点和右节点中比较小的那个元素if (newIndex + 1 < size && data[newIndex + 1] < data[newIndex])newIndex++;if (data[newIndex] > data[index])break;//更新哈希表中存放的索引mp[data[newIndex]] = index;mp[data[index]] = newIndex;//更新堆中元素的位置swap(data[newIndex], data[index]);index = newIndex;}return;}public:myHeap(){size = 0;}//添加元素void add(int d){data.push_back(d);mp[d] = size++;_shiftUp(mp[d]);}//删除元素void del(int d){int index = mp[d];mp[d] = size - 1;mp[data[size - 1]] = index;swap(data[index], data[size - 1]);size--;data.pop_back();_shiftUp(index);_shiftDown(index);mp.erase(d);}//打印堆中最小值void showMin(){cout << data[0] << endl;}
};int main() {/* Enter your code here. Read input from STDIN. Print output to STDOUT */int q;cin >> q;myHeap h;for (int i = 0; i < q; i++){int index;cin >> index;if (index == 1){int v;cin >> v;h.add(v);}else if (index == 2){int v;cin >> v;h.del(v);}else if (index == 3){h.showMin();}}
}

如有不足,还请指正~

基于哈希表的索引堆变形(Hackerrank: QHEAP1)相关推荐

  1. HashMap - 基于哈希表和 Map 接口的键值对利器 (JDK 1.7)

    HashMap 的一些整理: (JDK 1.7) 基于哈希表的Map接口的非同步实现,定义了键映射到值的规则 此实现提供所有可选的映射操作,并允许使用null值和null键 此实现假定哈希函数将元素适 ...

  2. python 字典 哈希_Python 字典和集合基于哈希表实现

    哈希表作为基础数据结构我不多说,有兴趣的可以百度,或者等我出一篇博客来细谈哈希表.我这里就简单讲讲:哈希表不过就是一个定长数组,元素找位置,遇到哈希冲突则利用 hash 算法解决找另一个位置,如果数组 ...

  3. 基础数据结构(二):字典树、并查集、堆、哈希表、字符串的哈希方式、STL的常见容器及其接口

    文章目录 一.字典树Trie 1 原理 2 Trie字符串统计 3 [LeetCode 208. 实现 Trie (前缀树)](https://leetcode-cn.com/problems/imp ...

  4. stl 基于哈希的map c++_关于哈希表,你该了解这些!

    (给算法爱好者加星标,修炼编程内功) 来源:代码随想录(本文来自作者投稿) 哈希表 首先什么是 哈希表,哈希表(英文名字为Hash table,国内也有一些算法书籍翻译为散列表,大家看到这两个名称知道 ...

  5. 【从蛋壳到满天飞】JS 数据结构解析和算法实现-哈希表

    前言 [从蛋壳到满天飞]JS 数据结构解析和算法实现,全部文章大概的内容如下: Arrays(数组).Stacks(栈).Queues(队列).LinkedList(链表).Recursion(递归思 ...

  6. c ++中哈希表如何访问_C / C ++中的哈希表–完整的实现

    c ++中哈希表如何访问 A Hash Table in C/C++ (Associative array) is a data structure that maps keys to values. ...

  7. 什么是BST?什么是哈希表?一文带你了解并实现查找的基础知识

      本文的全部代码均已上传Gitee MySearching~Star一下再走把: 文章目录 一.查找的基础概念 1.静态查找表: 2.动态查找表: 3.查找结构 二.顺序表查找 1.顺序表查找算法 ...

  8. PHP内核中的哈希表结构

    https://github.com/HonestQiao/tipi/commit/17ca680289e490763a6a402f79afa2a13802bb36 下载:https://github ...

  9. 数据结构 — 哈希表

    目录 文章目录 目录 哈希表 哈希表 哈希表,又称为散列表,是根据键值对(Key/Value)进行访问的数据结构,它让 Value 经过哈希函数的转换映射到哈希表对应的位置上,查找效率非常高.哈希索引 ...

最新文章

  1. GIL(全局解释器锁)与互斥锁
  2. 【03-14】日常资源访问备份
  3. STL 的string类怎么啦?
  4. 创建和应用Java包文件的两种方式(转)
  5. ehcache使用_Mybatis整合(Redis、Ehcache)实现二级缓存,恕我直言,你不会
  6. 计算机数学基础 视频讲解,计算机数学基础课件
  7. Red Hat Linux 253 实验部分
  8. [快速入门]Spring Boot+springfox-swagger2 之RESTful API自动生成和测试
  9. 财务报表越做越丑?这些秒杀Excel的可视化工具,人人都能用
  10. Python和Ruby两大语言全方位对比
  11. 物 理 学 简 介(三)
  12. 【光纤传输特性】图文并茂,你该了解这些
  13. pipe 半双工_linux进程间通信之管道(无名管道pipe)实现全双工双向通信
  14. MySQL中建表时的int(m)中的m代表什么意思?
  15. PTC Creo 5.0.2.0 + HelpCenter Win64 中文破解版
  16. 67键键盘如何输出`和~符号(一百五十一)
  17. Java高并发之魂:synchronized深度解析
  18. 阿里云校验用户身份证信息是否是真实有效,姓名和身份证号是参数
  19. 【Vue 2.0】用Vue写一个自己的简历网站带作品集
  20. Idea同时打开多个窗口

热门文章

  1. n皇后问题java_经典n皇后问题java代码实现
  2. vuex webpack 配置_vue+webpack切换环境和打包之后服务器配置
  3. 【Python】模拟面试技术面试题答
  4. android竖直和横向,如何在android中为纵向和横向模式定义不同的控件
  5. 怎么卸载apowerrec_如何删除windows10自带应用
  6. 分布式事物(同样适用于dubbo事务等分布式事务)
  7. Web框架 Bottle 、Flask 、Tornado
  8. 《In Search of an Understandable Consensus Algorithm》翻译
  9. SkipList 以及高度的确定
  10. 编译rocksdb源码导致的部署失败