数据结构课程设计-利用散列表做一个电话号码查找系统

   【基本要求】

    (1)设每个记录有下列数据项:电话号码、用户名、地址;

    (2)从键盘输入各记录,分别以电话号码和用户名为关键字建立散列表;

    (3)采用一定的方法解决冲突;

    (4)查找并显示给定电话号码的记录;

    (5)查找并显示给定用户名的记录。

    【选做内容】

    (1)系统功能的完善;

    (2)设计不同的散列函数,比较冲突率;

 (3)在散列函数确定的前提下,尝试各种不同类型处理冲突的方法,考察平均查找长度的变化。

 

用的C++开发,基本实现了3种哈希函数+3种解决冲突的方法。因为要求同时有姓名散列与按号码散列,所以用了

flag标记每次的散列类型 ,针对不同的要求对散列函数做了个别优化。

哈希表类的结构如下

 

 1 class HashTable{
 2     public:
 3         HashTable(int size = MAXSIZE-1);
 4         ~HashTable(){ delete[]E; delete[]tag; delete[]E2; delete[]tag2; }
 5         int  hash1(string name, int flag);//哈希函数1  除数求余法
 6         int  hash2(string tel);//哈希函数2 折叠法
 7         int  hash3(string tel);//哈希函数3 数字分析法
 8         int  solve1(int hashVal, int flag);//线性探测法解决冲突
 9         int  solve2(int hashVal, int flag);//二次探测法解决冲突
10         Node*  solve3(int hashVal, int flag);//拉链法解决冲突
11         User input();//往电话薄中添加用户
12         void creat(int flag);  //创建散列表
13         void show(int flag);    //列出电话薄所有元素
14         void search(int flag,string at);  //搜索指定用户
15         void searchByNode(int flag, string at);  //拉链法搜索指定用户
16         void insert(int flag, User newUser); //插入
17         void del(int flag, string by);//删除
18         void save(int flag);//将电话薄保存至本地文件
19         int length; //要创建的电话本长度
20         Node** ht;
21     private:
22         User* E;  //用户数组  按姓名散列
23         User* E2; //用户数组2 按电话号码散列
24         int* tag; //标记散列表1每个桶的存储状态 0为空 1为实
25         int* tag2;//标记散列表2每个桶的存储状态
26         int flag;      //1表示是按姓名  2表示按电话号码 新建的哈希表
27         int maxSize;   //哈希表最大长度
28         int f;//比例因子  主要用于折叠法
29 };
View Code

User类的结构

class User{
    public:
        string name; 
        string tel;
        string address;
        bool operator==(const User&target)
        {
            if (this->name == target.name&&this->address == target.address&&this->tel == target.tel)
                return true;
            else
                return false;
        }
};

 

哈希函数1

int HashTable::hash1(string name,int flag) //除留求余法
{
    int hashValue;        long a = 0;
    switch (flag)
    {
    case 1:
        for (int i = 0; i < name.length(); i++)
            a += int(name[i]);
        hashValue = a%maxSize;
        break;
    case 2:
        int temp = atof(name.c_str());
        hashValue = temp%maxSize;
        break;

    }
    return hashValue;
};

哈希函数2

int HashTable::hash2(string    tel)  //折叠法--移位法
{
    int hashValue;   
    int temp; //移位法求和
    temp = atof(tel.substr(0, 3).c_str()) + atof(tel.substr(3, 3).c_str()) 
        + atof(tel.substr(6, 3).c_str()) + atof(tel.substr(9, 2).c_str());
    //取计算之后的数的最后三位
    if (temp >= 999)
    {
        char p[10];
        sprintf(p, "%d", temp);
        string lastThree = p;
        lastThree = lastThree.substr(lastThree.length() - 3, 3);
        hashValue = atof(lastThree.c_str());
        return hashValue;
    }
    hashValue = temp;
    return hashValue;
};

哈希函数3

int HashTable::hash3(string tel)//数字分析法做哈希函数 
{
    int hashValue;
    hashValue = atof(tel.substr(8, 3).c_str()); //因为电话号码一般后4位不同
    return hashValue;
};

 


 

 解决冲突的方法

 1.线性探测法

int HashTable::solve1(int hashVal,int flag)  //线性探查法处理冲突
{
    int output = hashVal;
    switch (flag)
    {
    case 1:
        for (int j = 1; j < MAXSIZE; j++)
        {
            output = (hashVal + j) % MAXSIZE;
            if (tag[output] == 0)
            {
                tag[output] = 1;
                return output;
            }
        }
        return -1;
        break;
    case 2:
        for (int j = 1; j < MAXSIZE; j++)
        {
            output = (hashVal + j) % MAXSIZE;
            if (tag2[output] == 0)
            {
                tag2[output] = 1;
                return output;
            }
        }
        return -1;
    default:
        break;
    }

};

2.二次探查法

int HashTable::solve2(int hashVal, int flag)   //二次探查法解决冲突
{
    int i = hashVal;            //i为初始桶号
    int k = 0;                    //k为探查次数
    int odd = 0;                //odd为控制加减的标志
    int save;                    //缓存上一次的桶号
    switch (flag)
    {
    case 1:
        while (tag[i]==1)
        {
            if (odd == 0)
            {
                k++; save = i;
                i = (i + 2 * k-1) % MAXSIZE;
                odd = 1;
            }
            else
            {
                i = (save - 2 * k+1) % MAXSIZE;
                odd = 0;
                if (i<0)
                {
                    i = i + MAXSIZE;
                }
            }
        }
        return i;
        break;
    case 2:
        while (tag2[i] == 1)
        {
            if (odd == 0)
            {
                k++; save = i;
                i = (i + 2 * k - 1) % MAXSIZE;
                odd = 1;
            }
            else
            {
                k++;
                i = (save - 2 * k + 1) % MAXSIZE;
                odd = 0;
                if (i<0)
                {
                    i = i + MAXSIZE;
                }
            }
        }
        return i;
        break;
    default:
        break;
    }
    
};

3.拉链法

Node* HashTable::solve3(int hashVal, int flag)//拉链法解决冲突

{
    int i = hashVal; //第i条链
    
    Node*p = ht[i];  //该链上的头指针
    while (p!=NULL)
        p = p->next;//往后遍历直到找到一个空节点用于存放user
    return p;
};

所有代码如下

  1 #include 
  2 #include <string>
  3 #include 
  4 
  5 using namespace std;
  6 
  7 const int MAXSIZE = 12;//默认最大表长
  8 
  9 
 10 
 11 
 12 //存储项
 13 class User{
 14     public:
 15         string name; 
 16         string tel;
 17         string address;
 18         bool operator==(const User&target)
 19         {
 20             if (this->name == target.name&&this->address == target.address&&this->tel == target.tel)
 21                 return true;
 22             else
 23                 return false;
 24         }
 25 };
 26 
 27 //用于拉链法
 28 struct Node{
 29     User user;
 30     Node* next;
 31 };
 32 class HashTable{
 33     public:
 34         HashTable(int size = MAXSIZE-1);
 35         ~HashTable(){ delete[]E; delete[]tag; delete[]E2; delete[]tag2; }
 36         int  hash1(string name, int flag);//哈希函数1  除数求余法
 37         int  hash2(string tel);//哈希函数2 折叠法
 38         int  hash3(string tel);//哈希函数3 数字分析法
 39         int  solve1(int hashVal, int flag);//线性探测法解决冲突
 40         int  solve2(int hashVal, int flag);//二次探测法解决冲突
 41         Node*  solve3(int hashVal, int flag);//拉链法解决冲突
 42         User input();//往电话薄中添加用户
 43         void creat(int flag);  //创建散列表
 44         void show(int flag);    //列出电话薄所有元素
 45         void search(int flag,string at);  //搜索指定用户
 46         void searchByNode(int flag, string at);  //拉链法搜索指定用户
 47         void insert(int flag, User newUser); //插入
 48         void del(int flag, string by);//删除
 49         void save(int flag);//将电话薄保存至本地文件
 50         int length; //要创建的电话本长度
 51         Node** ht;
 52     private:
 53         User* E;  //用户数组  按姓名散列
 54         User* E2; //用户数组2 按电话号码散列
 55         int* tag; //标记散列表1每个桶的存储状态 0为空 1为实
 56         int* tag2;//标记散列表2每个桶的存储状态
 57         int flag;      //1表示是按姓名  2表示按电话号码 新建的哈希表
 58         int maxSize;   //哈希表最大长度
 59         int f;//比例因子  主要用于折叠法
 60 };
 61 
 62 HashTable::HashTable(int size)
 63 {
 64     maxSize = size;  //用作除数
 65     E = new User[MAXSIZE];
 66     E2 = new User[MAXSIZE];
 67     tag = new int[MAXSIZE];
 68     tag2 = new int[MAXSIZE];
 69     for (int i = 0; i < MAXSIZE; i++)
 70     {
 71         tag[i] = 0;
 72         tag2[i] = 0;
 73     }
 74     f = maxSize / 512;   //用于折叠法产生的哈希值过大 保留3位数的地址范围为0~511
 75     ht = new Node*[maxSize]; //存放节点的一维数组 拉链法
 76 };
 77 
 78 int HashTable::hash1(string name,int flag) //除数求余法
 79 {
 80     int hashValue;        long a = 0;
 81     switch (flag)
 82     {
 83     case 1:
 84         for (int i = 0; i < name.length(); i++)
 85             a += int(name[i]);
 86         hashValue = a%maxSize;
 87         break;
 88     case 2:
 89         int temp = atof(name.c_str());
 90         hashValue = temp%maxSize;
 91         break;
 92 
 93     }
 94     return hashValue;
 95 };
 96 
 97 int HashTable::hash2(string    tel)  //折叠法--移位法
 98 {
 99     int hashValue;   
100     int temp; //移位法求和
101     temp = atof(tel.substr(0, 3).c_str()) + atof(tel.substr(3, 3).c_str()) 
102         + atof(tel.substr(6, 3).c_str()) + atof(tel.substr(9, 2).c_str());
103     //取计算之后的数的最后三位
104     if (temp >= 999)
105     {
106         char p[10];
107         sprintf(p, "%d", temp);
108         string lastThree = p;
109         lastThree = lastThree.substr(lastThree.length() - 3, 3);
110         hashValue = atof(lastThree.c_str());
111         return hashValue;
112     }
113     hashValue = temp;
114     return hashValue;
115 };
116 
117 int HashTable::hash3(string tel)//数字分析法做哈希函数 
118 {
119     int hashValue;
120     hashValue = atof(tel.substr(8, 3).c_str()); //因为电话号码一般后4位不同
121     return hashValue;
122 };
123 
124 int HashTable::solve1(int hashVal,int flag)  //线性探查法处理冲突
125 {
126     int output = hashVal;
127     switch (flag)
128     {
129     case 1:
130         for (int j = 1; j < MAXSIZE; j++)
131         {
132             output = (hashVal + j) % MAXSIZE;
133             if (tag[output] == 0)
134             {
135                 tag[output] = 1;
136                 return output;
137             }
138         }
139         return -1;
140         break;
141     case 2:
142         for (int j = 1; j < MAXSIZE; j++)
143         {
144             output = (hashVal + j) % MAXSIZE;
145             if (tag2[output] == 0)
146             {
147                 tag2[output] = 1;
148                 return output;
149             }
150         }
151         return -1;
152     default:
153         break;
154     }
155 
156 };
157 
158 int HashTable::solve2(int hashVal, int flag)   //二次探查法解决冲突
159 {
160     int i = hashVal;            //i为初始桶号
161     int k = 0;                    //k为探查次数
162     int odd = 0;                //odd为控制加减的标志
163     int save;                    //缓存上一次的桶号
164     switch (flag)
165     {
166     case 1:
167         while (tag[i]==1)
168         {
169             if (odd == 0)
170             {
171                 k++; save = i;
172                 i = (i + 2 * k-1) % MAXSIZE;
173                 odd = 1;
174             }
175             else
176             {
177                 i = (save - 2 * k+1) % MAXSIZE;
178                 odd = 0;
179                 if (i<0)
180                 {
181                     i = i + MAXSIZE;
182                 }
183             }
184         }
185         return i;
186         break;
187     case 2:
188         while (tag2[i] == 1)
189         {
190             if (odd == 0)
191             {
192                 k++; save = i;
193                 i = (i + 2 * k - 1) % MAXSIZE;
194                 odd = 1;
195             }
196             else
197             {
198                 k++;
199                 i = (save - 2 * k + 1) % MAXSIZE;
200                 odd = 0;
201                 if (i<0)
202                 {
203                     i = i + MAXSIZE;
204                 }
205             }
206         }
207         return i;
208         break;
209     default:
210         break;
211     }
212     
213 };
214 
215 /*
216 Node* HashTable::solve3(int hashVal, int flag)//拉链法解决冲突
217 
218 {
219     int i = hashVal; //第i条链
220     
221     Node*p = ht[i];  //该链上的头指针
222     while (p!=NULL)
223         p = p->next;//往后遍历直到找到一个空节点用于存放user
224     return p;
225 };
226 
227 void HashTable::searchByNode(int flag, string at)//调用拉链法搜索
228 {
229     int i = hash1(at,1);
230     Node** ht = new Node*[maxSize]; //存放节点的一维数组
231     Node*p = ht[i];  //该链上的头指针
232     while (p!=NULL&&p->user.name!=at)
233     {
234         p = p->next;
235     }
236 };
237 */
238 User HashTable::input()
239 {
240     User user;
241     cout << "请输入姓名:" << endl;
242     cin >> user.name;
243     cout << "请输入电话号码:" << endl;
244     cin >> user.tel;
245     cout << "请输入地址:" << endl;
246     cin >> user.address;
247     return user;
248 };
249 
250 void HashTable::creat(int flag)
251 {
252     switch (flag)
253     {
254     case 1: //按姓名哈希创建哈希表
255         for (int i = 0; i < length; i++)
256         {
257             User newUser = input();
258             int val = hash1(newUser.name,1);
259             if (tag[val] == 1)
260                 val = solve1(val,1);//线性探测法解决冲突
261             E[val] = newUser;
262             tag[val] = 1;
263         }
264         break;
265     case 2:  //按电话号码哈希创建哈希表
266         for (int i = 0; i < length; i++)
267         {
268             User newUser = input();
269             int val = hash1(newUser.tel,2);
270             if(tag2[val] == 1)
271                 val = solve1(val,2);//线性探测法解决冲突
272             E2[val] = newUser;
273             tag2[val] = 1;
274         }
275         break;
276     }
277 };
278 void HashTable::show(int flag)
279 {
280     switch (flag)
281     {
282     case 1:
283         for (int i = 0; i < MAXSIZE; i++)
284         {
285             if (tag[i] == 1)
286                 cout << E[i].name << "  " << E[i].tel << "  " << E[i].address << "   位于:  " << i << endl;
287         }
288         break;
289     case 2:
290         for (int i = 0; i < MAXSIZE; i++)
291         {
292             if (tag2[i] == 1)
293                 cout << E2[i].name << "  " << E2[i].tel << "  " << E2[i].address << "   位于:  " << i << endl;
294         }
295         break;
296     }
297 
298 };
299 
300 void HashTable::search(int flag,string at) //at表示索引内容
301 {
302     int i = 0;
303     switch (flag)
304     {
305     case 1:  //调用线性探测法查找姓名
306         i = hash1(at,1);
307         if (tag[i] == 1 && E[i].name != at)
308             i = solve1(i, 2);
309         if (i < 0 || tag2[i] == 0)
310         {
311             cout << "查无此人!" << endl;
312             return;
313         }
314         if (tag[i] == 1 && E[i].name == at)
315             cout << E2[i].name << "  " << E2[i].tel << "  " << E2[i].address << endl;
316         break;
317     case 2:  //调用二次探测法查找电话号码
318         i = hash2(at);
319         if (tag2[i] == 1&&E2[i].tel!=at)
320             i = solve2(i,2);
321         if (i < 0||tag2[i]==0)
322         {
323             cout << "查无此人!" << endl;
324             return;
325         }
326         if (tag2[i] == 1 && E2[i].tel==at)
327             cout << E2[i].name << "  " << E2[i].tel << "  " << E2[i].address << endl;
328         break;
329     }
330 };
331 
332 void HashTable::insert(int flag, User newUser){
333     int i = -1;
334     switch (flag)
335     {
336     case 1:
337         i = hash1(newUser.name,1);
338         if (tag[i] == 1||E[i]==newUser)
339             i = solve1(i, 1);
340         if (i < 0)
341         {
342             cout << "表满!插入失败!" << endl;
343             return;
344         }
345         if (tag[i] == 0)
346         {
347             E[i] = newUser;
348             tag[i] = 1;
349             length++;
350             cout << "插入成功" << endl;
351         }
352     case 2:
353         i = hash1(newUser.tel,2);
354         if (tag2[i] == 1 || E2[i] == newUser)
355             i = solve1(i, 2);
356         if (i < 0)
357         {
358             cout << "表满!插入失败!" << endl;
359             return;
360         }
361         if (tag2[i] == 0)
362         {
363             E2[i] = newUser;
364             tag2[i] = 1;
365             length++;
366             cout << "插入成功" << endl;
367         }
368     default:
369         break;
370     }
371 };
372 
373 void HashTable::del(int flag, string by) //by表示按照何种标签进行删除
374 {
375     int i = -1;
376     int select;//选择是否删除
377     switch (flag)
378     {
379     case 1:  //调用线性探测法查找姓名
380         i = hash1(by,1);
381         if (tag[i] == 1 && E[i].name != by)
382             i = solve1(i, 2);
383         if (i < 0 || tag2[i] == 0)
384         {
385             cout << "查无此人!" << endl;
386             return;
387         }
388         if (tag[i] == 1 && E[i].name == by)
389         {
390             cout << E2[i].name << "  " << E2[i].tel << "  " << E2[i].address << endl;
391             cout << "是否删除 0.删了 1.算了" << endl;
392             cin >> select;
393             if (select == 0)
394                 tag[i] = 0;//伪删除
395         }
396         break;
397     case 2:  //调用二次探测法查找电话号码
398         i = hash2(by);
399         if (tag2[i] == 1 && E2[i].tel != by)
400             i = solve2(i, 2);
401         if (i < 0 || tag2[i] == 0)
402         {
403             cout << "查无此人!" << endl;
404             return;
405         }
406         if (tag2[i] == 1 && E2[i].tel == by)
407         {
408             cout << E2[i].name << "  " << E2[i].tel << "  " << E2[i].address << endl;
409             cout << "是否删除 0.删了 1.算了" << endl;
410             cin >> select;
411             if (select == 0)
412                 tag2[i] = 0;//伪删除
413         }
414         break;
415     }
416 };
417 
418 void HashTable::save(int flag)
419 {
420     fstream out1("电话薄(姓名散列).txt", ios::out);
421     fstream out2("电话薄(号码散列).txt", ios::out);
422     switch (flag)
423     {
424     case 1:
425         for (int i = 0; i < maxSize; i++)
426         {
427             if (tag[i] == 1)
428                 out1 << E[i].name << "  " << E[i].tel << "  " << E[i].address << endl;
429         }
430         cout << "已存至电话薄(姓名散列).txt" << endl;
431         return;
432         break;
433     case 2:
434         for (int i = 0; i < maxSize; i++)
435         {
436             if (tag2[i] == 1)
437                 out2 << E2[i].name << "  " << E2[i].tel << "  " << E2[i].address << endl;
438         }
439         cout << "已存至电话薄(号码散列).txt" << endl;
440         return;
441         break;
442     default:
443         break;
444     }
445 
446 };
hashtable.h
  1 #include 
  2 #include <string>
  3 #include 
  4 #include "hashtable.h"
  5 using namespace std;
  6 
  7 //菜单
  8 void menu()
  9 {
 10     cout << " ****************************" << endl;
 11     cout << "||          0.建表         ||" << endl;
 12     cout << "||          1.查看         ||" << endl;
 13     cout << "||          2.搜索         ||" << endl;
 14     cout << "||          3.添加         ||" << endl;
 15     cout << "||          4.删除         ||" << endl;
 16     cout << "||          5.保存         ||" << endl;
 17     cout << "||          6.退出         ||" << endl;
 18     cout << " ****************************" << endl;
 19 
 20 }
 21 
 22 int main()
 23 {
 24     User user;
 25     int size;//第一次创建的数据量大小
 26     int select;//主菜单选项
 27     int select_;//子菜单选项
 28     cout << "欢迎使用电话簿" << endl;
 29     HashTable ht;
 30     while (1)
 31     {
 32         menu();
 33         cin >> select;
 34         switch (select)
 35         {
 36         case 0:
 37             cout << "第一次使用,请输入要新建的电话本大小:" << endl;
 38             cin >> size;
 39             ht.length = size;
 40             cout << "1.姓名散列 2.电话号码散列" << endl;
 41             cin >> select_;
 42             ht.creat(select_);
 43             break;
 44         case 1:
 45             cout << "1.姓名散列 2.电话号码散列" << endl;
 46             cin >> select_;
 47             ht.show(select_);
 48             break;
 49         case 2:
 50             cout << "1.按姓名查找 2.按电话号码查找" << endl;
 51             cin >> select_;
 52             if (select_==1)
 53             {
 54                 cout << "输入姓名" << endl;
 55                 string name;
 56                 cin >> name;
 57                 ht.search(1, name);
 58             }
 59             else if (select_ == 2)
 60             {
 61                 cout << "输入号码" << endl;
 62                 string tel;
 63                 cin >> tel;
 64                 ht.search(2, tel);
 65             }
 66             else 
 67                 cout << "不合法操作!!!" << endl;
 68             break;
 69         case 3:
 70             user = ht.input();
 71             cout << "1.插入到姓名散列表 2.插入到电话号码散列" << endl;
 72             cin >> select_;
 73             ht.insert(select_,user);
 74             break;
 75         case 4:
 76             cout << "1.根据姓名删除 2.根据电话号码删除" << endl;
 77             cin >> select_;
 78             if (select_ == 1)
 79             {
 80                 cout << "输入姓名" << endl;
 81                 string name;
 82                 cin >> name;
 83                 ht.del(1, name);
 84             }
 85             else if (select_ == 2)
 86             {
 87                 cout << "输入号码" << endl;
 88                 string tel;
 89                 cin >> tel;
 90                 ht.del(2, tel);
 91             }
 92             else
 93                 cout << "不合法操作!!!" << endl;
 94             break;
 95         case 5:
 96             cout << "1.保存姓名散列表到本地 2.保存电话号码散列表到本地" << endl;
 97             cin >> select_;
 98             ht.save(select_);
 99         case 6:
100             return 0;
101         }
102     }
103 }
main.cpp

 

通过这次课程设计,总结如下

1.  C++技艺不精,语法不熟悉,比如模版类与运算符重载,指针更是不大熟练。要掌握C++还需要漫长的修炼。

2.  算法停留在理解阶段感觉远远不够,实现起来远没有当初想的那么简单。

3.  数据结构与算法很重要,很重要,很重要。同样的功能,一个优秀的算法节约了大量的时间,创造出巨大的经济效益!

 

我知道我还很菜,加油!

转载于:https://www.cnblogs.com/morningsky/p/5090467.html

你可能感兴趣的:(数据结构课程设计-利用散列表做一个电话号码查找系统)