哈希表(Hash Table)原理及其實現

原理

介紹

哈希表(Hash table,也叫散列表), 是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它經過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫作散列函數,存放記錄的數組叫作散列表。html

哈希表hash table(key,value) 的作法其實很簡單,就是把Key經過一個固定的算法函數既所謂的哈希函數轉換成一個整型數字,而後就將該數字對數組長度進行取餘,取餘結果就看成數組的下標,將value存儲在以該數字爲下標的數組空間裏。ios

而當使用哈希表進行查詢的時候,就是再次使用哈希函數將key轉換爲對應的數組下標,並定位到該空間獲取value,如此一來,就能夠充分利用到數組的定位性能進行數據定位。算法

哈希表最大的優勢,就是把數據的存儲和查找消耗的時間大大下降,幾乎能夠當作是常數時間;而代價僅僅是消耗比較多的內存。然而在當前可利用內存愈來愈多的狀況下,用空間換時間的作法是值得的。另外,編碼比較容易也是它的特色之一。 哈希表又叫作散列表,分爲「開散列」 和「閉散列」。數據庫

咱們使用一個下標範圍比較大的數組來存儲元素。能夠設計一個函數(哈希函數, 也叫作散列函數),使得每一個元素的關鍵字都與一個函數值(即數組下標)相對應,因而用這個數組單元來存儲這個元素;也能夠簡單的理解爲,按照關鍵字爲每一 個元素「分類」,而後將這個元素存儲在相應「類」所對應的地方。數組

可是,不可以保證每一個元素的關鍵字與函數值是一一對應的,所以極有可能出現對於不一樣的元素,卻計算出了相同的函數值,這樣就產生了「衝突」,換句話說,就是把不一樣的元素分在了相同的「類」之中。後面咱們將看到一種解決「衝突」的簡便作法。 總的來講,「直接定址」與「解決衝突」是哈希表的兩大特色。數據結構

哈希函數構造

就是映射函數構造,看某個元素具體屬於哪個類別。 
除餘法: 選擇一個適當的正整數 p ,令 h(k ) = k mod p ,這裏, p 若是選取的是比較大的素數,效果比較好。並且此法很是容易實現,所以是最經常使用的方法。最直觀的一種,上圖使用的就是這種散列法,公式: 
index = value % 16 
學過彙編的都知道,求模數實際上是經過一個除法運算獲得的,因此叫「除法散列法」。app

平方散列法 
求index是很是頻繁的操做,而乘法的運算要比除法來得省時(對如今的CPU來講,估計咱們感受不出來),因此咱們考慮把除法換成乘法和一個位移操做。公式: 
index = (value * value) >> 28 ( 右移,除以2^28。記法:左移變大,是乘。右移變小,是除)dom

數字選擇法: 若是關鍵字的位數比較多,超過長整型範圍而沒法直接運算,能夠選擇其中數字分佈比較均勻的若干位,所組成的新的值做爲關鍵字或者直接做爲函數值。函數

斐波那契(Fibonacci)散列法:平方散列法的缺點是顯而易見的,因此咱們能不能找出一個理想的乘數,而不是拿value自己看成乘數呢?答案是確定的。 
1,對於16位整數而言,這個乘數是40503 
2,對於32位整數而言,這個乘數是2654435769 
3,對於64位整數而言,這個乘數是11400714819323198485 
這幾個「理想乘數」是如何得出來的呢?這跟一個法則有關,叫黃金分割法則,而描述黃金分割法則的最經典表達式無疑就是著名的斐波那契數列,即如此形式的序列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377, 610, 987, 1597, 2584, 4181, 6765, 10946,…。另外,斐波那契數列的值和太陽系八大行星的軌道半徑的比例出奇吻合。 
對咱們常見的32位整數而言,公式: 
index = (value * 2654435769) >> 28性能

衝突處理

線性從新散列技術易於實現且能夠較好的達到目的。令數組元素個數爲 S ,則當 h(k) 已經存儲了元素的時候,依次探查 (h(k)+i) mod S , i=1,2,3…… ,直到找到空的存儲單元爲止(或者從頭至尾掃描一圈仍未發現空單元,這就是哈希表已經滿了,發生了錯誤。固然這是能夠經過擴大數組範圍避免的)。

舉例

哈希表支持的運算主要有:初始化(makenull)、哈希函數值的運算(h(x))、插入元素(insert)、查找元素(member)。 設插入的元素的關鍵字爲 x ,A 爲存儲的數組。

僞代碼 
初始化:

const empty=maxlongint; // 用很是大的整數表明這個位置沒有存儲元素  
p=9997; // 表的大小  
procedure makenull;  
var i:integer;  
begin  
for i:=0 to p-1 do  
A[i]:=empty;  
End;  

哈希函數值的運算根據函數的不一樣而變化,例如除餘法的一個例子:

function h(x:longint):Integer;  
begin  
h:= x mod p;  
end;

咱們注意到,插入和查找首先都須要對這個元素定位,即若是這個元素若存在,它應該存儲在什麼位置,所以加入一個定位的函數 locate:

function locate(x:longint):integer;  
var orig,i:integer;  
begin  
orig:=h(x);  
i:=0;  
while (i<S)and(A[(orig+i)mod S]<>x)and(A[(orig+i)mod S]<>empty) do  
inc(i);  
//當這個循環停下來時,要麼找到一個空的存儲單元,要麼找到這個元  
//素存儲的單元,要麼表已經滿了  
locate:=(orig+i) mod S;  
end;   

插入元素 :

procedure insert(x:longint);  
var posi:integer;  
begin  
posi:=locate(x); //定位函數的返回值  
if A[posi]=empty then A[posi]:=x  
else error; //error 即爲發生了錯誤,固然這是能夠避免的  
end;

查找元素是否已經在表中:

procedure member(x:longint):boolean;  
var posi:integer;  
begin  
posi:=locate(x);  
if A[posi]=x then member:=true  
else member:=false;  
end;  

當數據規模接近哈希表上界或者下界的時候,哈希表徹底不可以體現高效的特色,甚至還不如通常算法。可是若是規模在中央,它高效的特色能夠充分體現。試驗代表當元素充滿哈希表的 90% 的時候,效率就已經開始明顯降低。這就給了咱們提示:若是肯定使用哈希表,應該儘可能使數組開大,但對最太大的數組進行操做也比較費時間,須要找到一個平衡點。一般使它的容量至少是題目最大需求的 120% ,效果比較好(這個僅僅是經驗,沒有嚴格證實)。

何時適合應用哈希表呢?若是發現解決這個問題時常常要詢問:「某個元素是否在已知集合中?」,也就是須要高效的數據存儲和查找,則使用哈希表是最好不過的了!那麼,在應用哈希表的過程當中,值得注意的是什麼呢?

哈希函數的設計很重要。一個很差的哈希函數,就是指形成不少衝突的狀況,從前面的例子已經能夠看出來,解決衝突會浪費掉大量時間,所以咱們的目標 就是盡力避免衝突。前面提到,在使用「除餘法」的時候,h(k)=k mod p ,p 最好是一個大素數。這就是爲了盡力避免衝突。爲何呢?假設 p=1000 ,則哈希函數分類的標準實際上就變成了按照末三位數分類,這樣最多1000類,衝突會不少。通常地說,若是 p 的約數越多,那麼衝突的概率就越大。

簡單的證實:假設 p 是一個有較多約數的數,同時在數據中存在 q 知足 gcd(p,q)=d >1 ,即有 p=a*d , q=b*d, 則有 q mod p= q – p* [q div p] =q – p*[b div a] . ① 其中 [b div a ] 的取值範圍是不會超過 [0,b] 的正整數。也就是說, [b div a] 的值只有 b+1 種可能,而 p 是一個預先肯定的數。所以 ① 式的值就只有 b+1 種可能了。這樣,雖然mod 運算以後的餘數仍然在 [0,p-1] 內,可是它的取值僅限於 ① 可能取到的那些值。也就是說餘數的分佈變得不均勻了。容易看出, p 的約數越多,發生這種餘數分佈不均勻的狀況就越頻繁,衝突的概率越高。而素數的約數是最少的,所以咱們選用大素數。記住「素數是咱們的得力助手」。

另外一方面,一味的追求低衝突率也很差。理論上,是能夠設計出一個幾乎完美,幾乎沒有衝突的函數的。然而,這樣作顯然不值得,由於這樣的函數設計 很浪費時間並且編碼必定很複雜,與其花費這麼大的精力去設計函數,還不如用一個雖然衝突多一些可是編碼簡單的函數。所以,函數還須要易於編碼,即易於實 現。綜上所述,設計一個好的哈希函數是很關鍵的。而「好」的標準,就是較低的衝突率和易於實現。另外,使用哈希表並非記住了前面的基本操做就能以不變應萬變的。有的時候,須要按照題目的要求對哈希表的結構做一些改進。每每一些簡單的改進就能夠帶來巨大的方便。

這些只是通常原則,真正遇到試題的時候實際狀況變幻無窮,須要具體問題具體分析才行。

固然,以上講解的都是閉散列,若是使用鏈表,作開散列的話就能夠更方便存儲和刪除了。其實這個和以前作18-600的malloc裏面說的東西很相似。

拉鍊法

上面的方法使用數組實現的,其實不少時候須要使用數組鏈表來作。開一個數組,數組每一個元素都是一個鏈表。(hash函數選擇,針對字符串,整數,排列,具體相應的hash方法。 碰撞處理,一種是open hashing,也稱爲拉鍊法;另外一種就是closed hashing,也稱開地址法,opened addressing。)

使用除法散列: 
這裏寫圖片描述

使用斐波那契散列: 
這裏寫圖片描述

使用擴展法: 
d-left hashing中的d是多個的意思,咱們先簡化這個問題,看一看2-left hashing。2-left hashing指的是將一個哈希表分紅長度相等的兩半,分別叫作T1和T2,給T1和T2分別配備一個哈希函數,h1和h2。在存儲一個新的key時,同時用兩個哈希函數進行計算,得出兩個地址h1[key]和h2[key]。這時須要檢查T1中的h1[key]位置和T2中的h2[key]位置,哪個位置已經存儲的(有碰撞的)key比較多,而後將新key存儲在負載少的位置。若是兩邊同樣多,好比兩個位置都爲空或者都存儲了一個key,就把新key 存儲在左邊的T1子表中,2-left也由此而來。在查找一個key時,必須進行兩次hash,同時查找兩個位置。

hash索引跟B樹索引的區別。

Hash 索引結構的特殊性,其檢索效率很是高,索引的檢索能夠一次定位,不像B-Tree 索引須要從根節點到枝節點,最後才能訪問到頁節點這樣屢次的IO訪問,因此 Hash 索引的查詢效率要遠高於 B-Tree 索引。

(1)Hash 索引僅僅能知足」=」,」IN」和」<=>」查詢,不能使用範圍查詢。 
因爲 Hash 索引比較的是進行 Hash 運算以後的 Hash 值,因此它只能用於等值的過濾,不能用於基於範圍的過濾,由於通過相應的 Hash 算法處理以後的 Hash 值的大小關係,並不能保證和Hash運算前徹底同樣。

(2)Hash 索引沒法被用來避免數據的排序操做。 
因爲 Hash 索引中存放的是通過 Hash 計算以後的 Hash 值,並且Hash值的大小關係並不必定和 Hash 運算前的鍵值徹底同樣,因此數據庫沒法利用索引的數據來避免任何排序運算;

(3)Hash 索引不能利用部分索引鍵查詢。 
對於組合索引,Hash 索引在計算 Hash 值的時候是組合索引鍵合併後再一塊兒計算 Hash 值,而不是單獨計算 Hash 值,因此經過組合索引的前面一個或幾個索引鍵進行查詢的時候,Hash 索引也沒法被利用。

(4)Hash 索引在任什麼時候候都不能避免表掃描。 
前面已經知道,Hash 索引是將索引鍵經過 Hash 運算以後,將 Hash運算結果的 Hash 值和所對應的行指針信息存放於一個 Hash 表中,因爲不一樣索引鍵存在相同 Hash 值,因此即便取知足某個 Hash 鍵值的數據的記錄條數,也沒法從 Hash 索引中直接完成查詢,仍是要經過訪問表中的實際數據進行相應的比較,並獲得相應的結果。

(5)Hash 索引遇到大量Hash值相等的狀況後性能並不必定就會比B-Tree索引高。 
對於選擇性比較低的索引鍵,若是建立 Hash 索引,那麼將會存在大量記錄指針信息存於同一個 Hash 值相關聯。這樣要定位某一條記錄時就會很是麻煩,會浪費屢次表數據的訪問,而形成總體性能低下。

實現

問題描述:設計哈希表實現電話號碼查詢系統,實現下列功能: 
(1) 假定每一個記錄有下列數據項:電話號碼、用戶名、地址。 
(2) 一是從數據文件old.txt(本身現行建好)中讀入各項記錄,二是由系統隨機產生各記錄,而且把記錄保存到new.txt文件中以及顯示到屏幕上,記錄條數不要少於30,而後分別以電話號碼和用戶名爲關鍵字創建哈希表。 
(3) 分別採用僞隨機探測再散列法和再哈希法解決衝突。 
(4) 查找並顯示給定電話號碼的記錄;查找並顯示給定用戶名的記錄。 
(5) 將沒有查找的結果保存到結果文件Out.txt中,顯示查找結果前,要有提示語句。

// MyHashTable.cpp : 定義控制檯應用程序的入口點。
////設計哈希表實現電話號碼查詢系統
//說明:一是從文件old.txt中讀取的數據本身在程序運行前創建,
//      二是由系統隨機生成數據,在程序運行由隨機數產生器生成,而且將產生的記錄保存到 new.txt文件。

//存在的問題:使用隨機產生的文件,在顯示時出現亂碼


#include "stdafx.h"
#include<fstream>//文件流
#include<iostream>
#include <string>
using namespace std;

const int D[] = {3,5,8,11,13,14,19,21};//預約再隨機數
const int HASH_MAXSIZE = 50;//哈希表長度

//記錄信息類型
class DataInfo
{
public:
    DataInfo();//默認構造函數
    friend ostream& operator<<(ostream& out, const DataInfo& dataInfo); //重載輸出操做符
    //friend class HashTable;

//private:
    string name;//姓名
    string phone;//電話號碼
    string address;//地址 
    char sign;//衝突的標誌位,'1'表示衝突,'0'表示無衝突
};

DataInfo::DataInfo():name(""), phone(""), address(""), sign('0')
{

}

ostream& operator<<(ostream& out, const DataInfo& dataInfo) //重載輸出操做符
{
    cout << "姓名:" << dataInfo.name << "   電話:" << dataInfo.phone 
         << "    地址:" << dataInfo.address << endl;
    return out;
}

//存放記錄的哈希表類型
class HashTable
{
public:
    HashTable();//默認構造函數
    ~HashTable();//析構函數
    int Random(int key, int i);// 僞隨機數探測再散列法處理衝突
    void Hashname(DataInfo *dataInfo);//以名字爲關鍵字創建哈希表    
    int Rehash(int key, string str);// 再哈希法處理衝突   注意處理衝突還有鏈地址法等
    void Hashphone(DataInfo *dataInfo);//以電話爲關鍵字創建哈希表       
    void Hash(char *fname, int n);// 創建哈希表
    //fname 是數據儲存的文件的名稱,用於輸入數據,n是用戶選擇的查找方式  

    int Findname(string name);// 根據姓名查找哈希表中的記錄對應的關鍵碼
    int Findphone(string phone);// 根據電話查找哈希表中的記錄對應的關鍵碼
    void Outhash(int key);// 輸出哈希表中關鍵字碼對應的一條記錄
    void Outfile(string name, int key);// 在沒有找到時輸出未找到的記錄
    void Rafile();// 隨機生成文件,並將文件保存在 new.txt文檔中
    void WriteToOldTxt();//在運行前先寫入數據    

//private:
        DataInfo *value[HASH_MAXSIZE];
    int length;//哈希表長度
};

HashTable::HashTable():length(0)//默認構造函數
{
    //memset(value, NULL, HASH_MAXSIZE*sizeof(DataInfo*));
    for (int i=0; i<HASH_MAXSIZE; i++)
    {
        value[i] = new DataInfo();
    }
}

HashTable::~HashTable()//析構函數
{
    delete[] *value;
}

void HashTable::WriteToOldTxt()
{
    ofstream openfile("old.txt");
    if (openfile.fail())
    {
        cout << "文件打開錯誤!" << endl;
        exit(1);
    }

    string oldname;
        string oldphone;
    string oldaddress;

    for (int i=0; i<30; i++)
    {
        cout << "請輸入第" << i+1 << "條記錄:" << endl;        
        cin >> oldname ;
        cin >> oldphone;
        cin >> oldaddress;
        openfile << oldname << "  " << oldphone << "  " << oldaddress << "," << endl;
    }
    openfile.close();
}


int HashTable::Random(int key, int i)// 僞隨機數探測再散列法處理衝突
{//key是衝突時的哈希表關鍵碼,i是衝突的次數,N是哈希表長度
    //成功處理衝突返回新的關鍵碼,未進行衝突處理則返回-1
    int h;
    if(value[key]->sign == '1')//有衝突
    {
        h = (key + D[i]) % HASH_MAXSIZE;
        return h;
    }
    return -1;
}

void HashTable::Hashname(DataInfo *dataInfo)//以名字爲關鍵字創建哈希表
{//利用除留取餘法創建以名字爲關鍵字創建的哈希函數,在發生衝突時調用Random函數處理衝突
    int i = 0;  
    int key = 0;

        for (int t=0; dataInfo->name[t]!='\0'; t++)   
    {       
        key = key + dataInfo->name[t];
    }
    key = key % 42;
    while(value[key]->sign == '1')//有衝突
    {
        key = Random(key, i++);//處理衝突
    }
    if(key == -1) exit(1);//無衝突
    length++;//當前數據個數加
    value[key]->name = dataInfo->name;
    value[key]->address = dataInfo->address;
    value[key]->phone = dataInfo->phone;
    value[key]->sign = '1';//表示該位置有值
    //cout << value[key]->name << "  " << value[key]->phone << "  "  << value[key]->address << endl;
}

int HashTable::Rehash(int key, string str)// 再哈希法處理衝突
{//再哈希時使用的是摺疊法創建哈希函數    
    int h;
    int num1 = (str[0] - '0') * 1000 + (str[1] - '0') * 100 + (str[2] - '0') * 10 + (str[3] - '0');
    int num2 = (str[4] - '0') * 1000 + (str[5] - '0') * 100 + (str[6] - '0') * 10 + (str[7] - '0');
    int num3 = (str[8] - '0') * 100  + (str[9] - '0') * 10  + (str[10] - '0');
    h = num1 + num2 + num3;
    h = (h + key) % HASH_MAXSIZE;
    return h;
}

void HashTable::Hashphone(DataInfo *dataInfo)//以電話爲關鍵字創建哈希表
{//利用除留取餘法創建以電話爲關鍵字創建的哈希函數,在發生衝突時調用Rehash函數處理衝突
    int key = 0;    
    int t;

    for(t=0; dataInfo->phone[t] != '\0'; t++)
    {   
        key = key + dataInfo->phone[t];
    }
    key = key % 42;
    while(value[key]->sign == '1')//有衝突
    {
        key = Rehash(key, dataInfo->phone);
    }
    length++;//當前數據個數加
    value[key]->name = dataInfo->name;
    value[key]->address = dataInfo->address;
    value[key]->phone = dataInfo->phone;   
    value[key]->sign = '1';//表示該位置有值    
}

void HashTable::Outfile(string name, int key)//在沒有找到時輸出未找到的記錄
{
    ofstream fout;
    if((key == -1)||(value[key]->sign == '0'))//判斷哈希表中沒有記錄
    {
        fout.open("out.txt",ios::app);//打開文件

        if(fout.fail())
        {
            cout << "文件打開失敗!" << endl;
            exit(1);
        }
        fout << name << endl;//將名字寫入文件,有個問題,每次寫入的時候老是將原來的內容替換了
        fout.close();
    }
}

void HashTable::Outhash(int key)//輸出哈希表中關鍵字碼對應的記錄
{  
    if((key==-1)||(value[key]->sign=='0'))
        cout << "沒有找到這條記錄!" << endl;
    else
    {       
        for(unsigned int i=0; value[key]->name[i]!='\0'; i++)
        {
            cout << value[key]->name[i];            
        }

        for(unsigned int i=0; i<10; i++)
        {
            cout << " ";
        }

        cout << value[key]->phone;

        for(int i=0; i<10; i++)
        {
            cout << " ";
        }

        cout << value[key]->address << endl;
    }
}

void HashTable::Rafile()//隨機生成文件,並將文件保存在new.txt文檔中
{
    ofstream fout;
    fout.open("new.txt");//打開文件,等待寫入
    if(fout.fail())
    {
        cout << "文件打開失敗!" << endl;
        exit(1);
    }
    for(int j=0; j<30; j++)
    {       
        string name = "";
        for(int i=0; i<20; i++)//隨機生成長個字的名字
        {
            name += rand() % 26 + 'a';//名字是由個字母組成           
        }
        fout << name << "   ";//將名字寫入文件

        string phone = "";
        for(int i=0; i<11; i++)//隨機生成長位的電話號碼
        {
            phone += rand() % 10 + '0';//電話號碼是純數字
        }
        fout << phone << "      ";//將電話號碼寫入文件

        string address = "";
        for(int i=0; i<29; i++)//隨機生成長個字的名字
        {
            address += rand() % 26 + 'a';//地址是由個字母組成
        }
        address += ',';
        fout << address << endl;//將地址寫入文件
    }
    fout.close();
}

void HashTable::Hash(char *fname, int n)//創建哈希表
//fname是數據儲存的文件的名稱,用於輸入數據,n是用戶選擇的查找方式
//函數輸入數據,並根據選擇調用Hashname或Hashphone函數進行哈希表的創建
{
    ifstream fin;       
    int i;
    fin.open(fname);//讀文件流對象
    if(fin.fail())
    {
        cout << "文件打開失敗!" << endl;
        exit(1);
    }
    while(!fin.eof())//按行讀入數據
    {
        DataInfo *dataInfo = new DataInfo();
        char* str = new char[100];      
        fin.getline(str, 100, '\n');//讀取一行數據

        if(str[0] == '*')//判斷數據結束
        {
            break;
        }

        i = 0;//記錄字符串數組的下標
        //a-z:97-122     A-Z:65-90    
        //本程序的姓名和地址都使用小寫字母
        while((str[i] < 97) || (str[i] > 122))//讀入名字
        {
            i++;
        }

        for(; str[i]!=' '; i++)
        {           
            dataInfo->name += str[i];           
        }

        while(str[i] == ' ')
        {
            i++;
        }

        for(int j=0; str[i]!=' '; j++,i++)//讀入電話號碼
        {           
            dataInfo->phone += str[i];
        }

        while(str[i] == ' ')
        {
            i++;
        }

        for(int j=0; str[i]!=','; j++,i++)//讀入地址
        {           
            dataInfo->address += str[i];
        }

        if(n == 1)
        {           
            Hashname(dataInfo);
        }
        else
        {           
            Hashphone(dataInfo);//以電話爲關鍵字
        }

        delete []str;
        delete dataInfo;
    }   
    fin.close();
}

int HashTable::Findname(string name)//根據姓名查找哈希表中的記錄對應的關鍵碼
{
    int i = 0;
    int j = 1;
    int t;
    int key = 0;

    for(key=0, t=0; name[t] != '\0'; t++)
    {
        key = key + name[t];
    }
    key = key % 42;
    while((value[key]->sign == '1') && (value[key]->name != name))
    {  
        key = Random(key, i++);
        j++;
        if(j >= length) return -1;
    }
    return key;
}

int HashTable::Findphone(string phone)//根據電話查找哈希表中的記錄對應的關鍵碼
{  
    int key = 0;
    int t;

    for(t=0; phone[t] != '\0' ; t++)
        {
                key = key + phone[t];
        }
    key = key % 42;
    int j = 1;
    while((value[key]->sign == '1') && (value[key]->phone != phone))
    {
        key = Rehash(key, phone);
        j++;
        if(j >= length) 
                {
                    return -1;
                }
        }
    return key;
}

void main()
{
    //WriteToOldTxt();  
    int k;
    int ch; 
    char *Fname;
    HashTable *ht = new HashTable;
    while(1)
    {
        system("cls");//cls命令清除屏幕上全部的文字
        cout << "歡迎使用本系統!" << endl << endl;
        cout << "請選擇數據" << endl;
        cout << "1.使用已有數據文件" << endl;
        cout << "2.隨機生成數據文件" << endl;
        cout << "0.結束" << endl;
        cout << "輸入相應序號選擇功能:";
        cin >> k;
        switch(k)
        {
        case 0:
            return;
        case 1:
            Fname = "old.txt";//從數據文件old.txt(本身現行建好)中讀入各項記錄
            break;
        case 2:
            ht->Rafile();
            Fname = "new.txt";//由系統隨機產生各記錄,而且把記錄保存到new.txt文件中
            break;
        default:
            cout << "輸入序號有誤,退出程序。" << endl; 
            return;
        }

        do
        {
            system("cls");
            cout << " 請選擇查找方式" << endl;
            cout << "1.經過姓名查找" << endl;
            cout << "2.經過電話查找" << endl;
            cout << "輸入相應序號選擇功能:";
            cin >> ch;
            if((ch != 1) && (ch != 2))
                cout << "輸入序號有誤!" << endl;
        }while((ch != 1) && (ch != 2));

        ht->Hash(Fname, ch);
        while(ch == 1)
        {
            int choice;
            cout << endl << "請選擇功能" << endl;
            cout << "1.輸入姓名查找數據" << endl;
            cout << "2.顯示哈希表" << endl;
            cout << "0.退出"<<endl;
            cout << "輸入相應序號選擇功能:";
            cin >> choice;
            switch(choice)
            {
            case 1: 
                {//注意此處應該加上大括號
                    int key1;                   
                    string name;                    
                    cout << "請輸入姓名:";
                    cin >> name;                    
                    key1 = ht->Findname(name);
                    ht->Outfile(name, key1);
                    ht->Outhash(key1);  
                }
                break;

            case 2: 
                {
                    for(int i=0; i<HASH_MAXSIZE; i++)
                    {
                        if(ht->value[i]->sign!='0')
                        {
                            ht->Outhash(i); 
                        }
                    }   
                }                           
                break;


            default:
                cout << endl << "您的輸入有誤!" << endl;              
            }

            if(choice == 0) 
            {
                return;
            }
        }

        while(ch == 2)
        {
            int choice;
            cout << endl << "請選擇功能" << endl;
            cout << "1.輸入電話查找數據" << endl;
            cout << "2.顯示哈希表"<<endl;
            cout << "0.退出"<<endl;
            cout << "輸入相應序號選擇功能:";
            cin >> choice;
            switch(choice)
            {
            case 1: 
                {
                    int key2;                   
                    string phone;                   
                    cout << "請輸入11位的電話號碼:";

                    do
                    {
                        cin >> phone;                       
                        if(phone.length() != 11)                    
                        {
                            cout << "電話號碼應爲11位!\n請從新輸入:";
                        }

                    }while(phone.length() != 11);

                    key2 = ht->Findphone(phone);
                    ht->Outfile(phone, key2);
                    ht->Outhash(key2);
                }
                break;

            case 2: 
                {
                    for(int i=0; i<HASH_MAXSIZE; i++)
                    {
                        if(ht->value[i]->sign != '0') 
                        {
                            ht->Outhash(i); 
                        }
                    }
                }               
                break;

            default:
                 cout << endl << "您的輸入有誤!" << endl;              
            }

            if(choice == 0) 
            {
                return;
            }
        }

        while((ch != 1) && (ch != 2))
        {
            cout << "您的輸入有誤!請輸入相應須要選擇功能:";
        }
    }
    system("pause");    
}

代碼實現來源:

http://blog.csdn.net/htyurencaotang/article/details/7881427

原理說明來源:

http://www.tuicool.com/articles/BvI3Ir 
http://blog.csdn.net/nju_yaho/article/details/7402208 
http://blog.csdn.net/duan19920101/article/details/51579136 
http://blog.sina.com.cn/s/blog_6776884e0100pko1.html 
http://blog.csdn.net/v_july_v/article/details/6256463
相關文章
相關標籤/搜索