• 散列


    1、定义 

      散列(Hash,哈希)是一种对数据的处理方法,通过某种特定的算法将要检索的项与用来检索的索引(称为散列,或者散列值)关联起来,然后可以生成一种便于搜索的数据结构(称为散列表)。

      c++11中增加了获得hash值的方法,通过hash,hash是实现了operator()的类,所以其对象是函数对象:

        //template<class Key > struct hash;
        auto hashValue = std::hash<std::string>()("hello");

    2、散列的常见应用:

      散列常用作一种数据安全的方法,由一串数据中经过散列算法计算出来的资料指纹(data fingerprint),经常用来识别资料是否有被窜改,以保证资料确实是由原创者所提供。
      散列算法的另一用途是用来加密的密码,由于散列算法所计算出来的散列值(Hash Value)具有不可逆(无法逆向演算回原本的数值)的性质,因此可有效的保护密码。
      散列表就是使用散列函数将键名和键值关联起来的数据结构,c++中的unordered_map、unordered_set底层实现使用这种数据结构来保存数据。

    3、unordered_map

      unordered_map的基本原理:

       使用一个下标范围比较大的数组来存储元素。将每个键映射到0到数组大小这个范围中的某个数,将键值放到这个单元中存储,这个函数称为散列函数;散列函数具体来说就是使每个元素的键都与一个函数值(即数组下标)相对应,用这个数组单元(称为桶)来存储这个元素。但是,不能够保证每个元素的键与存储单元一一对应的,有可能出现对于不同的元素却计算出了相同的桶号,在这种情况下不同的元素被存储到了相同的数组单元中,这就称为“冲突(碰撞)”,所以还要有方法来处理这种情况,即为“解决冲突”,比如如果得到的哈希地址冲突(该位置上已存储数据)的话就将这个数据与原数据组成链表存放,如下图的B、D、F桶处所示:

       

      对于unordered_map插入元素的过程是:

       ①、通过hash函数得到key的hash值
       ②、得到桶号(一般为hash值对整个数组大小求模获得)
       ③、存放key和value在桶内。

      unordered_map查找元素的过程是:

       ①、通过hash函数得到key的hash值
       ②、得到桶号(一般为hash值对整个数组大小求模获得)
       ③、比较桶的内部元素是否与key相等,若都不相等,则没有找到,相等的话取出value值。
    根据unorder_map的查询和插入规则可知其查询、插入和删除操作不随数据量的增长而增大,时间复杂度是常数级别O(1)。

     4、map与unordered_map

      由于map内部使用的数据机构,所以map中元素会自动排序存放,unordered_map则不会。

      map是根据key自动排序的,对于key的类型必须支持<比较操作,所以对于自定义类型的元素应该增加operator<()函数,eg:

    #include <map>
    
    class CFoo
    {
        friend bool operator<(const CFoo& f1, const CFoo& f2);
        int m_iNum = 0;
    };
    bool operator<(const CFoo& f1, const CFoo& f2)
    {
        return f1.m_iNum < f2.m_iNum;
    }
    
    map<CFoo, int> m;
    m.insert(pair<CFoo, int>(CFoo(), 3));
    View Code

      unordered_map中key如果是自定义类型的话定义unordered_map的时候需要传入模板参数列表的第三个和第四个参数,分别用来指定散列函数和解决冲突时使用的判断是否相等,eg:

    #include <unordered_map>
    #include "boostfunctionalhash.hpp"
    class CFoo
    {
    public:
        int m_iNum = 0;
        string m_str;
    };
    struct foo_hash_value
    {
        size_t operator()(const CFoo& p)
        {
            //std::hash<int> h;
            //return h(p.m_iNum);
            size_t seed = 0;
            boost::hash_combine(seed, p.m_iNum);
            boost::hash_combine(seed, p.m_str);
            return seed;
        }
    };
    
    struct foo_is_equal
    {
        bool operator()(const CFoo& f1, const CFoo& f2)
        {
            return f1.m_iNum == f2.m_iNum;
        }
    };
    
    std::unordered_map<CFoo, int, foo_hash_value, foo_is_equal> m;
    View Code

    5、CRC与MD5、SHA-1

      CRC、MD5、SHA1都是通过对数据进行计算,来生成一个校验值,该校验值可以用来校验数据的完整性。

      CRC不是hash算法,而是采用多项式除法,其校验值的长度跟其多项式有关系,一般为16位(CRC16)或32位(CR32),其计算效率比MD5和SHA1要高很多,但其安全性相对于MD5和SHA1要弱很多,一般只用作通信数据的校验。

      MD5和SHA1使用的是hash算法,由于hash值具有不可逆性,无法通过hash值获得对应的原值,所以它们又经常用作数据加密。生成的校验值MD5是128位(16个字节),SHA1是160位(20个字节),SHA1的安全性比MD5还要高一点。如果我们想要通过加密后的值获得原加密之前的值,我们可以使用自己的加密算法或者使用非对称加密。比如使用非对称加密即为使用公钥对数据加密后进行保存,通过私钥对加密后的数据进行解密。常用的非对称加密算法有RSA、Elgamal。

  • 相关阅读:
    PhpStorm 常用快捷键和配置+关闭快捷键ctrl+alt+方向键旋转屏幕+快速复制一行快捷键恢复
    WP七牛云插件详解
    注册表删除键值时拒绝访问
    删除注册表子项清除u盘使用痕迹
    一件代发发货人怎么写?淘宝代理发货流程
    联动设置
    使用vue实现行列转换的一种方法。
    从后端到前端之Vue(五)小试路由
    从后端到前端之Vue(四)小试牛刀——真实项目的应用(树、tab、数据列表和分页)
    从后端到前端之Vue(三)小结以及一颗真实的大树
  • 原文地址:https://www.cnblogs.com/milanleon/p/8929685.html
Copyright © 2020-2023  润新知