哈希表是如何存取数据的原理是什么

2024-05-19 18:07

1. 哈希表是如何存取数据的原理是什么

哈希表:根据关键码值而直接进行访问的数据结构

哈希表是如何存取数据的原理是什么

2. 本人没有学过高等数学,是否可以通俗的解释一下哈系算法(hash)SHA-256的原理,最好能够具体点!谢谢!

【哈希算法】在【数据结构】课程里面有提及,它是散列表查找中的一种思想,当然与编程紧密相连。
能力有限,无法解释通透~~

3. hash函数在密码学中有什么作用

Hash,一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。

hash函数在密码学中有什么作用

4. 请解释下面这段MFC程序自动生成的代码的意思及作用,谢谢!

一下三行是该方法上面的说明。(是MFC自动生成的)
// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.
由此可知:条件IsIconic()是有必要的。
以上代码的意思就是:如果CWnd最小化了,那么就先获取设备上下文,然后用图表擦出背景,然后获得图表的宽和高,再获得最小化时的客户区矩形大小,通过int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;计算,然后在x,y出画图标m_hIcon.(由// Center icon in client rectangle注释可以知道是在最小化时的客户区中间画图标)否则直接调用父类的OnPaint重画对话框。

5. 什么叫函数不正确

多种原因会导致这个问题出现,先给出两种解决办法。
1、在我的电脑->属性->硬件->设备管理器中停用这个光驱,然后再启动就可以了。
如果还不行的原因就是:每种刻录机,里面都有1个FLASH芯片,专门记录刻录盘的详细地型号和刻录速度的。当你的光驱不能识别的刻录光盘的话,部分刻录机就会出现这个错误的。
所以不是你的光驱问题了。
2、硬盘被加密过。就是你原先在一个系统里对硬盘的分区加密过,但是后来又在另一个系统下或环境下访问该分区,就造成了无法访问,提示函数不正确。建议回到原来的系统对原区解密,方法:右击盘符  "属性-高级-加密/解密" 
3、系统安装了nero express后,自带的cd刻录功能被屏蔽了导致。可在系统下打开 "运行",输入services.msc,确定后弹出一个"服务"设置窗口,找到imapi cd-burning com services 项目,双击该项目,把启动类型由禁用改为自动,确定后重启系统。

什么叫函数不正确

6. MFC 中GetDlgItem(ID)函数的问题!

“用哪个控件的指针指向GetDlgItem(IDC_LIST1)” 这个说法不准确。

GetDlgItem(IDC_LIST1) 这个函数获得的,是一个CWnd* 的窗口类指针,如果需要使用这个指针对控件进行操作,那么标准窗口操作(如:GetWindowText)是不需要强制类型转换的。如果操作是控件专有方法(如AddString),就要对指针进行强制转换,成为CListBox* 才能使用列表框类的专有方法(函数)。

7. 在散列表和排序后的列表中找一个元素,哪个查找速度最快?

散列表又叫做哈希表

1. 引言
        哈希表(Hash Table)的应用近两年才在NOI中出现,作为一种高效的数据结构,它正在竞赛中发挥着越来越重要的作用。        
哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的。另外,编码比较容易也是它的特点之一。
哈希表又叫做散列表,分为“开散列” 和“闭散列”。考虑到竞赛时多数人通常避免使用动态存储结构,本文中的“哈希表”仅指“闭散列”,关于其他方面读者可参阅其他书籍。
2. 基础操作
2.1 基本原理
我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素“分类”,然后将这个元素存储在相应“类”所对应的地方。
但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了“冲突”,换句话说,就是把不同的元素分在了相同的“类”之中。后面我们将看到一种解决“冲突”的简便做法。
总的来说,“直接定址”与“解决冲突”是哈希表的两大特点。

2.2 函数构造
构造函数的常用方法(下面为了叙述简洁,设 h(k) 表示关键字为 k 的元素所对应的函数值):

a) 除余法:
选择一个适当的正整数 p ,令 h(k ) = k  mod  p 
这里, p 如果选取的是比较大的素数,效果比较好。而且此法非常容易实现,因此是最常用的方法。
b) 数字选择法:
如果关键字的位数比较多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干位,所组成的新的值作为关键字或者直接作为函数值。

2.3 冲突处理
线性重新散列技术易于实现且可以较好的达到目的。令数组元素个数为 S ,则当 h(k) 已经存储了元素的时候,依次探查 (h(k)+i) mod S , i=1,2,3…… ,直到找到空的存储单元为止(或者从头到尾扫描一圈仍未发现空单元,这就是哈希表已经满了,发生了错误。当然这是可以通过扩大数组范围避免的)。

2.4 支持运算
哈希表支持的运算主要有:初始化(makenull)、哈希函数值的运算(h(x))、插入元素(insert)、查找元素(member)。
设插入的元素的关键字为 x ,A 为存储的数组。
初始化比较容易,例如
       const empty=maxlongint;     //  用非常大的整数代表这个位置没有存储元素
            p=9997;               //  表的大小
       procedure makenull;
         var i:integer;
         begin
           for i:=0 to p-1 do
             A[i]:=empty;
         End;
       
哈希函数值的运算根据函数的不同而变化,例如除余法的一个例子:
        function h(x:longint):Integer;
          begin
            h:= x mod p;
          end;

我们注意到,插入和查找首先都需要对这个元素定位,即如果这个元素若存在,它应该存储在什么位置,因此加入一个定位的函数 locate 
         function locate(x:longint):integer;
           var orig,i:integer;
           begin
             orig:=h(x);
             i:=0;
             while (i<S)and(A[(orig+i)mod S]<>x)and(A[(orig+i)mod S]<>empty) do
inc(i); 
//当这个循环停下来时,要么找到一个空的存储单元,要么找到这个元
//素存储的单元,要么表已经满了
             locate:=(orig+i) mod S;
           end;
插入元素
        procedure insert(x:longint);
           var posi:integer;
           begin
             posi:=locate(x);           //定位函数的返回值
             if A[posi]=empty then A[posi]:=x
                            else error; //error 即为发生了错误,当然这是可以避免的
           end; 

查找元素是否已经在表中
        procedure member(x:longint):boolean;
           var posi:integer; 
           begin
             posi:=locate(x);
             if A[posi]=x then member:=true
                        else member:=false;
           end;

这些就是建立在哈希表上的常用基本运算。

           下文提到的所有程序都能在附录中找到。
3. 效率对比
3.1简单的例子与实验
下面是一个比较简单的例子:
===================================================================
集合 ( Subset )
问题描述:
给定两个集合A、B,集合内的任一元素x满足1 ≤ x ≤ 109,并且每个集合的元素个数不大于104 个。我们希望求出A、B之间的关系。只需确定在B 中但是不在 A 中的元素的个数即可。


这个题目是根据 OIBH NOIP 2002 模拟赛 # 1 的第一题改编的。

分析:我们先不管A 与 B 的具体关系如何,注意到这个问题的本质就是对于给定的集合A ,确定B 中的元素是否在 A 中。所以,我们使用哈希表来处理。至于哈希函数,只要按照除余法就行了,由于故意扩大了原题的数据规模, H(x) = x mod 15889;
当然本题可以利用别的方法解决,所以选取了速度最快的快速排序+二分查找,让这两种方法作效率对比。
我们假定 |A|=|B|  ,对于随机生成的数据,计算程序重复运行50次所用时间。
对比表格如下:

 哈希表(sec) 快速排序+二分查找(sec)
复杂度 O(N) (只有忽略了冲突才是这个结果。当然实际情况会比这个大,但是重复的几率与哈希函数有关,不容易估计) O(N log N+ N) = O(N log N)
测试数据规模 —— ——
500 0.957 0.578
1000 1.101 0.825
2500 1.476 1.565
5000 2.145 2.820
7500 2.905 4.203
10000 3.740 5.579
13500 7.775 7.753
15000 27.550 8.673


对于数据的说明:在 Celeron566 下用 TP 测试,为了使时间的差距明显,让程序重复运了行50次。同时哈希表中的P= 15889 ,下标范围 0..15888 。由于快速排序不稳定,因此使用了随机数据。

3.2 对试验结果的分析:
注意到两个程序的用时并不像我们期望的那样,总是哈希表快。设哈希表的大小为 P .

首先,当规模比较小的时候(大约为a< 10% * P,这个数据仅仅是通过若干数据估记出来的,没有严格证明,下同),第二种方法比哈希表快。这是由于,虽然每次计算哈希函数用O(1) 的时间,但是这个系数比较大。例如这道题的 H(x)=x mod 15589 ,通过与做同样次数的加法相比较,测试发现系数 > 12 ,因为 mod 运算本身与快速排序的比较大小和交换元素运算相比,比较费时间。所以规模小的时候,O(N)(忽略冲突)的算法反而不如 O(NlogN)。这一点在更复杂的哈希函数上会体现的更明显,因为更复杂的函数系数会更大。
其次,当规模稍大 (大约为 15%*P < a < 85%*P) 的时候,很明显哈希表的效率高。这是因为冲突的次数较少。
再次,当规模再大 (大约为 90%*P < a < P )的时候,哈希表的效率大幅下降。这是因为冲突的次数大大提高了,为了解决冲突,程序不得不遍历一段都存储了元素的数组空间来寻找空位置。用白箱测试的方法统计,当规模为13500的时候,为了找空位置,线性重新散列平均做了150000 次运算;而当规模为15000 的时候,平均竟然高达2000000 次运算,某些数据甚至能达到4265833次。显然浪费这么多次运算来解决冲突是不合算的,解决这个问题可以扩大表的规模,或者使用“开散列”(尽管它是动态数据结构)。然而需要指出的是,冲突是不可避免的。

初步结论:
当数据规模接近哈希表上界或者下界的时候,哈希表完全不能够体现高效的特点,甚至还不如一般算法。但是如果规模在中央,它高效的特点可以充分体现。我们可以从图像直观的观察到这一点。









  






试验表明当元素充满哈希表的 90% 的时候,效率就已经开始明显下降。这就给了我们提示:如果确定使用哈希表,应该尽量使数组开大(由于竞赛中可利用内存越来越多,大数组通常不是问题,当然也有少数情况例外),但对最太大的数组进行操作也比较费时间,需要找到一个平衡点。通常使它的容量至少是题目最大需求的 120% ,效果比较好(这个仅仅是经验,没有严格证明)。
                                                       
4. 应用举例
4.1 应用的简单原则
什么时候适合应用哈希表呢?如果发现解决这个问题时经常要询问:“某个元素是否在已知集合中?”,也就是需要高效的数据存储和查找,则使用哈希表是最好不过的了!那么,在应用哈希表的过程中,值得注意的是什么呢?
哈希函数的设计很重要。一个不好的哈希函数,就是指造成很多冲突的情况,从前面的例子已经可以看出来,解决冲突会浪费掉大量时间,因此我们的目标就是尽力避免冲突。前面提到,在使用“除余法”的时候,h(k)=k mod p ,p 最好是一个大素数。这就是为了尽力避免冲突。为什么呢?假设 p=1000 ,则哈希函数分类的标准实际上就变成了按照末三位数分类,这样最多1000类,冲突会很多。一般地说,如果 p 的约数越多,那么冲突的几率就越大。
简单的证明:假设 p 是一个有较多约数的数,同时在数据中存在 q 满足 gcd(p,q)=d >1 ,即有 p=a*d , q=b*d,  则有 q mod p= q – p* [q div p] =q – p*[b div a] . ① 其中 [b div a ] 的取值范围是不会超过 [0,b] 的正整数。也就是说, [b div a] 的值只有 b+1 种可能,而 p 是一个预先确定的数。因此 ① 式的值就只有 b+1 种可能了。这样,虽然mod 运算之后的余数仍然在 [0,p-1] 内,但是它的取值仅限于 ① 可能取到的那些值。也就是说余数的分布变得不均匀了。容易看出, p 的约数越多,发生这种余数分布不均匀的情况就越频繁,冲突的几率越高。而素数的约数是最少的,因此我们选用大素数。记住“素数是我们的得力助手”。
另一方面,一味的追求低冲突率也不好。理论上,是可以设计出一个几乎完美,几乎没有冲突的函数的。然而,这样做显然不值得,因为这样的函数设计很浪费时间而且编码一定很复杂,与其花费这么大的精力去设计函数,还不如用一个虽然冲突多一些但是编码简单的函数。因此,函数还需要易于编码,即易于实现。
综上所述,设计一个好的哈希函数是很关键的。而“好”的标准,就是较低的冲突率和易于实现。
另外,使用哈希表并不是记住了前面的基本操作就能以不变应万变的。有的时候,需要按照题目的要求对哈希表的结构作一些改进。往往一些简单的改进就可以带来巨大的方便。
这些只是一般原则,真正遇到试题的时候实际情况千变万化,需要具体问题具体分析才行。

在散列表和排序后的列表中找一个元素,哪个查找速度最快?

8. 什么是哈希表?特点是什么

简单说就是按照哈希函数关系建立的表
具体内容请参考数据结构相关知识~ 
下面引用一些别的地方
1 基本原理

  我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素"分类",然后将这个元素存储在相应"类"所对应的地方。

  但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了"冲突",换句话说,就是把不同的元素分在了相同的"类"之中。后面我们将看到一种解决"冲突"的简便做法。

  总的来说,"直接定址"与"解决冲突"是哈希表的两大特点。

2 函数构造

  构造函数的常用方法(下面为了叙述简洁,设 h(k) 表示关键字为 k 的元素所对应的函数值):

  a) 除余法:

  选择一个适当的正整数 p ,令 h(k ) = k mod p 
  这里, p 如果选取的是比较大的素数,效果比较好。而且此法非常容易实现,因此是最常用的方法。

  b) 数字选择法:

  如果关键字的位数比较多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干位,所组成的新的值作为关键字或者直接作为函数值。

3 冲突处理

  线性重新散列技术易于实现且可以较好的达到目的。令数组元素个数为 S ,则当 h(k) 已经存储了元素的时候,依次探查 (h(k)+i) mod S , i=1,2,3…… ,直到找到空的存储单元为止(或者从头到尾扫描一圈仍未发现空单元,这就是哈希表已经满了,发生了错误。当然这是可以通过扩大数组范围避免的)。

4 支持运算

  哈希表支持的运算主要有:初始化(makenull)、哈希函数值的运算(h(x))、插入元素(insert)、查找元素(member)。
  设插入的元素的关键字为 x ,A 为存储的数组。
  初始化比较容易,例如
  const empty=maxlongint; // 用非常大的整数代表这个位置没有存储元素
     p=9997;      // 表的大小
  procedure makenull;
   var i:integer;
   begin
    for i:=0 to p-1 do
     A[i]:=empty;
   End;

  哈希函数值的运算根据函数的不同而变化,例如除余法的一个例子:
  function h(x:longint):Integer;
   begin
    h:= x mod p;
   end;

  我们注意到,插入和查找首先都需要对这个元素定位,即如果这个元素若存在,它应该存储在什么位置,因此加入一个定位的函数 locate 
  function locate(x:longint):integer;
   var orig,i:integer;
   begin
    orig:=h(x);
    i:=0;
    while (ix)and(A[(orig+i)mod S]empty) do
     inc(i); 
     //当这个循环停下来时,要么找到一个空的存储单元,要么找到这个元
     //素存储的单元,要么表已经满了
    locate:=(orig+i) mod S;
   end;
  插入元素
  procedure insert(x:longint);
   var posi:integer;
   begin
    posi:=locate(x);      //定位函数的返回值
    if A[posi]=empty then A[posi]:=x
          else error; //error 即为发生了错误,当然这是可以避免的
   end; 

  查找元素是否已经在表中
  procedure member(x:longint):boolean;
    var posi:integer; 
    begin
     posi:=locate(x);
     if A[posi]=x then member:=true
             else member:=false;
    end;

  这些就是建立在哈希表上的常用基本运算。
最新文章
热门文章
推荐阅读