代码改变世界

哈希表概念

2011-01-20 23:13  Clingingboy  阅读(569)  评论(0编辑  收藏  举报

 

问题:

在内存中用数组存储50000个单词,用数组下标去找单词很快,但我们在单词软件中不知道单词在数组中的下标.

如a的下标为0,z最后单词的下标为49999,如果以这种方式来找的话,那么查找z开头的单词速度就会相当的慢。

方案:

想一个方法快速的找到单词相对应的下标,哈希函数的定义

将数据元素的关键字K作为自变量,通过一定的函数关系(称为哈希函数),计算出的值,即为该元素的存储地址

即其本身还是一个数组,只不过通过一个算法快速的找到其下标地址而已.

哈希函数的冲突

当用哈希函数新增元素时,算出来该元素的存储地址若已经存有元素的话,那么就称之为冲突,那么就得想办法来避免这种冲突.

示例

下面来看一个实际的例子.只做演示

以一个元素为20个的数组为例子

image

1.哈希函数

取数组大小的余数,即5%20和25%20是相同的.

2.插入25,余数为5

image

所以其存储地址为5

3.插入5

image

由于插入5时,与25的存储地址发生了冲突,就需要处理,这里使用了开发地址法的线性探测方法

线性探测

即若遇到地址发生了冲突,则沿着数组的下标继续寻找空白单元

查找

如查找25,算出来的存储地址为5,马上就能找到,即实现了1次查找,删除操作与查找相同,找到以后赋空值就可

实现

public class IntHashTable
{
    private int[] items;

    public IntHashTable(int size)
    {
        items = new int[size];
        for (int i = 0; i < items.Length; i++)
        {
            items[i] = -1;
        }
    }


    private int hashFunc(int key)
    {
        return key % items.Length;
    }

    public void Insert(int item)
    {
        int hashVal = hashFunc(item);
        while (items[hashVal]!=-1)
        {
            hashVal++;
        }
        items[hashVal] = item;
    }

    public int Find(int key)
    {
        int hashVal = hashFunc(key);
        while (items[hashVal]!=-1)
        {
            if (items[hashVal] == key)
                return hashVal;
            hashVal++;
            hashVal = hashFunc(hashVal);
        }
        return -1;
    }

    public void Delete(int key)
    {
        int hashVal = hashFunc(key);
        while (items[hashVal] != -1)
        {
            if (items[hashVal] == key)
            {
                items[hashVal] = -1;
            }
            hashVal++;
            hashVal = hashFunc(hashVal);
        }
    }

    public static void Main()
    {
        IntHashTable ht = new IntHashTable(20);

        ht.Insert(11);
        ht.Insert(12);
        ht.Insert(32);
        ht.Find(32);
        ht.Delete(32);
    }
}

链地址法

将发生冲突的存在一个链表里面,并且保持链表有序.如下

public class Link
{
    public int Value { get; set; }

    public Link Next { get; set; }
}

class SortedList
{
    private Link first;

    public SortedList()
    { 
        first = null;
    }

    public void Insert(Link theLink)
    {
        int key = theLink.Value;
        Link previous = null;
        Link current = first;

        while (current != null && key > current.Value)
        {
            previous = current;
            current = current.Next;
        }
        if (previous == null)
            first = theLink;
        else
            previous.Next = theLink;
        theLink.Next = current;
    }

    public void Delete(int key)
    {
        Link previous = null;
        Link current = first;

        while (current != null && key != current.Value)
        {
            previous = current;
            current = current.Next;
        }

        if (previous == null)
            first = first.Next;
        else
            previous.Next = current.Next;
    }

    public Link Find(int key)
    {
        Link current = first;

        while (current != null && current.Value <= key)
        {
            if (current.Value == key)
                return current;
            current = current.Next;
        }
        return null;
    }
}

public class LinkHashTable
{
    private SortedList[] items;

     public LinkHashTable(int size)
    {
        items = new SortedList[size];
        for (int i = 0; i < items.Length; i++)
        {
            items[i] = new SortedList();
        }
    }


    private int hashFunc(int key)
    {
        return key % items.Length;
    }

    public void Insert(int item)
    {
        int hashVal = hashFunc(item);
        var link = items[hashVal];
        link.Insert(new Link() { Value = item });
    }

    public int Find(int key)
    {
        int hashVal = hashFunc(key);
        var link = items[hashVal];
        return link.Find(key).Value;
        return -1;
    }

    public void Delete(int key)
    {
        int hashVal = hashFunc(key);
        var link = items[hashVal];
        link.Delete(key);
    }
}

二次探测

当发生冲突时才进行探测,犹如人左右张望,先看右边有无空位,再看左边有无空位,即先确认左右,

image

如上图的最后一个关键字3,

3和47冲突,先找到4(97)又发生冲突则找左边2空位.

遵照哈希函数公式即可