1. 哈希是一种算法,哈希表是用哈希算法构造出来的一种数据结构
2. 哈希算方法的几种方法
  • 直接定值法
这里有一个例题,就是我们想判断某一字符串中,某一个字符出现的个数,我们可以使用哈希的思想,就是可以遍历一遍字符串,然后开辟一个拥有26数据的整型数组,然后初始化全部为0,然后统计利用一种映射的思想,遍历字符串的时候,就把相应的位置++,每次我们查找某一个字符的时候,一下子就可以定值到那个字符的位置
还有一个例子就是,比如让我们存储1-10这个区间的不重复的数据,这个时候我么也是开辟一个拥有十个元素的整型数组,然后每个数据对应给位置,这样我们在查找的时候,就可以直接查找到这个数据是否存在。
第三个例子是,这里有一些 数据,比如是1001,1006,1009,1007这个时候我们知道数据的范围,我们也是可以开辟一个存储空间来存放这个数据,只不过这个时候我们不需要开辟1000多个大小的数组,我么只需要开辟十个即可,上面的每个数据都减去1000,然后在存放在这个位置,然后我们读取的时候也是按照这个规则读取
分析:上面的几种都是特殊的简单的情况,太具有局限性,还有就是如果我们的数据跨度很大的话,要开辟很大的存储空间,这显然是不合适的,但是一种解题的思路
  • 除留取余法
这里有一部分数据然后我们不能开辟所有的大小的空间,这个时候我呢就出现了一个除留取余法,我们的可以把数据都取余数组的长度,然后放置在相应的位置上面去。
致命缺点:哈希冲突,即是我们的数据可能映射的位置是同一个位置。
法一:闭散列法--开放地址法
(1)第一种方式是线性探测
其他的发生冲突的数据直接往后面走,直到有一个位置上面的数据为空,这个时候把数据放置上去。
比如我么的数据,有89,18,49,58,9,我们开辟的整型数组的空间空间时十个,然后我们放置89,89%10 = 9,然后把89放置在了第九个位置上面去,接着是放置18,18被放置在了8这个位置上面。然后拿到49的时候,我们的数据9这个位置已经后数据,这个时候,我们依次往后走,知道扎到了0号位置是空的,于是49被放置在了0这个位置上面去了。同样 的方法放置其他的数据。
数据放置好了之后,就是查找数据了,这个是时候,如果找9,首先定位到9这个位置上面上,然后没有找到,然后就是依次往后面找,知道找到9,或者就是找到一个空位置停止,这个时候 是没有找到相关的数据。
但是这种方式留下了一个问题就是,如果我们冲突的数据都是集中在了9这个位置了,那每次查找的时候是不是很麻烦,这个时候我我们采用的方法及时二次探测。
(2)第二种方式是二次探测
二次探测的方式值这样的,89进来的时候还是直接放置数据,但是当49进来的时候,不是直接往后面放置了,而是往后移动1的平方个位置,如果是空的就是直接放置,如果不是空的,就是相对于起始位置移动2的 平方个,依次类推下去,直到有一个位置为空。
第一个问题:我们既然已经知道了一次探测的冲突比较大,所以我们这里直接采用的是二次探测
第二个问题:我们的考虑使用数组,但是因为后面的数据可能会增加的很大,所以我们还会考虑增容的操作,这个时候我们不妨直接使用vector。
第三个问题:我们一开始的时候,初始化的时候,初始值应该是多少呢,大家可能和自然的想到了是0,但是有一个问题就是,但是如果我要找的数据就是0的,那不是GG了,所以我们在设计类的成员变量的时候的时候,vector里面的内容 可以放置的是一个结构体,结构体的一部分是数据,一部分是标识位,标识我们的这个位置上面有没有数据
第四个问题:我们既然有增加数据,就一定有删除数据,那个如果是上面的例子,我现在删除了一个数据是49,然后我接下来可能是要找9,这个时候首先定位到下标为9的这个位置,然后往后面二次探测1的平方,这个时候找到了49这个 位置,发现是空,就不往下找了,显然这是不符合 我们的要求的,于是我们想设计结构体的时候,这个标识位有三个值,一个标识有数据,一个标识没有数据,一个标识删除数据,于是我们想到了枚举类型。
第五个问题:我们需要考虑的什么时候增容呢,一开始的想法是当vector满了之后再增容 ,可是这种增容的话,当我们的vector满的 时候是不是数据的冲突就可能大呢,于是我们想的是当已经 放置的数据个数的大小时vector的容量大小的0.7的时候开始增容。
第六个问题:我们的数据在放入的时候,可能在二次探测的时候,可能会出现一只循环在写某些位置,这个 问题下一讲在解决。
代码:hashtable.h
#include<iostream>
using namespace std;
#include<vector>//作为一个标记位
enum State
{EMPTY,EXIT,DEL
};//哈希节点
template<class K,class V>
struct HashNode
{pair<K, V> _kv;State _state;HashNode():_state(EMPTY){}
};template<class K,class V>
ostream& operator<<(ostream &out, HashNode<K,V> node)
{out << " "<< " " << node._state;return out;
}template<class K,class V>
class HashTable
{//friend ostream& operator<<(ostream &out, HashNode<K, V> node);
public:HashTable():_n(0){_table.resize(10);  //预留的数租空间大小是}//插入函数bool Insert(const pair<K,V> node){Capacity();int m = HanshFunc(node);  //HanshFunc,找到我需要插入的一个下标size_t index = m;size_t i = 0;while (_table[index]._state == EXIT){index = m;i++;index = m + i*i;while (index >= _table.capacity()){index = index - _table.capacity();}}_table[index]._kv = node;_table[index]._state = EXIT;_n++;return true;}int Search(const pair<K, V> node){size_t m = HanshFunc(node);size_t index = m;size_t i = 0;while (_table[index]._kv.first != node.first){if (_table[index]._state == EMPTY){return -1;}index = m;i++;index = m + i*i;while (index >= _table.capacity()){index = index - _table.capacity();}}return index;}private:void Capacity(){//if (_n / _table.capacity() >= 0.7)   //这类需要改进的一个地方 就是,我们不能使用0.7,因为除以了之后,不是一个小数//{//    printf("增容\n");//}if (_n * 10 / _table.capacity() >= 7){printf("增容\n");}}int HanshFunc(const pair<K, V> node){size_t index = node.first % _table.capacity();return index;}private:vector<HashNode<K,V>> _table;  //这个是数组size_t _n;  //当前放置的数据的个数是多少
};

HashTable.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"hashtable.h"int main()
{HashTable<int, int> a;pair<int, int> p(5,6);pair<int, int> p1(15,7);pair<int, int> p2(25, 6);pair<int, int> p3(85, 6);pair<int, int> p4(75, 6);pair<int, int> p5(95, 6);pair<int, int> p6(2, 6);pair<int, int> p7(4, 6);a.Insert(p);a.Insert(p1);a.Insert(p2);a.Insert(p3);a.Insert(p4);a.Insert(p5);a.Insert(p6);a.Insert(p7);cout << a.Search(p);return 0;
}

哈希--直接定值法和除留取余法相关推荐

  1. 哈希表除留取余法的桶个数为什么是质数

    可先科普下质数的概念:质数,也就是素数,就是指一个大于1的自然数,约数(因数)只有1和它自己,否则叫合数. 除留取余,就是哈希函数将关键字被某个不大于哈希表长m的数p除后所得余数为哈希地址.这是最常用 ...

  2. 哈希表的实现(取余法)

    哈希表,又叫散列表,它可以提供快速的插入查找操作,对于大规模数据的查找时间空间效率会很高.哈希表构造方式有多种,其中取余法在比赛中最常用. 如果读入很多值非常大的数,让你判断一些数是否出现过.如果用桶 ...

  3. (C++)1022 D进制的A+B 除基取余法将10进制数换成给定进制数

    #include<cstdio> //除基取余法 const int M = 30;int main(){long long a,b,c;int D,ans[M+2];scanf(&quo ...

  4. 进制转换之十进制转换为D进制——整数部分除基取余法

    将十进制x转换为D进制的y: [方法]:整数采用"除基取余法",小数采用"乘基取整法" [推导]:以三位D进制为例,假设y的各位是a1a2a3 y = a 1 ...

  5. (C++)除基取余法:将十进制数转化为Q进制数

    所谓基,就是指将要转换成的进制Q. 除基取余的意思就是:每次将待转换数除以Q,然后将得到的余数作为低位存储,而商则继续除以Q并重复上面的操作,直至商0时,将所有位从高到低输出就可以得到Q进制数. 代码 ...

  6. 十进制转二进制(除2取余法)

    转载链接: https://baike.baidu.com/item/%E5%8D%81%E8%BF%9B%E5%88%B6%E8%BD%AC%E4%BA%8C%E8%BF%9B%E5%88%B6

  7. 哈希表(闭散列、拉链法--哈希桶)

    哈希表,也称散列表,是一种通过key值来直接访问在内存中的存储的数据结构.它通过一个关键值的函数(被称为散列函数)将所需的数据映射到表中的位置来访问数据. 关于哈希表,主要为以下几个方面: 一.哈希表 ...

  8. 乘基取整法是什么_深入理解计算机系统(六):进制间的转换原理

    目录 1.进制的介绍 2.二进制转换成其他进制 3.十进制转换成其他进制 4.十六进制转换成其他进制 5.总结 上一篇博客我们讲解了信息的在计算机中是如何存储以及如何表示的.但是对于各个进制的转换只是 ...

  9. 乘基取整法是什么_数字逻辑电路-学习指南

    数字逻辑电路 - 学习指南 一.判断题 (判断结果为真( T )或为假( F ) 1 . ( )逻辑函数的真值表是惟一的,但表达式不一定是惟一的. 2 . ( )在基数乘除法中,整数部分的转换采用 & ...

最新文章

  1. 图像读取、转为灰度图像、均值平滑、显示保存操作
  2. Scala堆的方式进行Spark topK词频查询(根据value进行TreeMap排序)
  3. SpringBoot系列: RestTemplate 快速入门
  4. 3 分钟搞定 Android Push
  5. 将图形以PNG格式输出到浏览器或文件
  6. 提升体验-支持Chrome Custom Tabs
  7. tcp为什么需要3次握手和3次握手的过程
  8. 多容器,Nginx容器灵活切换PHP版本!同时运行多个PHP容器
  9. HDU 2084 数塔
  10. 浅谈数据中心白盒交换机开发
  11. 74HC595串口转并口芯片学习
  12. (逆向工程)Android一键脱壳工具(MDEX)
  13. js获取当前时间(标准时间)
  14. 常用jquery方法 总结
  15. 记录下如何判断错误:no such file or directory
  16. STM8新建IAR工程
  17. 关于iOS中UITableView下拉距离短刷新没事,下拉距离长就会崩溃的问题解决方案
  18. Android View部分消失效果实现
  19. mac电脑打不开应用程序的解决方法
  20. 暴力破解之验证码绕过

热门文章

  1. linux定时器(crontab)实例
  2. 基于jQuery垂直多级导航菜单代码
  3. 有关 ecshop 属性 {$goods.goods_attr|nl2br} 标签的赋值问题
  4. :before和::before的区别
  5. 《OpenCV3编程入门》学习笔记7 图像变换(一)基于OpenCV的边缘检测
  6. mysql数据导入python_利用python将mysql中的数据导入excel
  7. centos6卸载mysql服务器_CentOS6.5下卸载自带的MySQL数据库安装MySQL5.6
  8. linux mysql 5.7.12_Linux环境mysql5.7.12安装教程
  9. php get memory,PHP memory_get_usage 和 memory_get_peak_usage获取内存的区别
  10. 递归下降文法C语言实验报告,递归下降语法分析器实验报告.doc