散列表

如果表够大,散列函数足够好,那么散列表在查找上具有O(1)的时间复杂度。

但是我们考虑出现冲突的情况,如果使用分离链表法来处理冲突。那么链表的平均长度等于装填因子a(元素个数与散列表大小的比值)的大小。所以不成功查找的复杂度为a,成功查找的复杂度为1 + a/2。

代码实现如下:

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cmath>
  4 #include <cstring>
  5 // 分离链接散列表 
  6 typedef unsigned int index;
  7 typedef int itemType;
  8 typedef struct ListNode *position;
  9 typedef position list;
 10 typedef struct HashTable *hashTable;
 11 
 12 struct ListNode {
 13     itemType item;
 14     position next;
 15 };
 16 struct HashTable {
 17     int tableSize;
 18     list *listArr;
 19 };
 20 
 21 int
 22 NextPrime(int n)
 23 {
 24     int ceiling, isPrime;
 25     
 26     isPrime = 0;
 27     ceiling = 2 * n;
 28     
 29     for ( ; n < ceiling; n++) {
 30         int nSqrt = (int)sqrt(n);
 31         isPrime = 1;
 32         for (int i = 2; isPrime && i <= nSqrt; i++)
 33         {
 34             if (n % i == 0) {
 35                 isPrime = 0;
 36             }  
 37         }
 38         if (isPrime) {
 39             return n;
 40         }
 41     }
 42 }
 43 
 44 index
 45 Hash(itemType key, int tableSize)
 46 {
 47     return key % tableSize;
 48 }
 49 
 50 hashTable
 51 InitializeTable(int tableSize)
 52 {
 53     hashTable h;
 54     
 55     h = (hashTable)malloc(sizeof(struct HashTable));
 56     if (h == NULL) {
 57         printf("Out of space!\n");
 58         exit(-1);
 59     }
 60     
 61     h->tableSize = NextPrime(tableSize);
 62     
 63     h->listArr = (list*)malloc(sizeof(list) * h->tableSize);
 64     //
 65     if (h->listArr == NULL) {
 66         printf("Out of space!\n");
 67         exit(-1);
 68     }
 69     
 70     memset(h->listArr, 0, h->tableSize * sizeof(list*));
 71     
 72     return h;
 73 }
 74 
 75 position
 76 Find(itemType key, hashTable h)
 77 {
 78     list l;
 79     
 80     l = h->listArr[Hash(key, h->tableSize)];
 81     
 82     for ( ; l != NULL; l = l->next) {
 83         //printf("%d\n", Hash(key, h->tableSize));
 84         if (l->item == key) {
 85             //printf("here\n");
 86             return l;
 87         }
 88     }
 89     
 90     return NULL;
 91 }
 92 
 93 void
 94 Insert(itemType key, hashTable h)
 95 {
 96     position p, newNode;
 97     list l;
 98     
 99     p = Find(key, h);
100     
101     int hValue = Hash(key, h->tableSize);
102     
103     if (p == NULL) {
104         newNode = (position)malloc(sizeof(struct ListNode));
105         if (newNode == NULL) {
106             printf("Out of space!\n");
107             exit(-1);
108         }
109         /* 错误的方法,只改变了 l 这个指针,却没变动listArr[hValue]本身。 
110         list l = h->listArr[hValue];
111         newNode->next = l;
112         newNode->item = key;
113         l = newNode;
114         //h->listArr[hValue] = newNode;
115         */
116         
117         newNode->item = key;
118         newNode->next = h->listArr[hValue];
119         h->listArr[hValue] = newNode;
120         printf("key %d inserted at position %d.\n", h->listArr[hValue]->item, Hash(key, h->tableSize));
121         
122     }
123 }
124 
125 int
126 main(int argc, char** argv)
127 {
128     hashTable h;
129     
130     h = InitializeTable(10);
131     
132     for (int i = 0; i < 10; i++) {
133         Insert(i*i, h);
134     }
135         
136     
137     for (int i = 0; i < 100; i++) {
138         if (Find(i, h)) {
139             printf("key: %d found.\n", i);
140         } else {
141             printf("key: %d not found.\n", i);
142         }
143     }
144     
145     
146     system("pause");
147     return 0;
148 }

-

另外,当我们使用开放定址法来处理冲突的时候,特别要注意删除的时候,不能直接删除。原因是如果你删除了P这个位置的元素x。那么如果以前有的元素y应该映射到P,却因为冲突而到了其他地方。当x被删除之后,P这个位置为空。那么如果我们要寻找y,就会第一个找到P这个位置,发现为空。于是就找不到y了。(可以继续遍历开放定址法的位置来寻找y……但是这样子不是太楞了吗?如果原本就没y这个元素呢,那我们得做许多无用功。)

不能直接删除,所以一般我们使用懒惰删除,给P位置一个“已删除”的标记,就能解决这个问题了。

posted @ 2014-11-09 23:33  nipan  阅读(241)  评论(0编辑  收藏  举报