查找算法之哈希查找

引言:哈希查找,也称为散列查找(本文以哈希称呼),在介绍哈希查找之前,我们先了解一下什么是哈希函数、哈希表

1、哈希函数

哈希技术是在记录的存储位置和它的key之间建立一个确定的对应关系f,使得每个key对应一个存储位置f(key)。建立了key与存储位置的映射关系,公式如存储位置 = f(key 这里把这种对应关系f称为哈希(Hash)函数

2、哈希表

采用哈希技术将key存在在一块连续的存储空间中,这块连续存储空间称为哈希表是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。

六种哈希函数的构造方法:

1,直接定址法:

函数公式:f(key)=a*key+b (a,b为常数) 

比如:关键字是2,a=1,b=1,那么2+1=3就为存储位置。

这种方法的优点是:简单,均匀,不会产生冲突。但是需要事先知道关键字的分布情况,适合查找表较小并且连续的情况。

2,数字分析法:

比如我们的11位手机号码“136XXXX7887”,其中前三位是接入号,一般对应不同运营公司的子品牌,如130是联通如意通,136是移动神州行,153是电信等。中间四们是HLR识别号,表示用户归属地。最后四们才是真正的用户号。

若我们现在要存储某家公司员工登记表,如果用手机号码作为关键字,那么极有可能前7位都是相同的,所以我们选择后面的四们作为哈希地址就是不错的选择。

3,平方取中法:

故名思义,比如关键字是1234,那么它的平方就是1522756,再抽取中间的3位就是227作为哈希地址。

4,折叠法:

折叠法是将关键字从左到右分割成位数相等的几个部分(最后一部分位数不够可以短些),然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址。

比如我们的关键字是9876543210,哈希表表长三位,我们将它分为四组,987|654|321|0 ,然后将它们叠加求和987+654+321+0=1962,再求后3位即得到哈希地址为962,哈哈,是不是很有意思。

5,除留余数法:

函数公式:f(key)=key mod p (p<=m)m为哈希表表长。

这种方法是最常用的哈希函数构造方法。

6,随机数法:

函数公式:f(key)= random(key)。

这里random是随机函数,当关键字的长度不等,采用这种方法比较合适。

设计得最好的哈希函数也不可能完全避免冲突,当我们在使用哈希函数后发现两个关键字key1!=key2,但是却有f(key1)=f(key2),即发生冲突。解决冲突,有2种方法

方法一:开放定址法:

开放定址法就是一旦发生了冲突,就去寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总是能找到,然后将记录插入。这种方法是最常用的解决冲突的方法。

C#实现代码:

namespace HashSearch.CSharp

{

    class Program

    {

        //初始化哈希表

        static int hashLength = 7;

        static int[] hashTable= new int[hashLength];

 

        //原始数据

        static List<int> list = new List<int>() { 13,29,27,28,26,30,38 };

 

        static void Main(string[] args)

        {

            Console.WriteLine("********************哈希查找(C#版)********************\n");

            

            //创建哈希表

            for (int i = 0; i < list.Count; i++)

            {

                Insert(hashTable,list[i]);

            }

            Console.WriteLine("展示哈希表中的数据:{0}",String.Join(",",hashTable));

 

            while (true)

            {

                //哈希表查找

                Console.Write("请输入要查找的数据:");

                int data = int.Parse(Console.ReadLine());

                var result = Search(hashTable, data);

                if (result == -1) Console.WriteLine("对不起,没有找到!");

                else Console.WriteLine("数据的位置是:{0}", result);

            }

        }

 

        /// <summary>

        /// 哈希表插入

        /// </summary>

        /// <param name="hashTable">哈希表</param>

        /// <param name="data">待插入值</param>

        public static void Insert(int[] hashTable, int data)

        {

            //哈希函数,除留余数法

            int hashAddress = Hash(hashTable,data);

 

            //如果不为0,则说明发生冲突

            while (hashTable[hashAddress] != 0)

            {

                //利用开放定址的线性探测法解决冲突

                hashAddress = (++hashAddress) % hashTable.Length;

            }

 

            //将待插入值存入字典中

            hashTable[hashAddress] = data;

        }

 

        /// <summary>

        /// 哈希表查找

        /// </summary>

        /// <param name="hashTable">哈希表</param>

        /// <param name="data">待查找的值</param>

        /// <returns></returns>

        public static int Search(int[] hashTable, int data)

        {

            //哈希函数,除留余数法

            int hashAddress = Hash(hashTable,data);

 

            //冲突发生

            while (hashTable[hashAddress] != data)

            {

                //利用开放定址的线性探测法解决冲突

                hashAddress = (++hashAddress) % hashTable.Length;

 

                //查找到了开放单元或者循环回到原点,表示查找失败

                if (hashTable[hashAddress] == 0 || hashAddress==Hash(hashTable,data)) return -1;

            }

            //查找成功,返回值的下标

            return hashAddress;

        }

 

        /// <summary>

        /// 哈希函数(除留余数法)

        /// </summary>

        /// <param name="hashTable">待操作哈希表</param>

        /// <param name="data"></param>

        /// <returns>返回数据的位置</returns>

        public static int Hash(int[] hashTable, int data)

        {

            return data % hashTable.Length;

        }

    }

}

(2)   链地址法

将哈希值相同的数据元素存放在一个链表中,在查找哈希表的过程中,当查找到这个链表时,必须采用线性查找方法








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值