★★★哈希查找算法(查找字符串中特定字符位置)----简单但是一定要掌握!!!

哈希查找(hash):哈希查找算法始终非常高效的查找算法,其时间复杂度在最好的情况下可以达到O(1),即使是比较坏的情况,时间复杂度也非常低,查找数据非常快。事物都有两面性,有优即有劣。虽然哈希查找非常高效,但是他的高效是以时间换空间的代价来实现的。在如今,空间问题我们很容易解决,比如加内存等。可是时间复杂度的提高就非常难,因此,在允许的情况下,如果可以选择空间换时间,那么我们就一般会选择牺牲空间换取时间上的高效。

在这里,我通过查找一个字符串中字符出现的位置来描述一下哈希查找的基本思想。

源字符串: “wgaslkjghasdljjhagh123*”
如上,上面是一个我们所要利用哈希查找使用的源字符串,该字符串中包含字母,数字,字符等。我们知道,每个字符都对应一个ASCALL码,因此,每个字母的ASCALL码都是唯一的。

而哈希查找算法的核心思想就是:利用一个键值对,通过键来获取值,每一个键对应一个值。在创建哈希表时,我们令arr[Key] = value,即利用一个数组来保存每一个键值对。因此,当我们获取到对应的键时,即可得到对应的值。由于要通过键来获取值,那么我们必须保证这里的键是唯一的,由于每个字符的ASCALL码是唯一的,因此我们可以将字符的ASCALL码作为键,将字符的位置作为值。(类似于函数的一 一映射)例如字符创"abcdef"的映射关系如下图:
在这里插入图片描述

这是一种映射关系即字符串“abcdef”的键位字符本身,值为其下标。但是,我们很快就会发现这种映射关系局限性非常大,即一个键只能映射一个值,那如果一个键同时出现在两个位置、三个位置,甚至更多位置呢?这是必然会的,在我们平时的字符串查找时,不可能一个字符串出现一次。如果一个字符串出现了多次,那么就相当于一个键对应了多个值,对于字符串"abcdefaa",有下面所示的映射关系:

在这里插入图片描述

如果是这种情况,那么在我们创建哈希表时,对于多次出现的键,后面的值就会覆盖前面的值,这当然是非常可怕的,因为这就相当于你前面所保存的数据丢失了!!!这也就是所谓的哈希冲突,哈希冲突是不可避免的。因此,为了尽可能的减少哈希冲突,设计了多种方法,而使用最多的就是拉链法。即,在进行保存数据时,如果有键出现了多次,那么同样保存在,该键所在位置,只是保存的位置要在之前的数据位置后,前一个数据指向后一个数据,也就是通过链表来将同一个键出现的多个值连接。当需要查找该键所对应的值时,就通过遍历该键所在的链表,来查找对应的数据。我们知道,链表的查找效率是非常低的,因此,如果一个键出现的次数非常多,那么使用拉链法时,哈希表的效率也将降低,如下如所示。
在这里插入图片描述链表的数据结构
我们都知道,链表必须得有一个指向其下一个元素的指针,并且,每个结点都得有值,因此链表的结构如下:

typedef struct node{
struct node *pNext;
int nValue;//由于我们要查找的是字符出现的位置,故value为整形
}Node;

创建什么样的数组呢?
因此当使用拉链法时,我们数组中的每个元素都相当于是一个链表的首地址,(每一个键对应的都可能是一个链表,因为键不可能会 只出现一次),由于数组的元素都是链表的头,而链表的头都是一个指针,因此数组的每个元素都得是一个指针,所以我们创建的数组为一个指针数组,并且数组的类型必须和其元素类型一一致,因此,数组的类型为Node*,即Node *arr[Number]。
创建多大的数组呢?
我们要想尽可能的保存每一个键,那么就需要一个能够存储足够键的数组,由于我们是使用字符的ASCALL码来作为键,即是char类型,而char为一个字节,其范围是0~255,因此创建的数组大小为256;

代码实现如下

#include <stdio.h>
#include <stdlib.h>

//链表节点类型
typedef struct node
{
	struct node *pNext;
	int nValue;
}Node;

#define Number 256 //数组大小宏

//创建Hash_Table
void CreateHashTable(Node *arr[],const char *src)
{
	if(src == NULL) return;
	//初始化数组
	int i;
	for(i = 0;i < Number;i++)
	arr[i] = NULL;
	//bzero(arr,sizeof(arr));
	i = 0;
	Node *pTemp = NULL;//记录新添加的节点
	Node *pMark = NULL;//标记添加位置
	while(src[i] != '\0')
	{
		//向Hash_Table添加元素
		pTemp = (Node*)malloc(sizeof(Node));
		pTemp->pNext = NULL;
		pTemp->nValue = i;
		if(arr[src[i]] == NULL)
		{
			arr[src[i]] = pTemp;
		}
		else
		{
			pMark = arr[src[i]];
			//找到要插入链表位置的尾,尾添加
			while(pMark->pNext != NULL)
				pMark = pMark->pNext;
			pMark->pNext = pTemp;
		}
		++i;
	}

}
//Hash_Search
void HashSearch(Node *arr[],const char ch,int position[])
{
	Node *pMark = arr[ch];
	int i = 0;
	while(pMark != NULL)
	{
		position[i] = pMark->nValue;
		++i;
		pMark = pMark->pNext;
	}

	//return position;
}
//销毁Hash_Table
void DestroyHashTable(Node *arr[])
{
	Node *pMark = NULL;
	Node *pDel = NULL;
	for(int i = 0;i < Number;i++)
	{
		pMark = arr[i];
		while(pMark != NULL)
		{
			pDel = pMark;
			pMark = pMark->pNext;
			free(pDel);
			pDel = NULL;
		}

	}
}

int main(void)
{
	//定义数组
	Node *arr[Number];
	char *src = "sdgadsfgfae";
	int position[Number];
	char ch = 'a';
	//将position数组里的值初始化为-1
	int i;
	for(int i = 0;i < Number;i++)
		position[i] = -1;
	CreateHashTable(arr,src);
	HashSearch(arr,ch,position);
		printf("The source character is [%s]\nThe char of [%c] position:",src,ch);
	i = 0;
	while(position[i] != -1 && i < Number)
	{
		printf("%d\t",position[i]);
		++i;
	}

		printf("\n");
		DestroyHashTable(arr);

	return 0;
}
  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bug.Remove()

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值