C++基础算法基于哈希表的索引堆变形

 更新时间:2021年10月12日 12:01:04   作者:zjuPeco  
这篇文章主要为大家介绍了C++基础算法,基于哈希表的索引堆变形示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步

问题来源

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

问题简述

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

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

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

******************输入*******************

1 4 
1 9 

2 4 

******************输出*******************

9

问题分析

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

最小堆

图1:最小堆示例图

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

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

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

这是我们在更新堆信息时最重要的公式,第3个式子的除法是取整的,所以左右孩子都一样。
如果只需要满足操作”1 v”和操作”3”的话,上述这些就已经完全够用了,难点在于这里需要我们对堆中指定的元素进行删除”2 v”。讲道理,这并不是一个最小堆所应该有的操作,最小堆只要管住最小的那个值就好了,其他的结构怎么样,最小堆并不关心。不过,既然题目故意这么出了,要来刁难我们,我们也只能迎难而上了。
借助于索引堆的想法,我们用一个哈希表来记录每一个元素在堆中的索引位置,这样,我们在删除时只需要 O(1) 的复杂度就可以找到要删除的元素了,而删除的过程是 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;
            }
            else
                break;
        }
        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();
        }
    }
}

如有不足,还请指正~

以上就是C++基础算法基于哈希表的索引堆变形的详细内容,更多关于C++算法哈希表索引堆变形的资料请关注脚本之家其它相关文章!

相关文章

  • 教你用C语言实现三子棋

    教你用C语言实现三子棋

    这篇文章主要为大家详细介绍了C语言实现简单三子棋程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • 使用C语言实现三子棋游戏

    使用C语言实现三子棋游戏

    这篇文章主要为大家详细介绍了使用C语言实现三子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • C语言实现用户态线程库案例

    C语言实现用户态线程库案例

    下面小编就为大家带来一篇C语言实现用户态线程库案例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • C++数据封装以及定义结构的详细讲解

    C++数据封装以及定义结构的详细讲解

    这篇文章主要详细讲解了C++数据封装以及定义结构,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • C语言线性表的链式表示及实现详解

    C语言线性表的链式表示及实现详解

    线性表的链式存储特点则是用一组任意的存储单元存储线性表的数据元素。这组存储单元既可以是连续的,也可以是不连续的。本文将详解一下C语言线性表的链式表示及实现,感兴趣的可以了解一下
    2022-07-07
  • C语言中的程序环境与预处理详情

    C语言中的程序环境与预处理详情

    这篇文章主要介绍了C语言中的程序环境与预处理详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-07-07
  • 详解C++ 拷贝构造函数和赋值运算符

    详解C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数、什么情况下调用赋值运算符。最后,简单的分析了下深拷贝和浅拷贝的问题。有需要的朋友可以看下
    2016-12-12
  • C语言编写学生成绩管理系统

    C语言编写学生成绩管理系统

    这篇文章主要为大家详细介绍了C语言编写学生成绩管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • 浅谈C++20新增内容

    浅谈C++20新增内容

    C++20 是 C++ 语言的一次重大更新,它引入了许多新特性,本文主要介绍了浅谈C++20新增内容,具有一定的参考价值,感兴趣的可以了解一下
    2025-04-04
  • 深入解析函数指针与返回函数的指针

    深入解析函数指针与返回函数的指针

    以下是对函数指针与返回函数的指针进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-07-07

最新评论