C++ hash table 的编程实现

关于Hash Table, 总结如下:

散列表能够实现通过key 对元素的快速访问。 而且易于扩展。 对元素能够实现快速访问(搜索等字典操作), 这是Hash Table 较之于链表的优势所在, 二者均易于扩展。 而易于扩展这个dynamic的结构(使用链接法的时候)又是较之于array的优势所在。  因为数组时不易于扩展的。 


使用散列表, 我们需要使用Hash function。 散列函数是对关键字和散列表元提供映射的函数。 常见的Hash function 有如下几种:

(1)直接寻址法。  取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a•key + b,其中a和b为常数(这种散列函数叫做自身函数)

(2)除法散列法, 关键字k 除以m 取余数, 将关键字k 映射到m个slots 中的一个上, 即 h(k) = k mod m, 一般取m 为素数。

(3)乘法散列法: 包含两步:

          step 1:用关键字k 乘上常数A(0<A <1), 并提取kA d的小数部分。

           step 2: 用m 乘以这个小数部分的值, 再往下取整,

        总之, 散列函数为: h(k) = floor(m(kA mod 1))。 乘法散列的好处就是m 可以是2的某个次幂。 而不只是素数了。

(4) 折叠法: 将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。

等等还有好多散列函数。


散列函数的性能直接影响着散列表数据结构的性能。 当散列函数把太少或太多关键字映射到单个散列表元上时,散列表的使用效率会大大降低。

性能最好的散列函数被称为完全散列函数。即保证所有可能的关键字都会分别映射到唯一的散列表元编号上。

通常很难找到一个完全散列函数。 不完全的散列函数就会导致冲突的发生。 冲突是散列表的一个缺点之一。

一般而言, 冲突是无法避免的。 因为关键字的域是无穷的, 而映射到的表中, 表的大小是一定的, 不可能是无穷大的。


既然无法避免冲突, 那么我们就要解决冲突, 解决办法有如下几种:

法一: 开放寻址法(open addressing):


的次数。增量序列可有下列取法:




所有的元素都在散列表中。 不会像链接法那样, 这里没有链表, 也没有链表存放在散列表外。 缺点是散列表可能被插满了。 而且此时的Hash Table也不容易扩展, 即不是dynamic的了。 我们希望我们的散列函数实现均匀散列(uniform hashing), 即每个关键字等可能的插入到slot 中。 但是这只是理想, 通常情况下, 会出现cluste的现象。 出现冲突的时候, 有三种方法用于探查开放寻址中的探查序列:(1)Linear probing(线性探查)(2)二次探查 (3)双重探查。


法二: 链接法(chaining)。 不难看出此时我们的Hash Table 变成了dynamic的了。 使用允许一个散列表元放置多个元素的方法,这时实际上散列表就是一个链表数组



程序如下:

code::blocks 新建工程, 一个主程序文件, 一个hash.h 文件, hash.cpp 文件。 主程序如下:

#include <iostream>
#include <cstdlib>
#include <string>
#include "hash.h"

using namespace std;

int main()
{
    Hash Hashy;
    string name1 = " ";
    string name2 = " ";
    Hashy.PrintTable();


    Hashy.AddItem("Paul", "Locha");
    Hashy.AddItem("Kim", "Iced Mocha");
    Hashy.AddItem("Anni", "Strawberry Smoothy");
    Hashy.AddItem("Sara", "Passion Tea");
    Hashy.AddItem("Mike", "Tea");
    Hashy.AddItem("steve", "Apple cider");
    Hashy.AddItem("Sill", "Root beer");
    Hashy.AddItem("Bill", "Lochs");
    Hashy.AddItem("Susan", "Cola");
    Hashy.AddItem("Joe", "Green Tea");

    Hashy.PrintTable();

    Hashy.PrintItemsInIndex(0);

    while(name1 != "exit") {
        cout << "search for: ";
        cin >> name1;
        if(name1 != "exit") {
            Hashy.FindDrink(name1);
        }
    }

     while(name2 != "exit") {
        cout << "Remove: ";
        cin >> name2;
        if(name2 != "exit") {
            Hashy.RemoveItem(name2);
        }
    }
    Hashy.PrintTable();

    Hashy.PrintItemsInIndex(0);

    return 0;
}

hash.h 文件:

#ifndef HASH_H
#define HASH_H


#include <iostream>
#include <cstdlib>
#include <string>

using namespace std;

struct item {
    string name;
    string drink;
    item* next;
};


class Hash{
public:
    int hashFunction(string key);
    Hash();

    void AddItem(string name, string drink);
    int NumberOfItemsInIndex(int index);
    void PrintTable();
    void PrintItemsInIndex(int index);
    void FindDrink(string name);
    void RemoveItem(string name);

private:
    static const int tableSize = 10; // 修改为40再试试看, 存储会变换
    item* HashTable[tableSize]; // in this case, 10 buckets, each bucket contains a pointer that has the ability to point to some item

};


#endif // HASH_H

hash.cpp 文件:

#include <iostream>
#include <cstdlib>
#include <string>
#include "hash.h"

using namespace std;


Hash::Hash() {
    for (int i = 0; i < tableSize; i++) {
        HashTable[i] = new item;
        HashTable[i] -> name = "empty";
        HashTable[i] -> drink = "empty";
        HashTable[i] -> next = NULL;
    }
}

int Hash::hashFunction(string key)
{
    int sum = 0;
    int index = 0;

    index = key.length(); // find the length of the string passing to the function

    for (int i = 0; i < index; i++) {
        sum += static_cast<int>(key[i]); // 可以修改hash function
    }

//    cout << "key[0] = " << key[0] << endl;
//    cout << "key[0] = " << static_cast<int>(key[0]) << endl;
//    cout << "key[1] = " << key[1] << endl;
//    cout << "key[2] = " << key[2] << endl;


//    cout << "sum = " << sum << endl;
    index = sum % tableSize;

    return index;
}


void Hash::AddItem(string name, string drink) {
    int index = hashFunction(name);

    if (HashTable[index] -> name == "empty") {

        HashTable[index] -> name = name;
        HashTable[index] -> drink = drink;
//        HashTable[index] -> next = NULL;
    }
    else {
        item* ptr = HashTable[index];

        item* n = new item;
        n -> name = name;
        n -> drink = drink;
        n -> next = NULL;

        while (ptr -> next != NULL) {
            ptr = ptr -> next;
        }

        ptr -> next = n;

    }
}


int Hash::NumberOfItemsInIndex(int index) {
    int Count = 0;
//  为什么注释掉的这个统计的程序会出现问题
// 因为Hash[index]?????

//    item* ptr = HashTable[index];
//
//    while (ptr -> name != "empty") {
//        Count++;
//        ptr = ptr -> next;
//    }

    if (HashTable[index] -> name == "empty") {
        return Count;
    }
    else {
        Count++;
        item* ptr = HashTable[index];
        while (ptr ->next != NULL) {
            Count++;
            ptr = ptr -> next;
        }
    }

    return Count;

}


void Hash::PrintTable() {
    int number; //number of items in each bucket
    for (int i = 0; i < tableSize; i++) {
        cout << "i = " << i << ": " << endl;
// print the first element in the bucket, and we can also see if there are some other items
        number = NumberOfItemsInIndex(i);
        cout << "--------------\n";
        cout << "index = " << i << ": " << endl;
        cout << HashTable[i] -> name << endl;
        cout << HashTable[i] -> drink << endl;
        cout << "# of items = " << number << endl;
        cout << "--------------\n";


    }
}

void Hash::PrintItemsInIndex(int index) {
    item* ptr = HashTable[index];

    if(ptr -> name == "empty") {
        cout << "index = " << index << " is empty";
    }
    else {
        cout << "index " << index << " conatains the following items \n";
        while(ptr != NULL) {
            cout << "--------------\n";
            cout << ptr -> name << endl;
            cout << ptr -> drink << endl;
            cout << "--------------\n";
            ptr = ptr -> next;
        }
    }
}


void Hash::FindDrink(string name) {
    int index = hashFunction(name);
    bool FoundName = false;

    string drink;

    item* ptr = HashTable[index];
    while(ptr != NULL) {
        if(ptr -> name == name) {
            FoundName = true;
            drink = ptr -> drink;
        }
        ptr = ptr ->next;
    }

    if(FoundName == true) {
        cout << "Favorite drink = " << drink << endl;
    }
    else {
        cout << name << "'s info was not found in the hash table. \n";
    }

}


void Hash::RemoveItem(string name) {
    int index = hashFunction(name);

    item* delPtr;
    item* P1;
    item* P2;

    // case 0: bucket is empty
    if (HashTable[index] -> name == "empty" && HashTable[index] -> drink == "empty") {
        cout << name << " was not found in the hash table. \n";
    }

    //case 1: only one item contained in the bucket, and that item
    //has matching name
    else if(HashTable[index] -> name == name && HashTable[index] -> next == NULL) {
        HashTable[index] -> name = "empty";
        HashTable[index] -> drink = "empty";
        cout << name << " was removed from the hash table. \n";

    }

    //case 2: match is located in the first item in the bucket
    // and there are more items in the bucket

    else if(HashTable[index] ->name == name) {
        delPtr = HashTable[index];

        HashTable[index] = HashTable[index] -> next;
        delete delPtr;
        cout << name << " was removed from the hash table. \n";

    }


    // case 3: the bucket contains items, but first item is not a match
    else {
        P1 = HashTable[index] -> next;
        P2 = HashTable[index];
        while(P1 != NULL && P1 -> name != name) {
            P2 = P1;
            P1 = P1 -> next;
        }
         // case 3.1: no match
        if(P1 ==NULL) {
            cout << name << " was not found in the hash table. \n";
         }
        //case 3.2: match is found
        else {
            delPtr = P1;
            P1 = P1 -> next;
            P2 -> next = P1;

            delete delPtr;
            cout << name << " was removed from the hash table. \n";

        }


    }

}

根据提示, 运行结果如下:




  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,我无法提供实际的编程功能,但我可以给您提供一个基本的哈希表实现示例,如下所示: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define TABLE_SIZE 100 struct node { char *key; char *value; struct node *next; }; struct hash_table { struct node *list[TABLE_SIZE]; }; int hash_function(char *key) { int hash_value = 0; for (int i = 0; i < strlen(key); i++) { hash_value += key[i]; } return hash_value % TABLE_SIZE; } struct node *create_node(char *key, char *value) { struct node *new_node = (struct node *) malloc(sizeof(struct node)); new_node->key = (char *) malloc(strlen(key) + 1); new_node->value = (char *) malloc(strlen(value) + 1); strcpy(new_node->key, key); strcpy(new_node->value, value); new_node->next = NULL; return new_node; } void insert(struct hash_table *ht, char *key, char *value) { int index = hash_function(key); struct node *new_node = create_node(key, value); new_node->next = ht->list[index]; ht->list[index] = new_node; } char *search(struct hash_table *ht, char *key) { int index = hash_function(key); struct node *current_node = ht->list[index]; while (current_node != NULL) { if (strcmp(current_node->key, key) == 0) { return current_node->value; } current_node = current_node->next; } return NULL; } void delete(struct hash_table *ht, char *key) { int index = hash_function(key); struct node *current_node = ht->list[index]; struct node *previous_node = NULL; while (current_node != NULL) { if (strcmp(current_node->key, key) == 0) { if (previous_node == NULL) { ht->list[index] = current_node->next; } else { previous_node->next = current_node->next; } free(current_node->key); free(current_node->value); free(current_node); return; } previous_node = current_node; current_node = current_node->next; } } int main() { struct hash_table ht; for (int i = 0; i < TABLE_SIZE; i++) { ht.list[i] = NULL; } insert(&ht, "apple", "a sweet fruit"); insert(&ht, "banana", "a yellow fruit"); insert(&ht, "cherry", "a small red fruit"); char *value = search(&ht, "apple"); if (value == NULL) { printf("Key not found\n"); } else { printf("Value: %s\n", value); } delete(&ht, "apple"); value = search(&ht, "apple"); if (value == NULL) { printf("Key not found\n"); } else { printf("Value: %s\n", value); } return 0; } ``` 这个示例实现了一个简单的哈希表,使用了一个基本的哈希函数,一个链式散列表,以及插入,查找和删除元

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值