数据结构 --- c语言散列结构描述哈希

散列表

  • 描述单一个体:一个数据域,两个指针域

  • 纵向是一个有序链表,横向用来处理哈希冲突

  • 纵向有序是因为要有一个逻辑地址,否则不知道是否产生冲突

  • 有序指的是哈希地址有序,元素不一定是有序的

  • 32与12产生哈希冲突,以12这个节点为头节点创建一个横向链表,把产生哈希冲突的节点放在当前链表中,如果还有存在哈希冲突的元素接着放在后面即可,横向链表采用什么插入方式由自己决定(头插法 || 尾插法) 

数据结构 --- c语言散列结构描述哈希_第1张图片

 构建数据类型

#include 
#include 
#include 
#include 
struct dataType 
{
    int key;			//键
	char element[20];	//数据类型--->字符串类型
};

 横向链表的节点描述 

struct Node                 //节点
{
	struct dataType data;   //数据
	struct Node* next;      //指针
};

 创建节点

  • 把用户的数据变成一个节点

  • 为横向链表的插入做准备

struct Node* createNode(struct dataType data) 
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	assert(newNode);
    //给数据做初始化
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}

 散列表的节点描述--->散列的单一个体

  • 有两个指针:一个纵向指针,一个横向指针

struct skipListNode 
{
	struct dataType data;           //数据
	struct Node* firstNode;			//用firstNode表示整个横向链表 用来处理冲突
	struct skipListNode* next;		//纵向链表连接的指针
};

 创建纵向第一列的节点

struct skipListNode* createSkipNode(struct dataType data) 
{
	struct skipListNode* newNode = 
		(struct skipListNode*)malloc(sizeof(struct skipListNode));
	assert(newNode);
	newNode->data = data;
	newNode->firstNode = NULL;    //刚开始没有节点 不存在冲突 指向NULL
	newNode->next = NULL;         
	return newNode;
}

 哈希表的创建--->采用取余法

struct listHash
{
	struct skipListNode* headNode;   //由横向链表和纵向链表组合在一起
	int curSize;
	int divisor;                     //求hash地址|取余数去确定最大个数
};

 哈希结构的结构体描述--->用结构体指针表示哈希结构 

struct listHash* createHash(int divisor) 
{
	struct listHash* hash = (struct listHash*)malloc(sizeof(struct listHash));
	assert(hash);
    //描述哈希的最初状态
	hash->curSize = 0;
	hash->divisor = divisor;
	hash->headNode = NULL;    //刚开始哈希表没有节点 指向NULL
	return hash;
}

 插入数据

  • 不存在冲突,不需要考虑横向,只要创建一个纵向的有序链表即可

  • 如果表不存在,创建出来的节点要成为表头

  • 如果第 1 个元素的哈希地址 > 要插入元素的哈希地址,意味着要插入的节点成为第 1 个节点:nextSkipNode->next 指向原来哈希表的表头,哈希结构中的表头变成新的节点

  • 第 1 个节点不是指定位置,要找相邻两个节点:假设要插入 4,从第1个节点开始找,在纵向链表中找第一次大于 4 这个哈希地址的位置,把 4 插在前驱节点 pre 和当前节点 cur 中间

  • 横向链表也是一个无头链表,也需要考虑修改表头的情况

  • 横向链表中也可能会存在和横向链表的第一个元素产生哈希冲突的情况

数据结构 --- c语言散列结构描述哈希_第2张图片

//要插入的表 插入的数据
void insertData(struct listHash* hash, struct dataType data) 
{
	int dataHashPos = data.key % hash->divisor;               //求哈希地址
	struct skipListNode* newSkipNode = createSkipNode(data);  //纵向创建有序链表
	if (hash->headNode == NULL) 
	{
		hash->headNode = newSkipNode;                         //表为空
		hash->curSize++;                                      //插入节点要成为表头
	}
	else                                                      //不为空
	{
		//找相邻的两个节点 从第1个节点开始找   
		struct skipListNode* pmove = hash->headNode;          //移动的节点
		struct skipListNode* premove = NULL;                  //移动的前驱节点
		//考虑新节点的hash地址是最小,成为表头 第1个元素的hash地址>要插入的元素的hash地址
		if (pmove->data.key % hash->divisor > dataHashPos) 
		{
			newSkipNode->next = hash->headNode;               //插入的节点成为第1个节点
			hash->headNode = newSkipNode;
			hash->curSize++;
		}
		else
		{
			//纵向链表找相邻的两个节点(找到第一次大于插入元素hash地址位置)
			while (pmove != NULL && (pmove->data.key % hash->divisor < dataHashPos))
			{
				premove = pmove;                              //往下走
				pmove = pmove->next;
			}
			//分析结果   == dataHashPos表示存在冲突             //找到了
			if (pmove != NULL && (pmove->data.key % hash->divisor) == dataHashPos)
			{
				if (pmove->data.key == data.key)              //相同的key采用覆盖的方式
				{
					strcpy(pmove->data.element, data.element);
				}
				else
				{
					//key不相同做横向插入--->处理哈希冲突
					struct Node* newNode = createNode(data);  //创建新节点
					struct Node* ppMove = pmove->firstNode;   //也要考虑修改表头的情况
					if (ppMove == NULL)
					{
						newNode->next = pmove->firstNode;     //新节点next指向原来表头
						pmove->firstNode = newNode;           //原来的表头指向第1个节点
						hash->curSize++;
					}
					else   //判断横向链表是否存在相同键的节点    //不修改表头
					{
						while (ppMove != NULL && ppMove->data.key != data.key)
						{
							ppMove = ppMove->next;            //!=key一直往下找
						}
						if (ppMove == NULL)                   //没找到
						{
							newNode->next = pmove->firstNode;//没有相同的 表头法插入
							pmove->firstNode = newNode;
							hash->curSize++;
						}
						else                                  //找到相同的直接覆盖
						{
							strcpy(ppMove->data.element, data.element);
						}
					}
				}

			}
			else                                              //没找到==NULL不存在冲突
			{
				premove->next = newSkipNode; //为NULL 说明到达纵向链表表尾 直接插在表尾
				newSkipNode->next = pmove;   //当前哈希地址是最大的 pmove == NULL
				hash->curSize++;
			}
		}
	}
}

 打印哈希表--->打印数组

  • 打印纵向链表和横向链表

void printHash(struct listHash* hash) 
{
	struct skipListNode* pmove = hash->headNode;//定义1个移动的指针指向hash表的第1个节点
	while (pmove != NULL)                        //第1个节点不为空 打印纵向的第1个数据
	{
		printf("%d:%s\t\t", pmove->data.key, pmove->data.element);
		struct Node* ppMove = pmove->firstNode;  //横向链表不为空 打印横向链表中的节点
		while (ppMove != NULL)                     
		{
			printf("%d:%s\t\t", ppMove->data.key, ppMove->data.element);
			ppMove = ppMove->next;
		}
		pmove = pmove->next;                     //纵向链表移动到下一个位置
		printf("\n");
	}
}

 测试代码

int main()
{
	struct listHash* hash = createHash(10);    //创建纵向哈希链表
	struct dataType array[] =
	{
		1,"雷电",11,"春天",23,"四月",44,"baby",56,"virtual",
		54,"小白",73,"永远",72,"二月"
	};
	for (int i = 0; i < 8; i++) 
	{
		insertData(hash, array[i]);
	}
	printHash(hash);
	return 0;
}

//测试代码
1:雷电     11:春天
72:二月
23:四月    73:永远
44:baby    54:小白
56:virtual       

你可能感兴趣的:(数据结构,数据结构,c语言,散列结构描述哈希)