数据结构——手动写一个哈希表

数据结构——哈希表

哈希表介绍


1、什么是哈希表?

可以考虑这么一个情景

现在某个学校为新生录入信息,新生们的学号为2001、2002、2003…这样的类型,现将其存储在一个数组中,现在要查找学号为2066的学生。

如果采用顺序查找的方法进行查找,它的执行过程是先从数组第一个位置出发,将2066与这个位置对应的关键字进行比较,直到找到那个与2066相等的位置返回,这次查找过程中,一共需要66次查找

在这里插入图片描述

而假设我们设定一个函数,h(学号) = 学号-2001,然后通过这个函数来定位2066这个学号对应在哪个位置,可以直接通过这个函数h(2066) = 2066-2001 = 65,可以直接定位到这个学号为2066的同学在数组的第66个位置,也即索引为array[65],而在此次查找过程中,只用了一次查找就完成了对这个学生信息的查找

在这里插入图片描述

对比两种解决方案,很明显在这种情景下,选用第二种方法明显更加合理,类似于这样的一种情况,把关键字通过哈希函数

映射到目标地址的查找方式称为哈希,能大大的加快查找的过程,当一组关键字与存储地址之间存在着某种对应关系的话,他就可以用哈希表这种数据结构来实现,其一般查找过程为:

key —> hash函数 —> 目标地址

2、如何构造哈希函数?

哈希函数一般是根据不同的关键字特征而采用特定的构造方法来构造的一个映射关系,常用的构造哈希函数的方法有

  • 直接定址法

直接定址法是通过关键字key加上某个特定的常数来定位到目标位置的方法,其公式为

# h(key) = key + c 

这一类哈希设计方法是针对于关键字的分布基本连续时,可以使用直接定址法,就对于前面举到的例子,2001,2002,2003…

可以设计哈希函数h(key) = key - 2001来定位特定学号的学生对应的数组下标位置,从而快速定位到该位置,如果关键字不连续采用这种方法会导致内存大量的浪费

  • 除留余数法

对于一系列不连续的关键字,可以用除留余数法来进行hash,除留余数法大致是取一个整数q,q小于哈希表的长度m,对于每一个关键字key,其哈希函数hash(key) = key mod p,来定位到特定的位置,对于一些关键字不连续的数据,可以通过取余的方式来定位到特定的地址位置

  • 数字分析法

比如对于一组关键字,他们的数字十分巨大,但是在这些关键字之间的某些位上,存在着一定的特点,那么此时就可以用这些位上的特定数字来作这个哈希函数,如果觉得我说得不太清除的话可以看下面的例子

在这里插入图片描述

比如对于这样一组数据,每一个关键字十分巨大,但是这组关键字的后4位几乎相同,只是前两位有变化,那么结合这些数据的特点,将这两位数字拆分出来当作哈希函数,对应到特定的数组位置,这样的根据关键字特征来创建哈希函数的方法叫做数字分析法。

3、哈希冲突和装载因子?

什么是哈希冲突?

在哈希函数中,某些数据的存储会不可避免的出现冲突,就比如除留取余法中,13%10=3,23%10=3,不同的关键字通过哈希函数得到的映射结果是一样的,这就是哈希冲突,哈希冲突用正经一点的语句概括就是:不同的关键字经过哈希函数得到的映射结果一样,也可以说,只要是哈希表,就一定会存在哈希冲突,只是不同的针对于不同的场景选用不同的哈希函数,发生哈希冲突的可能性有大有小而已

程序员表情包 的图像结果

什么是装载因子?

装填因子就是值哈希表中已存入数据与哈希表长度之比,装填因子越小,发生哈希冲突的可能性就越小,然而太小的装填因子又会导致存储空间的浪费,所以一般将装填因子控几在0.6到0.9之间,当然某些哈希结构装填因子可以大于一

哈希表的查找性能与什么有关?

说到这个,其实都已经直到,其实哈希表的查找性能大致与下面几个因素有关

  1. 装填因子大小
  2. 哈希函数选择
  3. 当发生哈希冲突时解决哈希冲突的方法

4、哈希冲突都有什么解决办法?

开放定址法

开放定址法即在出现哈希冲突的时候往存储位置左右两边寻找空位的一个方法,就比如你在电影院买了一张票,去的时候发现你的那个座位被人坐了,然后你看到边上的作为都是空的,就选择坐在了边上的座位,这就是开放定址法,这些经过哈希函数映射到同一个地址的关键字称为同义词,在这周围找空闲位置的方法有线性探测法平方探测法

  • 线性探测法

线性探测法即从发生哈希冲突的位置d0开始,往后找空闲位置,这个往后是循环扫描的意思,其递推公式大致为
{ d 0 = h ( k ) d i = ( d i − 1 + 1 ) m o d ( m ) − − ( 1 ≤ i ≤ m − 1 ) \begin{cases} d_0=h(k)\\d_i=(d_{i-1}+1) mod(m)--(1\leq i\leq m-1) \end{cases} {d0=h(k)di=(di1+1)mod(m)(1im1)
以看电影为例子,假设座位只有一排(20个座位左右),你的座位是8,然后你到电影院的时候发现8这个座位被人占了,然后就从9开始找空位置,顺序一次是9,10,11…20有没有空位,有空位就坐下,没有空位就在1,2,3,4,…7去找空位,这就是线性探测法,线性探测法有优点是思路简单,容易实现,但是容易产生堆积问题,就是因为如果多个同义词关键字存放到hash表中时,后面d0+1,d0+2…个位置都会被占据,这就会导致d0+1,d0+2对应的关键字继续往后放,最后可能会导致很多关键字都不在其对应的位置上,影响查找的性能

  • 平方探测法

平方探测法即在d0进行左右摇摆,寻找空的位置,其递推式为
{ d 0 = h ( d ) d i = d 0 ± i 2 \begin{cases} d_0=h(d)\\d_i=d_0\pm i^2 \end{cases} {d0=h(d)di=d0±i2
它的思路也是跟线性探测法一样,只不过是左右摇摆着找空闲位置罢了

拉链法

哈希表的第二种方法就是拉链法,拉链法的意思是,所有的同义词关键字都存放在一个链表表中,此时哈希表的数据组织形式为数组+链表,即每一个数组位置并不是存放着一个对应的关键词,而是存储着这一系列同义词关键字链表的头节点,后序数据组织形式中还有更复杂的,比如Java里面的concurrentHashMap里面采用的数组+链表+红黑树的结构,它的组织形式大致如下,由于拉链法它的容量可以大于哈希表的长度,所以一般装载因子选a=1

在这里插入图片描述

与开放定址法相比较的话,拉链法的优点有

  1. 哈希表的长度可以动态的构造,适用于一些无法构造特定表长的情况
  2. 处理冲突简单,不会出现开放定址法那样的堆积现象,平均查找性能要快一些
  3. 装填因子可以大于1
  4. 删除结点的操作更加易于实现

实现一个哈希表

用开放定址法解决哈希冲突

用c++实现

暂时可以不在这个结构里面存储数据,只在里面定义一个关键字KeyType,数据组织采用一个结构体,然后将哈希表操作放到一个类中

//头文件依赖
#include<vector>
//宏定义
#define EMPTYKEY -1   //当位置为空关键字KeyType的值为-1

using KeyType = int;

template<typename T>
struct HashNode{
    KeyType key;
    T data;
    HashNode(int key):key(key){} 
    HashNode(){  //构造一个空的结点
        key = EMPTYKEY;  
    }
    void setData(T data)
        this->data = data;
}


template<class T>
class HashTable{
    //维护一个数组
    HashTable(int len){
        if(len < 0){
            throw runtime_error("len<0");
        }
        this->mod = len;
        this->factor = 0;
        this->datacount = 0;
        this->capacity = len;
        this->table = vector<HashNode>(len);        
    }
    bool insertNode(KeyType key,T data);  //将数据插入到哈希表中
   	bool deleteNode(KeyType key);  //删除特定关键字对应的结点
    bool searchNode(KeyType key,T &result);  //查找结点,返回键值
    int indexOfNode(KeyType key);  //查找key对应在哈希表里面的位置
    KeyType dataToKey(T,data);  //查找T对应的key
    void reHash();  // 当装载因子达到需要reHash的上线的时候,就需要对数据进行reHash,重构哈希表
   	
private:
    int factor;   //装载因子 = 现存数据量/数据总量
    int datacount;  // 现存数据量
    int capacity;   //哈希表的存储能力,即哈希表的长度
    int mod;   //取余因子
    vector<HashNode<T>> table;  //存储数据的哈希表
}

方法实现

这些方法都是写在类里面的方法

  1. reHash

第一个实现的方法是reHash,reHash是为了解决当装载因子factor高于或低于某个值的时候,为了保证哈希表的性能但是又不造成过大的内存占用情况的条件下,我的设定为当装载因子大于0.9或小于0.3的时候,都需要进行reHash操作,前者将哈希表的长度扩大到原来的1.8倍,后者将哈希表的长度缩小到原来的0.6倍

void reHash(bool up){
    std::vector<HashNode<T>> tables = table;
    if(up)
        capacity = 1.8*capacity;
    else
        capacity = 0.6*capacity;
    datacount = 0;
    mod = capacity;
    table = std::vector<HashNode<T>>(this->capacity);
    for(auto node : tables){
        if(node.key != EMPTYKEY){
            insertNode(node.key,node.data);
        }
    }
}
  1. insertNode

将关键字结点插入到哈希表中去,采用取余的哈希函数,以及开放定址法解决哈希冲突,如果插入成功,返回一个false,形参有一个T是因为前面函数组织时定义了一个泛型T

bool insertNode(KeyType key,T data){
    if(capacity <= 0)
        throw std::runtime_error("len <= 0 ");
    else{
        if(factor > 0.9)
            reHash(true);
        int adr,mod = this->mod;
        adr = key % mod;
        if(table[adr].key == EMPTYKEY || table[adr].key == key){
         	table[adr].setData(data);
            table[adr].setKey(key);
            datacount++;
            factor = (float)datacount / (float)capacity;
            return true;   
        }else{
            int i=1,k;
            k = (adr + i) % mod;
            while(k != adr){
                if(adr[k].key == EMPTYKEY || adr[k].key == key){
                    table[adr].setData(data);
            		table[adr].setKey(key);
            		datacount++;
            		factor = (float)datacount / (float)capacity;
            		return true;  
                }else{
                    ++i;
                    k = (adr + i) % mod;
                }
			}
            return false
        }
	}
}
  1. deleteNode

deleteNode方法用于来删除哈希表中的结点

bool deleteNode(KeyType key){
	if(capacity <= 0)
        throw std::runtime_error("len <= 0");
    if(key < 0)
        return false;
    else{
        int adr,mod = this->mod;
        adr = (mod + i) % mod;
        if(table[adr].key == key){
            table[adr].setKey(EMPTYKEY);
        	datacount--;
        	factor = (float)datacount / (float)capacity;
        	if(factor < 0.3)
                reHash(false);
        	return true;
        }else{
            int i=1,k;
            k = (adr + i) % mod;
            while(k != adr){
                if(table[k].key == key){
                    table[k].setKey(EMPTYKEY);
                    datacount--;
                    factor = (float)datacount / (float)capacity;
                    if(factor < 0.3)
						reHash(false);
                    return true;
                }else{
                    ++i;
                    k = (adr + i) % mod;
				}
            }
        }
    }
    return false;
}
  1. searchNode

在哈希表中查找结点,将查找到对饮关键字的值付给传入的泛型数据

bool searchNode(KeyType key,T &result){
    if(capacity <+ 0)
        throw std::runtime_error("len <= 0");
    else{
        int adr,mod = this->mod;
        adr = key % mod;
        if(table[adr].key == key){
            result = table[adr].key;
            return true;
		}else{
            int i=1,k;
            k = (adr + i) % mod;
            while(k != adr){
                if(table[k].key == key){
                    result = table[k].key;
                    return true;
				}else{
                    ++i;
                    k = (adr + i) % mod;
				}
            }
		}
    }
    return false;
}
  1. indexOfKey

返回关键字在哈希表中的位置

int indexOfKey(KeyType key){
    if(capacity <= 0)
        throw std::runtime_error("len <= 0");
    else{
        int adr,mod = this->mod;
        adr = key % mod;
        if(table[adr].key == key)
            return adr+1;
        else{
            int i=1,k;
            k = (adr + i) % mod;
            while(k != adr){
                if(table[k].key == key)
                    return k+1;
                else{
                    ++i;
                    k = (adr + i) % mod;
                }
            }
        }
    }
    return -1;
}
  1. dataToKey

对于某个data,获取其对应的key值

KeyType dataToKey(T data){
    if(capacity <= 0)
        throw std::runtime_error("len <= 0");
    else{
        for(auto node : table){
            if(node.data == data && node.key != EMPTYKEY)
                return node.key;
		}
    }
    return -1;
}
  1. 其它方法
int dataCount(){
	return datacount;
}

int dataCapacity(){
    return capacity;
}
用Java实现
package myHash;

public class MyHashTable<E> {

    private double factor;  //装载因子
    private int datacount; //哈希表存储数量
    private int capacity;  //哈希表长度
    private int mod;  //取余因子 等于capacity
    private HashNode<E>[] table;  //存储数据的数据表

	//构造器
    public MyHashTable(int capacity){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            this.mod = capacity;
            this.capacity = capacity;
            this.factor =0.0;
            this.datacount = 0;
            this.table = new HashNode[capacity];
            for (int i = 0; i < capacity; i++) {  //初始化table
                table[i] = null;
            }
        }
    }

    //重新运算装载因子
    private void reComplateFactor(){
        factor = (double) datacount / (double) capacity;
    }

    //插入哈希结点,散列函数为取余
    public boolean insertNode(int key,E data){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            if(factor > 0.9)
                reHash(true);
            int adr,mod = this.mod;
            adr = key % mod;
            if(table[adr] == null || (table[adr] != null && table[adr].key == key)){
                if(table[adr] == null)
                    table[adr] = new HashNode<E>(key, data);
                else{
                    table[adr].setData(data);
                }
                datacount++;
                reComplateFactor();
                return true;
            }else{
                int i=1,k;
                k = (adr + i) % mod;
                while(k != adr){
                    if(table[k] == null || (table[k] != null && table[k].key == key)){
                        if(table[k] == null)
                            table[k] = new HashNode<E>(key,data);
                        else{
                            table[k].setData(data);
                        }
                        datacount++;
                        reComplateFactor();
                        return true;
                    }else{
                        ++i;
                        k = (adr + i) % mod;
                    }
                }
            }
        }
        return false;
    }

    //删除哈希结点
    public boolean deleteNode(int key){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            int adr,mod = this.mod;
            adr = key % mod;
            if(table[adr] != null && table[adr].key == key) {
                table[adr] = null;
                datacount--;
                reComplateFactor();
                if(factor < 0.3)
                    reHash(false);
                return true;
            }
            else{
                int i=1,k;
                k = (adr + i) % mod;
                while(k != adr){
                    if(table[k] != null && table[k].key == key){
                        table[k] = null;
                        datacount--;
                        reComplateFactor();
                        if(factor < 0.3)
                            reHash(false);
                        return true;
                    }else{
                        ++i;
                        k = (adr + i) % mod;
                    }
                }
            }
        }
        return false;
    }

    //通过关键字查找键值
    public E searchNode(int key){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            int adr,mod = this.mod;
            adr = key % mod;
            if(table[adr] != null && table[adr].key == key){
                return table[adr].data;
            }else{
                int i=1,k;
                k = (adr + i) % mod;
                while(k != adr){
                    if(table[k] != null && table[k].key == key)
                        return table[k].data;
                    else{
                        ++i;
                        k = (adr + i) % mod;
                    }
                }
            }
        }
        return null;
    }

    //获取当前哈希表的数据量
    public int getDatacount(){
        return datacount;
    }

    //获取当前哈希表的存储长度
    public int getCapacity(){
        return capacity;
    }

    //获取关键字的索引
    public int indexOfKey(int key){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            int adr,mod = this.mod;
            adr = key % mod;
            if(table[adr] != null && table[adr].key == key)
                return adr+1;
            else{
                int i=1,k;
                k = (adr + i) % mod;
                while(k != adr){
                    if(table[k] != null && table[k].key == key)
                        return k+1;
                    else{
                        ++i;
                        k = (adr + i) % mod;
                    }
                }
            }
        }
        return -1;
    }

    //在哈希表中查找键值对应的key,如果没有的话会返回-1
    public int dataToKey(E data){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            for (HashNode<E> eHashNode : table) {
                if(eHashNode != null){
                    if(eHashNode.data.equals(data))
                        return eHashNode.key;
                }
            }
        }

        return -1;
    }

    //
    private void reHash(boolean up){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            if(up)
                capacity = (int)(1.8*capacity);
            else
                capacity = (int)(0.6*capacity);
            datacount=0;
            mod = capacity;
            factor = 0.0;
            HashNode<E>[] tables = table;
            table = new HashNode[capacity];
            for (int i = 0; i < capacity; ++i) {
                table[i] = null;
            }
            for(int i = 0;i<tables.length;++i){
                if(tables[i] != null)
                    insertNode(tables[i].key,tables[i].data);
            }
        }
    }

    class HashNode<E>{
        /**
         * key为对应关键字
         * data为对应的键值
         */
        int key;
        E data;
        HashNode(int key,E data){
            this.key = key;
            this.data = data;
        }
        void setKey(int key){
            this.key = key;
        }

        void setData(E data){
            this.data = data;
        }
    }

    public static void main(String[] args) {
        MyHashTable<String> hh = new MyHashTable<String>(4);
        for(int i=0;i<20;i++){
            hh.insertNode(i*i,""+i);
            System.out.println(hh.searchNode(i*i)+"  ("+hh.getDatacount()+","+hh.getCapacity()+")  "+hh.indexOfKey(i*i));
        }
        System.out.println("#############");
        for(int i=0;i<20;i++){
            System.out.print(hh.dataToKey(""+i));
            hh.deleteNode(i*i);
            System.out.println(" ( "+hh.getDatacount()+","+hh.getCapacity()+" )");
        }
        System.out.println("#############");
    }
}

用拉链法解决哈希冲突

方法设定跟前面的一样,只是数据组织稍稍存在差别在,并且装载因子可以大于1,而且reHash方法需要重新进行设计

还是需要对哈希表的长度进行一定的扩容,当装载因子大于2时,哈希表长度扩容为原来的1.8倍,当装载因子小于0.3时

哈希表长度缩容为原来的0.6倍,当然这样的规定只是笔者自己依据常识规定的reHash条件,可能不是最适合的reHash条件

用c++实现

数据组织

哈希结点以链式结点存储方式,数据组织如下

template<typename T>
struct HashChainNode{

    KeyType key; //关键字
    T data;  //存储的值
    HashChainNode<T> *next;  //下一个结点
    
    HashChainNode(KeyType key,T data){
        this->key = key;
        this->data = data;
        this->next = nullptr;
    }

    void setKey(KeyType key){
        this->key = key;
    }

    void setData(T data){
        this->data = data;
    }
};

组织哈希表的类HashChainTable

template<typename T>
class HashChainTable{

public:
    //构造函数,取余因子 = 哈希表长度
    HashChainTable(int capacity) {
        if(capacity <= 0)
            throw std::runtime_error("capacity <= 0");
        this->mod = capacity;
        this->capacity = capacity;
        this->datacount = 0;
        this->factor = 0.0;
        this->table = std::vector<HashChainNode<T>*>(capacity);
    }
	//析构函数
    ~HashChainTable(){
        for(auto node : table){
            HashChainNode<T> *p = node;
            while(p != nullptr){
                p = p->next;
                delete node;
                node = p;
            }
            delete p;
        }
    }

public:
    //往哈希表中插入结点
    bool insertNode(KeyType key,T data){
        if(capacity <= 0)
            throw std::runtime_error("capacity <= 0");
        else if(key == EMPTYKEY)
            return false;
        else{
            if(factor > 2)
                reHash(true);
            int adr = key % this->mod;
            if(table[adr] == nullptr){
                table[adr] = new HashChainNode<T>(key,data);
            }
            else{
                HashChainNode<T> *temp = new HashChainNode<T>(key, data);
                temp->next = table[adr];
                table[adr] = temp;
            }
            datacount++;
            reComplateFactor();
        }
        return true;
    }

    //删除哈希表中的结点
    bool deleteNode(KeyType key){
        if(capacity <= 0){
            throw std::runtime_error("capacity <= 0");
        }else if(key == EMPTYKEY){
            return false;
        }else{
            int adr = key % mod;
            HashChainNode<T> *p = table[adr];
            if(p == nullptr)
                return false;
            HashChainNode<T> *q = p->next;
            if(p->key == EMPTYKEY)
                return false;
            else{
                if(p->key == key){
                    if(q == nullptr)
                        table[adr] = new HashChainNode<T>();
                    else
                        table[adr] = q;
                    delete p;
                    p = nullptr;
                    datacount--;
                    reComplateFactor();
                    return true;
                }else{
                    while(q != nullptr && q->key != key){
                        p = q;
                        q = q->next;
                    }
                    if(q != nullptr){
                        p->next = q->next;
                        delete q;
                        q = nullptr;
                        datacount--;
                        reComplateFactor();
                        return true;
                    }
                }
            }
        }
        return false;
    }

    //查找哈希表中的结点
    bool searchNode(KeyType key, T &result){
        if(capacity <= 0)
            throw std::runtime_error("capacity <= 0");
        else if(key == EMPTYKEY)
            return false;
        else{
            int adr = key % this->mod;
            HashChainNode<T> *p = table[adr];
            while(p != nullptr){
                if(p->key == key){
                    result = p->data;
                    return true;
                }else{
                    p = p->next;
                }
            }
        }
        return false;
    }
	//返回哈希表当前的数据长度
    int dataCount(){
        return datacount;
    }
	//返回哈希表的存储能力
    int dataCapacity(){
        return capacity;
    }

    KeyType dataToKey(T data){
        if(capacity <= 0)
            throw std::runtime_error("capacity <= 0");
        else{
            for(auto hashnode: table){
                if(hashnode == nullptr)
                    continue;
                else{
                    HashChainNode<T> *p = hashnode;
                    while(p!= nullptr){
                        if(p->data == data)
                            return p->key;
                        p = p->next;
                    }
                }
            }
        }
        return -1;
    }

public:
    //重载运算符
    T operator[](KeyType key){
        T result = NULL;
        searchNode(key,result);
        return result;
    }

private:
    //私有方法

    //重新计算装载因子
    void reComplateFactor(){
        factor = (float)datacount / (float)capacity;
    }

    //reHash
    void reHash(bool up){
        if(up)
            capacity = 1.8*capacity;
        else
            capacity = 0.6*capacity;
        datacount = 0;
        mod = capacity;
        factor = 0;
        auto tables = table;
        table = std::vector<HashChainNode<T>*>(capacity);
        for(auto node : tables){
            while(node != nullptr && node->key != EMPTYKEY){
                insertNode(node->key,node->data);
                node = node->next;
            }
        }

    }
private:
    float factor{};  //装载因子
    int datacount{}; //数据总量
    int capacity{};  //哈希存储列表
    int mod{};  //取余因子
    std::vector<HashChainNode<T>*> table{};  //数据存储表格
};
用Java实现
package myHash;

public class MyHashChainTable<E>{


    private int mod;  //取余因子
    private int capacity;  //哈希表的存储长度
    private int datacount;  //当前存储数值
    private double factor;  //装载因子
    private HashChainNode<E>[] table;  //存储数值的hash表

    //构造函数
    MyHashChainTable(int capacity){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            this.capacity = capacity;
            this.mod = capacity;
            this.factor = 0.0;
            this.datacount = 0;
            table = new HashChainNode[capacity];
            initTable(table);
        }
    }

    public boolean insertNode(int key,E data){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else if(key < 0)
            return false;
        else{
            if(factor > 2)
                reHash(true);
            int adr = key % this.mod;
            if(table[adr] == null){
                table[adr] = new HashChainNode<E>(key,data);
            }else{
                HashChainNode<E> newnode = new HashChainNode<E>(key,data);
                newnode.next = table[adr];
                table[adr] = newnode;
            }
            datacount++;
            reComplateFactor();
        }
        return true;
    }



    //删除结点
    public boolean deleteNode(int key){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else if(key < 0)
            return false;
        else{
            int adr = key % this.mod;
            HashChainNode<E> p = table[adr];
            if(p == null)
                return false;
            else{
                HashChainNode<E> q = p.next;
                if(p.key == key){
                    table[adr] = q;
                    datacount--;
                    reComplateFactor();
                    if(factor < 0.3)
                        reHash(false);
                    return true;
                }
                else{
                    while(q != null && q.key != key){
                        p = q;
                        q = q.next;
                    }
                    if(q != null){
                        p.next = q.next;
                        datacount--;
                        reComplateFactor();
                        if(factor < 0.3)
                            reHash(false);
                        return true;
                    }
                }
            }
        }
        return false;
    }


    public E searchNode(int key){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            int adr = key % this.mod;
            HashChainNode<E> p = table[adr];
            while(p != null){
                if(p.key == key)
                    return p.data;
            }
        }
        return null;
    }

    public int dataToKey(E data){
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            for(HashChainNode<E> node:table){
                while(node != null){
                    if(node.data.equals(data)){
                        return node.key;
                    }
                    node = node.next;
                }
            }
        }
        return -1;
    }

    public int getDatacount(){
        return datacount;
    }

    public int getCapacity(){
        return capacity;
    }

    //重新计算装载因子
    private void reComplateFactor(){
        factor = (double) datacount / (double) capacity;
    }

    //初始化table
    private void initTable(HashChainNode<E>[] table){
        for(int i=0;i<table.length;++i){
            table[i] = null;
        }
    }

    private void reHash(boolean b) {
        if(capacity <= 0)
            throw new RuntimeException("capacity <= 0");
        else{
            if(b)
                capacity = (int)(1.8*capacity);
            else
                capacity = (int)(0.6*capacity);
            this.mod = capacity;
            datacount = 0;
            factor = 0;
            HashChainNode<E>[] temptable = table;
            table = new HashChainNode[capacity];
            initTable(table);
            for(HashChainNode<E> node:temptable){
                while(node != null){
                    insertNode(node.key,node.data);
                    node = node.next;
                }
            }
        }
    }

	//结点定义
    class HashChainNode<E>{

        int key;  //关键字
        E data;  //存储的值
        HashChainNode next;  //下一个结点

        HashChainNode(int key,E data){
            this.key = key;
            this.data = data;
            this.next = null;
        }

        public void setKey(int key){
            this.key = key;
        }

        public void setData(E data){
            this.data = data;
        }

    }

    //测试主函数
    public static void main(String[] args) {
        MyHashChainTable<String> hh = new MyHashChainTable<String>(4);
        for(int i=0;i<20;i++){
            hh.insertNode(i*i,""+i);
            System.out.println(hh.searchNode(i*i)+"  ("+hh.getDatacount()+","+hh.getCapacity()+")");
        }
        System.out.println("#############");
        for(int i=0;i<20;i++){
            System.out.print(hh.dataToKey(""+i));
            hh.deleteNode(i*i);
            System.out.println(" ( "+hh.getDatacount()+","+hh.getCapacity()+" )");
        }
        System.out.println("#############");
    }
}

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值