數據結構和算法系列13 五大查找之哈希查找

這一篇要總結的是五天查找的最後一篇,哈希查找,也稱爲散列查找(本文以哈希稱呼)。提起哈希,個人第一印象就是C#中的Hashtable類,它是由一組key/value的鍵值對組成的集合,它就是應用了散列技術。html

那麼,什麼是哈希查找呢?在弄清楚什麼是哈希查找以前,咱們要弄清楚哈希技術,哈希技術是在記錄的存儲位置和記錄的關鍵字之間創建一個肯定的對應關係f,使得每一個關鍵字key對應一個存儲位置f(key)。查找時,根據這個肯定的對應關係找到給定值的映射f(key),若查找集合中存在這個記錄,則一定在f(key)的位置上。哈希技術既是一種存儲方法,也是一種查找方法。數組

六種哈希函數的構造方法:dom

1,直接定址法:函數

函數公式:f(key)=a*key+b (a,b爲常數)spa

這種方法的優勢是:簡單,均勻,不會產生衝突。可是須要事先知道關鍵字的分佈狀況,適合查找表較小而且連續的狀況。設計

2,數字分析法:code

好比咱們的11位手機號碼「136XXXX7887」,其中前三位是接入號,通常對應不一樣運營公司的子品牌,如130是聯通如意通,136是移動神州行,153是電信等。中間四們是HLR識別號,表示用戶歸屬地。最後四們纔是真正的用戶號。htm

若咱們如今要存儲某家公司員工登記表,若是用手機號碼做爲關鍵字,那麼極有可能前7位都是相同的,因此咱們選擇後面的四們做爲哈希地址就是不錯的選擇。blog

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),即發生衝突。

方法一:開放定址法:

開放定址法就是一旦發生了衝突,就去尋找下一個空的哈希地址,只要哈希表足夠大,空的哈希地址老是能找到,而後將記錄插入。這種方法是最經常使用的解決衝突的方法。

方法二:鏈地址法:

下面是實現代碼:

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;
        }
    }
}
複製代碼

程序輸出結果如圖:

ds41

 

C語言版:

複製代碼
#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define SUCCESS 1
#define UNSUCCESS 0

#define HASHSIZE 7 /* 定義散列表長爲數組的長度 */
#define NULLKEY -32768 

typedef int Status;    
typedef struct
{
    int *elem; /* 數據元素存儲地址,動態分配數組 */
    int count; /*  當前數據元素個數 */
}HashTable;

int m=0; /* 散列表表長,全局變量 */

/*初始化*/
Status Init(HashTable *hashTable)
{
    int i;

    m=HASHSIZE;
    hashTable->elem= (int *)malloc(m*sizeof(int)); //申請內存
    hashTable->count=m;
    for (i=0;i<m;i++)
    {
        hashTable->elem[i]=NULLKEY;
    }
    return OK;
}

/*哈希函數(除留餘數法)*/
int Hash(int data)
{
    return data%m;
}

/*插入*/
void Insert(HashTable *hashTable,int data)
{
    int hashAddress=Hash(data); //求哈希地址

    //發生衝突
    while(hashTable->elem[hashAddress]!=NULLKEY)
    {
        //利用開放定址的線性探測法解決衝突
        hashAddress=(++hashAddress)%m;
    }

    //插入值
    hashTable->elem[hashAddress]=data;
}

/*查找*/
int Search(HashTable *hashTable,int data)
{
    int hashAddress=Hash(data); //求哈希地址

    //發生衝突
    while(hashTable->elem[hashAddress]!=data)
    {
        //利用開放定址的線性探測法解決衝突
        hashAddress=(++hashAddress)%m;

        if (hashTable->elem[hashAddress]==NULLKEY||hashAddress==Hash(data)) return -1;
    }

    //查找成功
    return hashAddress;
}

/*打印結果*/
void Display(HashTable *hashTable)
{
    int i;
    printf("\n**********展現結果**********\n");

    for (i=0;i<hashTable->count;i++)
    {
        printf("%d ",hashTable->elem[i]);
    }

    printf("\n**********展現完畢**********\n");
}

void main()
{
    int i,j,result;
    HashTable hashTable;
    int arr[HASHSIZE]={13,29,27,28,26,30,38};

    printf("***************哈希查找(C語言版)***************\n");

    //初始化哈希表
    Init(&hashTable);

    //插入數據
    for (i=0;i<HASHSIZE;i++)
    {
        Insert(&hashTable,arr[i]);
    }
    Display(&hashTable);

    //查找數據
    result= Search(&hashTable,29);
    if (result==-1) printf("對不起,沒有找到!");
    else printf("29在哈希表中的位置是:%d",result);

    getchar();
}
複製代碼

程序輸出結果如圖:

ds42





 

原文轉自:http://www.cnblogs.com/mcgrady/archive/2013/09/01/3294871.html
原做者爲 mcgrady。請尊重原做者版權
相關文章
相關標籤/搜索