哈希表

C语言哈希表

【1】设计数据结构

(1)哈希表由一个结构体HashTable构成

(2)结构体HashTable由两个元素组成。其一为指针数组(链式存储元素);其二为整型变量(记录元素个数)

(3)指针数组类型为HashNode *(哈希节点指针)

(4)结构体HashNode由数据域和指针域组成。数据域也是一种结构类型变量,指针域为一个同类型指针,为了实现链式存储。

(5)数据域结构体ElemType由关键码以及其他相关信息组成(在此程序里没有添加)

(6)拟定哈希表长度为13

【2】C语言表示数据结构

 1 #define   M   13   
 2 typedef  int  KeyType;
 3 typedef  struct 
 4 {
 5     KeyType key;   
 6     //otherinfo;
 7 }ElemType;
 8 typedef struct  _Node
 9 {
10     ElemType  data;     
11     _Node     *next;    
12 }HashNode;
13 typedef struct
14 {
15     HashNode  *table[M];    
16     int  len;                
17 }HashTable;

【3】函数设计

    在设计函数之前,让我们先理解一下基本的逻辑:

    哈希表中存储的每个元素都有一个关键码,而每个关键码都可以通过与哈希表的总数取模(当然这个算法可以任意选择)得到一个索引,而索引就是该元素欲存入的“桶”标识。一个循环链表可以形象的理解为一只桶。

    插入函数采用的前插入方式,删除函数就相当于链表的删除操作,关键仅仅在于对指针操作。

    哈希函数可以有多种实现算法,在此采用取模法。

 (1)哈希函数Hash,取模定位。实现代码如下:

1 //hash函数
2 int Hash(KeyType k)
3 {
4     return (k % M);
5 }

 (2)初始化哈希表。元素分别置为空。实现代码如下:

1 void InitHash(HashTable &ht)
2 {
3     ht.len=0;
4     for(int i=0;i<M;++i)
5     {
6         ht.table[i]=NULL;     //置空
7     }
8 }

 (3)插入元素InsertHashTable(HashTable &ht,ElemType  x)。引用传入哈希表对象,传入关键字。实现代码如下:

 1 /*
 2 *插入函数
 3 */
 4 void InsertHashTable(HashTable &ht,ElemType x)
 5 {
 6     //判断是否存在
 7     int pos=SearchHash(ht,x);
 8     if(pos == -1)
 9     {
10         cout<<"已存在"<<endl;
11         return ;
12     }
13     //不存在的前提下:new一个新的节点
14     HashNode *s=new HashNode();
15     s->data=x;             
16     //头部插入法
17     s->next =ht.table[pos];  
18     ht.table[pos]=s;
19     //重置长度                 
20     ht.len+=1;
21 }

 (4)在插入之前,我们先要确定关键字映射的索引。函数SearchHash实现这个需求。实现代码如下:

 1 //查询关键码的pos
 2 int SearchHash(HashTable &ht,ElemType x)
 3 {
 4     /*
 5     *初始化
 6     */
 7     //首先定位
 8     int pos=Hash(x.key);
 9     HashNode *p=ht.table[pos];
10     /*
11     *循环遍历节点
12     */
13     while(p != NULL && p->data.key != x.key)
14     {
15         p=p->next;
16     }
17     /*
18     *处理结果
19     */
20     //没找到
21     if(p == NULL)
22     {
23         return pos;
24     }
25     //找到
26     else
27     {
28         return -1;
29     }
30 }

(5)在实现了添加元素之后,我们相应地考虑到删除元素。删除元素的前提是要找到元素所被存储的节点。于此函数Search实现该功能。返回欲删除元素的指针。实现代码如下:

 1 HashNode *Search(HashTable &ht,ElemType x)
 2 {
 3     /*
 4     *初始化
 5     */
 6     //定位
 7     int pos=Hash(x.key);
 8     HashNode *p=ht.table[pos];
 9     /*
10     *循环
11     */
12     while(p != NULL && p->data.key !=x.key)
13     {
14         p=p->next;
15     }
16     /*
17     *结果处理
18     */
19     return p;
20 }

 (6)删除函数RemoveHash(HashTable &ht,ElemType x)设计思想:

    A.调用Hash函数确定元素的索引

    B.依据索引定位元素链表的起点(即就是确定从那个指针数组索引进行遍历)

    C.备用指针

    D.循环遍历

    E.结果处理。具体实现代码如下:

 1 void RemoveHash(HashTable &ht,ElemType x)
 2 {
 3     /*
 4     *初始化
 5     */
 6     int pos=Hash(x.key);
 7     HashNode *q=ht.table[pos];
 8     HashNode *p=NULL;       //备用指针
 9     /*
10     *循环遍历
11     */
12     while(q != NULL && q->data.key != x.key)
13     {
14         p=q;
15         q=q->next;
16     }
17     /*
18     *结果处理
19     */
20     //找到的是第一个结点
21     if(p == NULL && q != NULL)
22     {
23         ht.table[pos]=q->next;
24         ht.len-=1;
25         delete q;
26     }
27     //找到的是其他节点包括最后一个
28     if(p != NULL && q != NULL)
29     {
30         p->next=q->next;
31         ht.len-=1;
32         delete q;
33     }
34 
35 }

【4】测试程序以及源程序代码

  1 #include <iostream>
  2 #include <malloc.h>
  3 using namespace std;
  4 
  5 //数据结构
  6 #define   M   13        //hash表的大小
  7 typedef  int  KeyType;  //关键字类型定义
  8 
  9 //储存元素类型(结构体代表数据库的一行(即就是一个完整的个体))
 10 typedef  struct 
 11 {
 12     KeyType key;   //关键码类型:一般比如学号,身份证号
 13      //otherinfo;
 14 } ElemType;
 15 
 16 //链表节点类型
 17 typedef struct  _Node
 18 {
 19     ElemType  data;      //元素变量
 20     _Node*    next;      //指向下一个的指针
 21 } HashNode;
 22 
 23 //hash表结构
 24 typedef struct
 25 {
 26     HashNode* table[M];    //节点指针(相当于链表头结点)
 27     int  len;              //大小
 28 } HashTable;
 29 
 30 /*
 31 *初始化hash
 32 */
 33 void InitHash(HashTable &ht)
 34 {
 35     ht.len = 0;
 36     for (int i = 0; i < M; ++i)
 37     {
 38         ht.table[i] = NULL;     //置空
 39     }
 40 }
 41 
 42 //hash函数
 43 int Hash(KeyType k)
 44 {
 45     return (k % M);
 46 }
 47 
 48 //查询关键码的pos是否存在
 49 int SearchHash(HashTable &ht, ElemType x)
 50 {
 51     /*
 52     *初始化
 53     */
 54     //首先定位
 55     int pos = Hash(x.key);
 56     HashNode *p = ht.table[pos];
 57     /*
 58     *循环遍历节点
 59     */
 60     while (p != NULL && p->data.key != x.key)
 61     {
 62         p = p->next;
 63     }
 64     /*
 65     *处理结果
 66     */
 67     //没找到
 68     if (NULL == p)
 69         return pos;
 70 
 71    return -1;
 72 }
 73 /*
 74 *插入函数
 75 */
 76 void InsertHashTable(HashTable &ht, ElemType x)
 77 {
 78     //判断是否存在
 79     int pos = SearchHash(ht, x);
 80     if (-1 == pos)
 81     {
 82         cout << "已存在" << endl;
 83         return;
 84     }
 85     //不存在的前提下:new一个新的节点
 86     HashNode *s = new HashNode();
 87     s->data = x;             
 88     //头部插入法
 89     s->next = ht.table[pos];  
 90     ht.table[pos] = s;
 91     //重置长度                 
 92     ht.len += 1;
 93 }
 94 /*
 95 *查询某元素的指针
 96 */
 97 HashNode *Search(HashTable &ht, ElemType x)
 98 {
 99     /*
100     *初始化
101     */
102     //定位
103     int pos = Hash(x.key);
104     HashNode *p = ht.table[pos];
105     /*
106     *循环
107     */
108     while (p != NULL && p->data.key != x.key)
109     {
110         p = p->next;
111     }
112     /*
113     *结果处理
114     */
115     return p;
116 }
117 /*
118 *删除元素
119 */
120 void RemoveHash(HashTable &ht, ElemType x)
121 {
122     /*
123     *初始化
124     */
125     int pos = Hash(x.key);
126     HashNode *q = ht.table[pos];
127     HashNode *p = NULL;       //备用指针
128     /*
129     *循环遍历
130     */
131     while (q != NULL && q->data.key != x.key)
132     {
133         p = q;
134         q = q->next;
135     }
136     /*
137     *结果处理
138     */
139     //找到的是第一个结点
140     if (NULL == p && q != NULL)
141     {
142         ht.table[pos] = q->next;
143         ht.len -= 1;
144         delete q;
145     }
146     //找到的是其他节点包括最后一个
147     if (p != NULL && q != NULL)
148     {
149         p->next = q->next;
150         ht.len -= 1;
151         delete q;
152     }
153 }
154 
155 void main()
156 {
157     cout << __FILE__ << endl;
158     cout << __LINE__ << endl;
159     cout << __TIME__ << endl;
160 
161     ElemType ar[11] = {1, 14, 27, 12, 40, 34, 53, 65, 78, 9, 5,};
162     HashNode *p = NULL;
163     ElemType x;
164     HashTable ht;
165 
166     InitHash(ht);
167     for (int i = 0; i < 11; ++i)
168     {
169         InsertHashTable(ht, ar[i]);
170     }
171     cout << "请输入查找关键码并以-1结束" << endl;
172     while (cin>>x.key, x.key != -1)
173     {
174         p = Search(ht, x);
175         if (NULL == p)
176         {
177             cout << "无此数据" << endl;
178         }
179         else
180         {
181             cout << "关键码地址:"<< p << "  " << "关键码值:" << p->data.key << endl;
182         }
183     }
184     cout <<"请输入删除的关键码" <<endl;
185     while (cin>>x.key, x.key != -1)
186     {
187         RemoveHash(ht, x);
188     }
189 }

【5】Demo总结

完成一个简单的程序之前,首先整理思路,把基本的逻辑思路想清楚,再开始一步一步地策划,一点一点的处理相应的功能需求,一次一次的重审自己的设计思路。

在所有的步骤大概都设计好以后,着手书写代码,实现相应地模块,或者说函数,进而完成整个程序。

最后测试自己的代码,是否达到了预期地要求,是否可以扩充,有没有多余的代码等。

本次程序学到了循环链表的基本操作以及哈希表的原理。

 

Good Good Study, Day Day Up.

顺序  选择  循环  坚持

posted @ 2012-07-26 20:58  kaizenly  阅读(2896)  评论(0编辑  收藏  举报
打赏