• 【算法导论】简单哈希表的除法实现


            哈希表,又名散列表。hashtable。

    。。

    云云,看似非常高大上,事实上只是是直接寻址的延伸而已。直接寻址为何物,看一个数组:a[10],那么取当中一个元素a[1],这就是直接寻址,直接去这个a+1的地址上,就找到了这个数值,时间复杂度为O(1)。

    而哈希表的目的就是要让查找的时间复杂度尽量往O(1)上靠。


            一、哈希表的最简单形式


    假如有10000个数,比方0~9999,是可能出现的数字的集合,我们如今要将一段时间内,出现的数字,所有保存起来。假设出现的数字都不反复的情况下。我们能够使用一个长度为10000的数组a[10000]来保存。假设数字987出现了。那么我们直接将其保存在a[987]元素上,以此类推。当我们要查询某个数是否出现的时候,比方1002这个数,是否出现。我们能够直接去找a[1002]。看看值是否是1002,为了保险起见。也能够在初始化的时候将数组做-1初始化。这样。查找的时间复杂度就是O(1)。


            二、改进的哈希表


    上面的做法感觉是非常快。可是却有问题。太浪费空间了。还是上面的样例,假如可能出现的集合为0~9999这一万个数。可是我们能够预知,实际会出现的数字仅仅有最多10个。假设依照上面的做法。就须要为查找这10个数。而花费10000个空间?是不是太奢侈了点?

    其次,上面的做法,基础在于全部出现的数组。都不反复,假设我们人品太好,总共出现两个888怎么办?我们又想知道。888出现了几次?显然这时候。一个a[888]空间是不够的。


    为了解决上面两个问题,分两步来做:


    1、压缩存储空间。


    怎样压缩?这里要用到通常所说的哈希函数。哈希函数的作用,就是将大的集合数据,印射到一个相对较小的,我们能够接受的集合范围内。使得速度和内存空间达到一个平衡。比方这里。0~9999一万个可能出现的数字集合。而最多实际仅仅会出现10个。我们就能够使用a%b(取余)操作来处理,比方这里,我们能够使用a%10。来让全部出现的数据的范围由0~9999,变成0~9这十个数,然后就能够使用一个a[10]的数组,去搞定直接寻址。


    2、使用链表解决反复出现数据的问题


    像上面说的,出现两个888怎么搞?那么我们在a[888]这个位置上。不放元素。我们将a数组作为一个链表数组,a[888]放链表的位置,这样,出现两个888,每次都从链表的头部插入,这样就能放的下了。

    假设查找的时候,时间复杂度就不能是单单的O(1)了。我们考虑最坏的情况,比方n个元素的集合,数组的长度为m。当然(n>m)。这时候,除却哈希函数的取余操作的O(1),还要加上(n/m)的链表长度的查找,这是在全部位置链表的长度都同样的情况。假设链表的情况非常极端。。。这就不好了。。


    所以依据上面的分析,不难发现,这个哈希函数(散列函数)非常关键,最好是能让数据,平均的分布到各个位置的链表上,而不要集中到一个或某几个。由于这样会造成某一个链表的长度非常长,那么查询起来,时间复杂度就不理想了。


    关于散列函数(哈希函数)的取法,有非常多种。这里就不再讨论,以下给出除法(取余法)的散列函数,实现简单的哈希表代码:


    #include <iostream>
    using namespace std;
    
    /**
     * @作者:Alex/苦咖啡
     * @时间:2015.03.18
     * @博客:http://blog.csdn.net/cyp331203
     */
    
    struct node {
    	int key;
    	node* next;
    	node* pre;
    };
    
    struct List {
    
    	node* head;
    
    	List() :
    			head(NULL) {
    	}
    
    	~List() {
    		node* tmp = NULL;
    		while (head != NULL) {
    			tmp = head->next;
    			delete head;
    			head = tmp;
    		}
    	}
    
    	void print() {
    		node* tmp = head;
    		while (tmp != NULL) {
    			cout << tmp->key << ' ';
    			tmp = tmp->next;
    		}
    		cout << endl;
    	}
    
    	void insertHead(int key) {
    		if (head != NULL) {
    			node* n = new node();
    			n->key = key;
    			n->next = head;
    			head->pre = n;
    			head = n;
    		} else {
    			head = new node();
    			head->key = 5;
    		}
    	}
    
    	node* search(int key) {
    		node* pre = NULL;
    		node* curr = head;
    		while (curr != NULL && curr->key != key) {
    			pre = curr;
    			curr = curr->next;
    		}
    		if (curr == NULL) {
    			return NULL;
    		} else if (pre == NULL) {
    			return this->head;
    		} else {
    			return pre->next;
    		}
    	}
    
    	void deleteNode(node* n) {
    		if (n != NULL) {
    			n->pre->next = n->next;
    			n->next->pre = n->pre;
    			delete n;
    		}
    	}
    };
    
    //void deleteList(List* l) {
    //	if (l != NULL && (l->head) != NULL) {
    //		node* pre = NULL;
    //		node* curr = l->head;
    //		while (curr != NULL) {
    //			pre = curr;
    //			delete curr;
    //			curr = pre->next;
    //		}
    //	}
    //}
    
    struct HashTable {
    	List* arr;
    
    	HashTable() {
    		arr = new List[100];
    	}
    
    	//析构
    	~HashTable() {
    		for (int i = 0; i < 100; i++) {
    			if (arr + i != NULL) {
    //				deleteList(arr + i);
    				delete (arr+i);
    			}
    		}
    	}
    
    	void CHAINED_HASH_INSERT(int key) {
    		(arr + (key % 100))->insertHead(key);
    	}
    
    	node* CHAINED_HASH_SEARCH(int key) {
    		return (arr + (key % 100))->search(key);
    	}
    
    	void CHAINED_HASH_DELETE(node* n) {
    		if (n != NULL) {
    			(arr + (n->key % 100))->deleteNode(n);
    		}
    	}
    
    };
    
    int main() {
    
    	HashTable* ht = new HashTable();
    	ht->CHAINED_HASH_INSERT(5);
    	node* n = ht->CHAINED_HASH_SEARCH(5);
    
    //	cout << n->key << endl;
    
    	ht->CHAINED_HASH_INSERT(205);
    
    //	n.print();
    	ht->arr[5].print();
    
    	return 0;
    }
    




  • 相关阅读:
    经过我修改的一个记录集分页插件(C#)[原创]
    基于javascript的asp数据库操作类,含分页、字符串截取、用户登陆验证[原创]
    .net中关于windows身份验证的一个教训[原创]
    IE功能汇总[网上收集]
    一些珍藏代码[网上收集]
    对以前改的c#分页插件的补充[原创]
    ASP验证码图形生成[网上收集]
    关于嵌套使用DataList的心得[原创]
    浏览器标签显式网页logo
    金日开博
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5094405.html
Copyright © 2020-2023  润新知