9-散列表

2019-09-29  本文已影响0人  董泽平

散列表

1.什么是散列表?
散列表就是通过一个映射函数,把每个数据映射为一个数组下标,按照下标存储起来,当我们访问这个数据时,只需要通过映射函数计算出下标,就可以找到这个数。

2.散列表映射函数
从散列表定义来看,散列表最重要的是设计映射函数,映射函数决定效率和减少地址冲突,常见的映射函数有下面的几个。。。

3.散列表的冲突问题
不管我们设计的散列函数多牛逼。都无法避免数据映射冲突问题,冲突就是两个数据经过映射函数处理后得到的数据是一样的。这个时候不可能把它两放一起吧。因此发明了解决地址冲突问题的办法

下面我们用两种解决冲突的办法去实现一个完整的散列表。

一:开放地址法

1.首先准备大小为9的数组。设计映射函数是除留取余法。

映射函数:key = data % size(size就是散列表长度,data就是数据)

find2.jpg

2.假设我们有数据5,4,8,15,6,7时,对应散列表情况,我们按照设计的映射函数逐个将数据放入到散列表,整个过程如下图所示。

find4.jpg

3.现在我们已经学会了如何插入数据到散列表,现在开始发挥它的威力了。散列表设计的本质就是用于搜索查找记录。

假设我们还是用上面已经插入好数据的散列表,我们分别要查找数据6,7,9

开放地址法的代码

//具有自动扩容性的散列表(装载因子最大为0.75时自动扩容)
Hash_Table_Array::Hash_Table_Array()
{
    this->maxSize = 10;
    this->data = new int[this->maxSize];
    this->size = 0;
    for (int i = 0; i < this->maxSize; i++)
    {
        this->data[i] = 99999;//标记数组初始值(标空)
    }
}
//数据的插入
void Hash_Table_Array::Insert(int data)
{
    int *newData,i,index;
    //1.每次插入数据前,先检测是否超过了最大装载因子
    if (this->size*1.0 >= this->maxSize*0.75)
    {
        newData = new int[this->maxSize * 2];
        for (i = 0; i < this->maxSize * 2; i++)
        {
            newData[i] = 99999;
        }
        //将原数据重新映射地址
        for (i = 0; i < this->maxSize;i++)
        {
            
            if (this->data[i] != 99999)
            {
                index = this->data[i] % (2 * this->maxSize);
                while (newData[index] != 99999)
                {
                    index = (index + 1) % (2 * this->maxSize);
                }
                newData[index] = this->data[i];
            }
        }
        this->maxSize = 2 * this->maxSize;
        delete[] this->data;
        this->data = newData;
        index = data % this->maxSize;
        //开放地址法
        while (this->data[index] != 99999)
        {
            index = (index + 1) % this->maxSize;
        }
        this->data[index] = data;
        this->size++;
    }
    else {
        index = data % this->maxSize;
        //开放地址法
        while (this->data[index]!=99999)
        {
            index = (index + 1) % this->maxSize;
        }
        this->data[index] = data;
        this->size++;
    }
}

//数据的查询
int Hash_Table_Array::isFind(int data)
{
    int index,sum=0;
    index = data % this->maxSize;
    //开放地址法
    while (this->data[index] != data)
    {
        index = (index + 1) % this->maxSize;
        sum++;
        //如果继续查找数据为空,或者回到原点,表示该数据不存在
        if (this->data[index] == 99999 || sum == this->maxSize)
        {
            return -1;
        }
    }
    return index;
}

二:链地址法

1.首先准备大小为10的边表数组,设计映射函数仍然是除留取余法,处理冲突的办法时链地址法

映射函数: key=data%size

find3.jpg

2.假设我们有数据5,4,8,15,6按个插入,如下图所示

find5.jpg

3.链地址法的查找

假设我们要查找数据15,8,9

4.链地址法的代码

//初始化处理
Hash_Table_List::Hash_Table_List()
{
    int i;
    this->len = 0;
    this->length = 10;
    this->head = new Node[10];
    for (i = 0; i < 10; i++)
    {
        this->head[i].data = i;
        this->head[i].next = NULL;
    }
}
//插入数据
void Hash_Table_List::Insert(int data)
{
    //超过装载因子,自动扩容
    Node *newHead,*p,*q;
    int i,value,index;
    if (this->len*1.0 >= this->length * 0.75)
    {
        newHead = new Node[this->length * 2];
        for (i = 0; i < this->length * 2; i++)
        {
            newHead[i].data = i;
            newHead[i].next = NULL;
        }
        //将旧数据,逐个重新映射到新的散列表上
        for (i = 0; i < this->length; i++)
        {
            p = this->head[i].next;
            while (p)
            {
                //原数据
                value = p->data;
                //映射后地址
                index = p->data % (this->length * 2);
                //根据地址,放到新散列表的后面
                q = new Node;
                q->data = value;
                q->next = newHead[index].next;
                newHead[index].next = q;
                //删除旧散列表边表结构
                this->head[i].next = p->next;
                delete p;
                p = this->head[i].next;
            }
        }
        if (this->head)
        {
            delete[] this->head;
        }
        this->head = newHead;
        this->length = this->length * 2;

        //插入数据进去
        index = data % this->length;
        q = new Node;
        q->data = data;
        q->next = this->head[index].next;
        this->head[index].next = q;
        this->len++;
    }
    //没有超过装载因子,不需要扩容
    else
    {
        index = data % this->length;
        q = new Node;
        q->data = data;
        q->next = this->head[index].next;
        this->head[index].next = q;
        this->len++;
    }
}



//查找处理
int Hash_Table_List::isFind(int data)
{
    Node *p;
    int index;
    index = data %this->length;
    p = this->head[index].next;
    while (p != NULL && p->data != data)
    {
        p = p->next;
    }
    if (p == NULL)
    {
        return -1;//没找到
    }
    //返回链表的下标
    else {
        return index;
    }
}

获取完整代码

我分别用C,C++,JAVA三种主流语言编写了完整代码,请大家指导批正,一起学习。

点击查看

上一篇 下一篇

猜你喜欢

热点阅读