zoukankan      html  css  js  c++  java
  • DS-哈希表浅析

    1.哈希表

    2.哈希函数

    3.哈希冲突

    哈希表

    哈希表是一种按key-value存储的数据结构,也称散列表。

    之前的数组、树和图等等查找一个值时都要与结构中的值相比较,查找的效率取决于比较的次数。

    而哈希表因为key与value对应,则可以在较少的比较次数中找到元素。

    哈希函数

    哈希表是对于给定的一个key,建立一个函数H(key),y =H(key)即为元素key在数组内的储存位置。

    1.直接定址法

    直接建立一个与key相关的线性函数形如:H(key)=k*key+b

    2.数字分析法

    就是找找规律,尽量让大多数H(key)落在不同区间,例如对于身份证号码,前面的数字重复率太高,可以考虑用出生年月日计算当key。

    3.平方取中法

    取关键字平方后的中间几位当做哈希地址。

    4.折叠法

    将关键字分成位数相同的几个部分(最后一部分位数可以不同),然后取这几部分的叠加和作为哈希地址。

    关键字位数很多,并且数字分步均匀时可以考虑采用折叠法。

     5.除留余数法

    形如H(key) = key %m.

    6.随机数法(还莫得理解)

    哈希冲突

    当key过多时,就容易发生多个key对于H出现同样的结果,就是它们会占用数组同一个位置,这就是哈希冲突。

    冲突是不可避免的,我们要做的就是要尽可能的使冲突达到最小。

    常用的处理冲突的方法有:

    1.开放定址法

       这个时候H* = (H(key)+di)%m   ,  di = 1、2、3........m-1

     这里对于di 的值又有三种取法:

      1)线性探测再散列:

         顾名思义,di取值为1,2,3.....m-1,也就是当前位置已经有元素时,会像下一个位置走。

       2)二次探测再散列

         这里di 取值为 1^2 、 -1^2 、2^2、-2^2.......- (m/2)^2  , m为散列表长度,若是算出key+di为负数,则加上m再取余,也就是移到了后面,即-1就移到最后一位。 

            3)伪随机探测再散列(这个还没搞懂)

     

    2.链地址法

    链地址法,看图理解。

    经过哈希函数后,对于处于同一位置的元素,用链表将它们储存。

      

     (图源自网络)

     3.再哈希法

       Hi = RHi(key), i = 1,2,3.....k

     RH是不同的函数,在用一个哈希函数产生冲突时就调用另一个,直到不产生冲突为止。

     4.建立公共溢出表

    也就是建立另一个区间,产生冲突的都放进另一个区间。感觉不太好用,冲突多了比较次数又多了。

    下面用除余法和链地址法(尾接法)写的代码如下:

    #include<iostream>
    using namespace std;
    
    #define hashsize 11
    
    struct Node{//链表节点
        int data;
        Node *next;
        int flag;
        Node(){
            flag=0;
            next=NULL;
        }
        Node (int num){
            flag=1;
            data=num;
            next=NULL;
        } 
    };
    
    class Hash{
        private:
            Node *hashtable;
            int  len;//记录现在数据个数
        
        public:
            Hash(){
                len=0;
                hashtable = new Node[hashsize]; 
            } 
            ~Hash(){
            }
            //获得key值 ,哈希函数
            int getKey(int num){
                return num%hashsize;
            }
            //往表中插入元素 ,链接法 
            void insertData(int num){
                int key = getKey(num);//获得key值
                
                if(hashtable[key].flag==0){//此时当前key位置无数据 
                    hashtable[key].data=num;
                    hashtable[key].flag=1; 
                }
                else{//尾插入法 
                    Node *tmp = &hashtable[key];//指针要获得地址才能赋值 
                    while(tmp->next!=NULL)
                        tmp=tmp->next;
                    tmp->next = new Node(num);
                } 
                len++; 
            }
            //查找hash表中是否有这个元素,有则返回查找次数,无则返回-1 
            int find(int num){
                int  sum=1;
                int key = getKey(num);//获取该数键值
                if(hashtable[key].flag==0){
                    insertData(num);
                    return -1;
                }
                else{
                    Node *tmp = &hashtable[key];
                    while(tmp->data!=num){
                        sum++;
                        if(tmp->next==NULL){
                            insertData(num);
                            return -1;
                        }
                        tmp=tmp->next;
                    }
                    //能退出上面的while说明找到相等的了,return sum 
                    return sum;
                } 
            } 
    }; 
    
    int main(){
        int n;
        cin>>n;
        int num[100];
        for(int i=0;i<n;i++)
            cin>>num[i];
            
        Hash *hash = new Hash();
        for(int i=0;i<n;i++)
            hash->insertData(num[i]);
        
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>num[i];
            if(hash->find(num[i])!=-1)
                cout<<hash->getKey(num[i])<<" "<<hash->find(num[i])<<endl;
            else cout<<"error"<<endl;
        }

    这里待测样例如下:

    6
    11 23 39 48 75 62
    6
    39
    52
    52
    63
    63
    52
    View Code

    样例输出:

    接下来是用除余法与二次散列再分配写的代码:

    //该程序使用二次散列再查找的方式处理冲突构建哈希表 
    //哈希函数选择除于11的方法
    
    #include<iostream>
    using namespace std;
    #define modnum 11
    
    struct HashData{
        int data;
        int flag; 
        HashData(){
            flag=0;//当查找次数 
        }
    };
    
    class Hash{
        
        private:
            HashData *hashtable;//哈希表
            int len;
            int *value;
        public:
            Hash(int n){
                hashtable = new HashData[n];
                len=n;
                int flag = -1;
                int pos=0;
                value = new int [len];
                for(int i=1;i<len/2;i++){
                    value[pos++]=i*i;
                    value[pos++]=flag*i*i;
                }
            }
            ~Hash(){
            }
            int getKey(int num){
                return num%modnum;
            } 
            void insert(int num){
                int key = getKey(num);
                
                if(hashtable[key].flag==0){
                    hashtable[key].data = num;
                    hashtable[key].flag = 1;
                }
                else{
                    int flag1=1;
                    for(int i=0;i<len;i++){
                        //二次散列 ,找到空的位置 
                        flag1++;
                        //如果此时key+value[i] 是负数就去到H()+len的位置 
                            if(hashtable[(key+value[i]+len)%len].flag==0){
                                hashtable[(key+value[i]+len)%len].data = num;
                                hashtable[(key+value[i]+len)%len].flag = flag1;
                                return ;
                        }
                    }
                }
            }
            int find(int num){
                int key = getKey(num);//获得键值
                if(hashtable[key].data==num){
                    return key+1;
                }
                else{
                    for(int i=0;i<len;i++){
                            if(hashtable[(key+value[i]+len)%len].data==num){
                                return (key+value[i]+len)%len+1; 
                            }
                    }
                    return -1;
                }
            }
            int getSum(int num){
                int key = find(num);//获得键值
                if(key != -1)
                    return hashtable[key-1].flag;
                else{
                    int sum=1;
                    for(int i=0;i<len;i++){
                        sum++;
                        //说明找不到这个元素     
                        if(hashtable[(key+value[i]+len)%len].flag==0)
                            break;
                    }
                    return sum;
                }        
            }
            void show(){
                for(int i=0;i<len-1;i++){
                    if(hashtable[i].flag!=0){
                        cout<<hashtable[i].data<<" ";
                    }
                    else
                        cout<<"NULL ";
                }
                if(hashtable[len-1].flag!=0){
                        cout<<hashtable[len-1].data<<endl;
                    }
                    else
                        cout<<"NULL"<<endl;
                        
                for(int i=0;i<len;i++)
                    cout<<hashtable[i].flag<<" ";
                cout<<endl;
            }
            
            void deleteData(int num){
                int key = find(num);
                if(key!=-1){
                    if(hashtable[key-1].flag!=0){
                        hashtable[key-1].flag=0;
                    }
                }
            }        
    };
    
    
    int main(){
        int n,m;
        cin>>m>>n;
        Hash *hash = new Hash(m);
        int a[100];
        for(int i=0;i<n;i++){
            cin>>a[i];
            hash->insert(a[i]);
        }
        hash->show();
        //查找
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>a[i];
            if(hash->find(a[i])!=-1){
                cout<<1<<" "<<hash->getSum(a[i])<<" "<<hash->find(a[i])<<endl;
            }
            else
                cout<<0<<" "<<hash->getSum(a[i])<<endl;
        }
        //删除
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>a[i];
            hash->deleteData(a[i]);
            hash->show();
        }     
    }

    样例输入(不包括新加的删除样例):

    12 10
    22 19 21 8 9 30 33 4 41 13
    4
    22
    15
    30
    41
    View Code

    样例输出:

    哈希的就先到这里,那些高端操作拜拜了您下次再看。

    最好的开始时间是以前,其次是现在。
  • 相关阅读:
    windows 系统配置(二)MongoDB
    The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "".
    【读书笔记】C#高级编程 第十三章 异步编程
    【读书笔记】C#高级编程 第十二章 动态语言扩展
    【读书笔记】C#高级编程 第十一章 LINQ
    【读书笔记】C#高级编程 第十章 集合
    【读书笔记】C#高级编程 第九章 字符串和正则表达式
    【读书笔记】C#高级编程 第八章 委托、lambda表达式和事件
    【读书笔记】C#高级编程 第七章 运算符和类型强制转换
    【读书笔记】C#高级编程 第六章 数组
  • 原文地址:https://www.cnblogs.com/dragonsbug/p/11980018.html
Copyright © 2011-2022 走看看