《数据结构与算法》课程设计——哈希表设计

前言

项目名称哈希表设计项目类型应用软件类
项目难度中等素材资源
使用工具不限编译系统Windows
硬件需求程序语言C/C++
知识点结构体/类、链表、查找、函数、键盘操作等
项目描述针对某个集体中人名设计一个哈希表,使得平均查找长度不超过R,并完成相应的建表和查表程序。
假设人名为中国人姓名的汉语拼音形式。待填入哈希表的人名共有3000个,取平均查找长度的上限R。
功能实现
  • 主菜单主界面与功能一览
  • 写一个函数生成3000个人的姓名
  • 哈希函数用除留余数法构造,用线性探测再散列法或链地址法处理冲突
  • 建立哈希表
代码行数300行及以上


 项目总体思路

  1. 设计哈希表数据结构:设计一个可动态分配内存大小的哈希表,可使用平衡二叉搜索树作为哈希表中每个槽位的数据结构。
  2. 设计平衡二叉搜索树:节点信息为储存人名的字符串数组指针,实现平衡二叉搜索树的建立、处理失衡、删除、插入、查询等操作。
  3. 处理冲突:在哈希表中可能会出现冲突,即不同的人名映射到了相同的哈希值。使用链地址法将相同哈希值的元素放入同一棵二叉树中。
  4. 建立哈希函数:用除留余数法作为哈希算法,将人名转换为一个哈希值映射到哈希表的存储位置。
  5. 编写初始化哈希表函数:该函数可根据传入参数的值来创建一个空的哈希表,每个槽位初始化为空的平衡二叉搜索树。
  6. 实现查找功能:编写函数实现在哈希表中查找指定人名的功能。根据给定的人名,使用哈希函数计算哈希值,根据哈希值在哈希表对应槽位上的二叉搜索树进行查找操作。
  7. 实现插入功能:编写函数实现在哈希表中插入指定人名的功能。先查找该人名是否在哈希表中,若不在,则计算哈希值,得到对应槽位,然后在该槽位上的二叉树中执行插入操作。
  8. 实现删除功能:编写函数实现在哈希表中删除指定人名的功能。先查找该人名是否在哈希表中,若在,则计算哈希值,定位到相应槽位,在该槽位上的二叉树执行删除操作。
  9. 编写生成人名函数:编写一个函数,可生成n个人的姓名。可以初始化一些字符串数组,使用随机算法在数组中随机挑选字符串组成人名。
  10. 动态扩容哈希表:当哈希表容量不满足需求时,动态扩容,创建一个更大的哈希表,将旧哈希表中的数据拷贝到新哈希表,释放旧哈希表内存。
  11. 设计主菜单与功能一览:打印一个主菜单界面,列出各种功能选项,比如建立哈希表、插入人名、查找人名等。可以通过输入一个值来实现。
  12. 其他功能可根据需求拓展。

项目实现

通过结合人名哈希表和平衡二叉搜索树,可以同时享受哈希表高效的插入、查询和删除操作,以及平衡二叉搜索树的有序性和快速搜索能力。

  • 定义数据结构

定义二叉树节点结构体,用于储存人名;定义哈希表结构体,表槽位指针用来指向二叉树根节点。

//树节点结构体
typedef struct Node {
	char* name;
	struct Node* left;
	struct Node* right;
	int height;
}Node;
//哈希表结构体
typedef struct {
	Node** Table;
	int size;
}HashTable;

  • 初始化哈希表

创建一个指定大小的哈希表,并将每个槽位初始化为NULL。

//初始化哈希表
HashTable* InitHashTable(int size) {
	if (size <= 0) {
		return NULL;
	}
	HashTable* H = malloc(sizeof(HashTable));
	H->size = size;
	H->Table = (Node**)malloc(sizeof(Node*) * size);
	//将每个指针置为NULL
	for (int i = 0; i < size; i++) {
		H->Table[i] = NULL;
	}
	return H;
}

  •  哈希函数 

使用除留余数法计算人名的哈希值。

//哈希函数(除留余数法)
int Hash(char* name, int size) {
	int seed = 11;//乘积种子
	int h = 0;
	while (*name) {
		h = h * seed + *name;
		h %= size;
		name++;
	}
	if (h < 0) {
		return -h;
	}
	return h;
}

  •  平衡二叉搜索树的实现 

获得节点高度
int GetHeight(Node* node) {
	if (node == NULL)
		return 0;
	return node->height;
}
计算平衡因子
int GetBF(Node* node) {
	if (node == NULL)
		return 0;
	return GetHeight(node->left) - GetHeight(node->right);
}
更新节点高度
void UpdatHeight(Node* node) {
	int leftheight = GetHeight(node->left);//左子树高度
	int rightheight = GetHeight(node->right);//右子树高度
	//取高度大的那个再加1
	node->height = (leftheight > rightheight ? leftheight : rightheight) + 1;
}
创建一个节点
Node* CreateNode(char* name) {
	Node* newnode = malloc(sizeof(Node));
	newnode->name = name;
	newnode->height = 1;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}
平衡调节操作
//右旋操作
Node* RotateRight(Node* y) {
	Node* x = y->left;
	Node* t = x->right;
	x->right = y;
	y->left = t;
	UpdatHeight(x);
	UpdatHeight(y);

	return x;
}

//左旋操作
Node* RotateLeft(Node* x) {
	Node* y = x->right;
	Node* t = y->left;
	y->left = x;
	x->right = t;
	UpdatHeight(x);
	UpdatHeight(y);
	return y;
}
插入节点
Node* InsertNode(Node* node, char* name) {
	//若当前节点为空,创建一个节点
	if (node == NULL) {
		return CreateNode(name);
	}
	//若插入值比当前节点小,插入到左子树
	if (strcmp(name, node->name) < 0) {
		node->left = InsertNode(node->left, name);
	}
	//若插入值比当前节点大,插入到右子树
	else if (strcmp(name, node->name) > 0) {
		node->right = InsertNode(node->right, name);
	}
	//若与当前节点相等,直接返回
	else {
		return node;
	}

	//更新节点高度
	UpdatHeight(node);

	//平衡调整
		//获得平衡因子
	int BF = GetBF(node);

	//LL情况,插入左孩子左子树,右旋
	if (BF > 1 && strcmp(name, node->left->name) < 0) {
		return RotateRight(node);
	}
	//RR情况,插入右孩子右子树,左旋
	if (BF < -1 && strcmp(name, node->right->name)>0) {
		return RotateLeft(node);
	}
	//LR情况,插入左孩子右子树,先对左子树左旋,再对根节点右旋
	if (BF > 1 && strcmp(name, node->left->name) > 0) {
		node->left = RotateLeft(node->left);
		return RotateRight(node);
	}
	//RL情况,插入右孩子左子树,先对右子树右旋,再对根节点左旋
	if (BF < -1 && strcmp(name, node->right->name) < 0) {
		node->right = RotateRight(node->right);
		return RotateLeft(node);
	}
	return node;
}
查找最小值节点
Node* MinValNode(Node* node) {
	Node* t = node;
	while (t->left != NULL)
		t = t->left;
	return t;
}
 查找节点
Node* searchnode(Node* root, char* name) {
	//节点为空或找到值返回节点
	if (root == NULL || strcmp(name, root->name) == 0) {
		return root;
	}
	//查找值比当前节点小,继续查找左子树
	if (strcmp(name, root->name) < 0) {
		return searchnode(root->left, name);
	}
	//继续查找右子树
	return searchnode(root->right, name);
}
删除节点
Node* DeleteNode(Node* root, char* name) {
	//若未找到要删除节点
	if (root == NULL) {
		return root;
	}
	//查找左子树
	if (strcmp(name, root->name) < 0) {
		root->left = DeleteNode(root->left, name);
	}
	//查找右子树
	else if (strcmp(name, root->name) > 0) {
		root->right = DeleteNode(root->right, name);
	}

	//若找到
	else {
		//若要删除节点最多只有一个子树
		if (root->left == NULL || root->right == NULL) {
			Node* temp = root->left ? root->left : root->right;
			if (temp == NULL) {
				temp = root;
				root = NULL;
			}
			else {
				*root = *temp;
			}
			free(temp);
		}

		//若要删除节点有两个子树
		else {
			//找到右子树最小的孩子作为根节点
			Node* temp = MinValNode(root->right);
			root->name = temp->name;//复制数据
			root->right = DeleteNode(root->right, temp->name);//删除该右孩子
		}
	}

	//若根节点为空,直接返回
	if (root == NULL) {
		return root;
	}
	//else
	//更新节点高度
	UpdatHeight(root);

	//平衡调整
	int BF = GetBF(root);

	if (BF > 1 && strcmp(name, root->left->name) < 0) {
		return RotateRight(root);
	}
	if (BF < -1 && strcmp(name, root->right->name)>0) {
		return RotateLeft(root);
	}
	if (BF > 1 && strcmp(name, root->left->name) > 0) {
		root->left = RotateLeft(root->left);
		return RotateRight(root);
	}
	if (BF < -1 && strcmp(name, root->right->name) < 0) {
		root->right = RotateRight(root->right);
		return RotateLeft(root);
	}
	return root;
}
释放节点内存
void freenode(Node* node) {
	if (node == NULL) {
		return;
	}
	freenode(node->left);
	freenode(node->right);
	free(node);
}

二叉树迭代版中序遍历
void Inorder(Node* root) {
	Node* Stack[1000];
	int top = 0;
	Node* p = root;
	while (p != NULL || top > 0) {
		//将左子树入栈
		while (p != NULL) {
			Stack[top++] = p;
			p = p->left;
		}
		//出栈,打印中间节点
		p = Stack[--top];
		printf("(%s)  ", p->name);
		//遍历右子树
		p = p->right;
	}
}

  •  哈希表的查询

查询指定人名是否在哈希表中,在则返回该节点。

//查询哈希节点
Node* Search(HashTable* H, char* name) {
	if (H == NULL) {
		return NULL;
	}
	int h = Hash(name, H->size);//计算哈希值
	//查找哈希表槽位指向的树,并返回结果
	return searchnode(H->Table[h], name);;
}

  •  哈希表的插入

将指定人名插入到哈希表中

//插入哈希节点
bool Insert(HashTable* H, char* name) {
	if (H == NULL) {
		return false;
	}
	//查询插入姓名是否已存在
	if (Search(H, name) != NULL) {
		return false;
	}
	int h = Hash(name, H->size);//计算哈希值
	H->Table[h] = InsertNode(H->Table[h], name);//插入到二叉树中
	return true;
}

  •  哈希表节点删除

删除哈希表中指定的人名

//删除哈希节点
bool Delete(HashTable* H, char* name) {
	if (H == NULL) {
		return false;
	}
	int h = Hash(name, H->size);
	//删除节点,并判断是否删除成功
	if (DeleteNode(H->Table[h], name) == NULL) {
		return false;
	}
	return true;
}


  •  随机生成人名

利用随机数生成器在存有汉语拼音的数组中随机选取拼音组成拼音姓名。

//生成人名
char** GenerateName(int n) {
	if (n <= 0) {
		return NULL;
	}
	//定义储存姓的数组
	char surname[][10] = { "zhao","qian","sun","li","zhou","wu","zheng","wang","feng","chen","chu","wei","jiang",
		"shen","han","yang","zhu","qin","you","xu","he","lv","shi","zhang","kong","cao","yan","hua","jin","wen" };
	//定义储存名的两个数组
	char givenname1[][10] = { "cheng","jin","kun","niu","shui","ma","diu","lei","lou","mou","cao","shi","tian",
		"ru","bu","ka","zu","dou","xin","ta","tuan","zuan","yuan","wen","hao","san","jiu","guan","cai","bing" };
	char givenname2[][10] = { "bo","","fa","run","","","","lan","yv","","","","","qian","ruan","liu","bian","",
	"shi","chen","","","fen","","","jiang","ta","","","pu","chu","ming","","","","ding","niang","wo","","","",
	"qing","zhuang","pian","","","","fan","mian","kan" };
	char** name = malloc(sizeof(char*) * n);//创建储存人名的数组
	int i;
	for (i = 0; i < n; i++) {
		char* temp = malloc(sizeof(char) * 30);//储存单个生成的人名
		srand((unsigned int)time(NULL) - i);//设置随机生成器的种子
		strcpy(temp, surname[rand() % 30]);//随机生成姓
		strcat(temp, givenname1[rand() % 30]);//随机生成名
		strcat(temp, givenname2[rand() % 50]);//随机生成名
		name[i] = temp;
	}
	return name;
}

  • 销毁哈希表

释放哈希表内存。

//销毁哈希表
void freehashtable(HashTable* H) {
	if (H == NULL) {
		return;
	}
	//将哈希表节点指向的每棵树释放内存
	for (int i = 0; i < H->size; i++) {
		if (H->Table[i] != NULL) {
			freenode(H->Table[i]);
		}
	}
}

  • 扩容哈希表

对哈希表动态扩容指定倍数容量,创建一个新哈希表,利用队列对二叉树层序遍历并将节点信息插入到新哈希表,最后释放原来哈希表内存。

队列实现

//队列节点结构体
typedef struct Qnode {
	Node* data;
	struct Qnode* next;
}Qnode;
//队列结构体
typedef struct {
	Qnode* front;
	Qnode* rear;
}Queue;

//初始化队列
Queue* InitQueue() {
	Queue* Q = malloc(sizeof(Queue));
	Q->front = (Qnode*)malloc(sizeof(Qnode));
	Q->front->data = NULL;
	Q->front->next = NULL;
	Q->rear = Q->front;
	return Q;
}

//入队操作
void Push(Queue* Q, Node* p) {
	Qnode* newnode = malloc(sizeof(Qnode));
	newnode->data = p;
	newnode->next = NULL;
	Q->rear->next = newnode;
	Q->rear = newnode;
}

//出队操作
Node* Pop(Queue* Q) {
	if (Q->front == Q->rear) {
		return NULL;
	}
	Qnode* q = Q->front->next;
	Q->front->next = q->next;
	Node* p = q->data;
	if (Q->rear == q) {
		Q->rear = Q->front;
	}
	free(q);
	return p;
}

 扩容

//扩容哈希表
bool ResizeHashTable(HashTable* H, int mup) {
	//判断倍数参数是否合法
	if (mup <= 0) {
		return false;
	}
	//创建一个新哈希表
	HashTable* newH = InitHashTable(H->size * (mup + 1));

	//拷贝旧哈希表数据到新哈希表
	for (int i = 0; i < H->size; i++) {
		if (H->Table[i] != NULL) {
			//用队列对树层序遍历并将节点数据插入到新哈希表
			Queue* Q = InitQueue();
			Push(Q, H->Table[i]);
			Node* p = NULL;
			while ((p = Pop(Q)) != NULL) {
				Insert(newH, p->name);
				if (p->left != NULL)
					Push(Q, p->left);
				if (p->right != NULL)
					Push(Q, p->right);
			}
			free(Q);
		}
	}

	//释放旧哈希表内存
	freehashtable(H);
	//更新数据
	H->Table = newH->Table;
	H->size = newH->size;
	free(newH);
	return true;
}

  •  打印生成人名单

//打印名单
void print_name(char** name, int name_size) {
	int i, j;
	for (i = 0; i < name_size;) {
		for (j = 1; j < 10 && i < name_size; j++) {
			printf("%s   ", name[i++]);
		}
		printf("\n");
	}
}
  •  打印哈希表 

//打印哈希表
void print_hashtable(HashTable* H) {
	Node* p = NULL;
	for (int i = 0; i < H->size; i++) {
		printf("%d ——> ", i);
		p = H->Table[i];
		Inorder(H->Table[i]);
		printf("\n");
	}
}
  • 打印主菜单界面

设计一个用户友好的主菜单界面,列出该程序选项。

//主菜单界面
void print_menu() {
	printf("\n==============================哈希查找程序====================================\n");
	printf("\n");
	printf("输入一个数字如:\n");
	printf("1:初始化一个大小为n的哈希表\n");
	printf("2:生成n个人名\n");
	printf("3:打印当前人名单\n");
	printf("4:将当前人名插入哈希表\n");
	printf("5:手动输入n个名字插入哈希表\n");
	printf("6:查找人名\n");
	printf("7:删除人名\n");
	printf("8:打印哈希表\n");
	printf("9:对哈希表动态扩容\n");
	printf("10:销毁哈希表\n");
	printf("0:退出程序\n");
	printf("\n");
	printf("==============================================================================\n\n");
}

源代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>

//树节点结构体
typedef struct Node {
	char* name;
	struct Node* left;
	struct Node* right;
	int height;
}Node;
//哈希表结构体
typedef struct {
	Node** Table;
	int size;
}HashTable;

//获得节点高度
int GetHeight(Node* node) {
	if (node == NULL)
		return 0;
	return node->height;
}

//计算节点平衡因子
int GetBF(Node* node) {
	if (node == NULL)
		return 0;
	return GetHeight(node->left) - GetHeight(node->right);
}

//更新节点高度
void UpdatHeight(Node* node) {
	int leftheight = GetHeight(node->left);//左子树高度
	int rightheight = GetHeight(node->right);//右子树高度
	//取高度大的那个+1
	node->height = (leftheight > rightheight ? leftheight : rightheight) + 1;
}

//创建一个新节点
Node* CreateNode(char* name) {
	Node* newnode = malloc(sizeof(Node));
	newnode->name = name;
	newnode->height = 1;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

//右旋操作
Node* RotateRight(Node* y) {
	Node* x = y->left;
	Node* t = x->right;
	x->right = y;
	y->left = t;
	UpdatHeight(x);
	UpdatHeight(y);

	return x;
}

//左旋操作
Node* RotateLeft(Node* x) {
	Node* y = x->right;
	Node* t = y->left;
	y->left = x;
	x->right = t;
	UpdatHeight(x);
	UpdatHeight(y);
	return y;
}

//插入节点
Node* InsertNode(Node* node, char* name) {
	//若当前节点为空,创建一个节点
	if (node == NULL) {
		return CreateNode(name);
	}
	//若插入值比当前节点小,插入到左子树
	if (strcmp(name, node->name) < 0) {
		node->left = InsertNode(node->left, name);
	}
	//若插入值比当前节点大,插入到右子树
	else if (strcmp(name, node->name) > 0) {
		node->right = InsertNode(node->right, name);
	}
	//若与当前节点相等,直接返回
	else {
		return node;
	}

	//更新节点高度
	UpdatHeight(node);

	//平衡调整
		//获得平衡因子
	int BF = GetBF(node);

	//LL情况,插入左孩子左子树,右旋
	if (BF > 1 && strcmp(name, node->left->name) < 0) {
		return RotateRight(node);
	}
	//RR情况,插入右孩子右子树,左旋
	if (BF < -1 && strcmp(name, node->right->name)>0) {
		return RotateLeft(node);
	}
	//LR情况,插入左孩子右子树,先对左子树左旋,再对根节点右旋
	if (BF > 1 && strcmp(name, node->left->name) > 0) {
		node->left = RotateLeft(node->left);
		return RotateRight(node);
	}
	//RL情况,插入右孩子左子树,先对右子树右旋,再对根节点左旋
	if (BF < -1 && strcmp(name, node->right->name) < 0) {
		node->right = RotateRight(node->right);
		return RotateLeft(node);
	}
	return node;
}

//查找最小值节点
Node* MinValNode(Node* node) {
	Node* t = node;
	while (t->left != NULL)
		t = t->left;
	return t;
}

//查找节点
Node* searchnode(Node* root, char* name) {
	//节点为空或找到值返回节点
	if (root == NULL || strcmp(name, root->name) == 0) {
		return root;
	}
	//查找值比当前节点小,继续查找左子树
	if (strcmp(name, root->name) < 0) {
		return searchnode(root->left, name);
	}
	//继续查找右子树
	return searchnode(root->right, name);
}

//删除节点
Node* DeleteNode(Node* root, char* name) {
	//若未找到要删除节点
	if (root == NULL) {
		return root;
	}
	//查找左子树
	if (strcmp(name, root->name) < 0) {
		root->left = DeleteNode(root->left, name);
	}
	//查找右子树
	else if (strcmp(name, root->name) > 0) {
		root->right = DeleteNode(root->right, name);
	}

	//若找到
	else {
		//若要删除节点最多只有一个子树
		if (root->left == NULL || root->right == NULL) {
			Node* temp = root->left ? root->left : root->right;
			if (temp == NULL) {
				temp = root;
				root = NULL;
			}
			else {
				*root = *temp;
			}
			free(temp);
		}

		//若要删除节点有两个子树
		else {
			//找到右子树最小的孩子作为根节点
			Node* temp = MinValNode(root->right);
			root->name = temp->name;//复制数据
			root->right = DeleteNode(root->right, temp->name);//删除该右孩子
		}
	}

	//若根节点为空,直接返回
	if (root == NULL) {
		return root;
	}
	//else
	//更新节点高度
	UpdatHeight(root);

	//平衡调整
	int BF = GetBF(root);

	if (BF > 1 && strcmp(name, root->left->name) < 0) {
		return RotateRight(root);
	}
	if (BF < -1 && strcmp(name, root->right->name)>0) {
		return RotateLeft(root);
	}
	if (BF > 1 && strcmp(name, root->left->name) > 0) {
		root->left = RotateLeft(root->left);
		return RotateRight(root);
	}
	if (BF < -1 && strcmp(name, root->right->name) < 0) {
		root->right = RotateRight(root->right);
		return RotateLeft(root);
	}
	return root;
}

//释放节点内存
void freenode(Node* node) {
	if (node == NULL) {
		return;
	}
	freenode(node->left);
	freenode(node->right);
	free(node);
}

//二叉树中序遍历
void Inorder(Node* root) {
	Node* Stack[1000];
	int top = 0;
	Node* p = root;
	while (p != NULL || top > 0) {
		//将左子树入栈
		while (p != NULL) {
			Stack[top++] = p;
			p = p->left;
		}
		//出栈,打印中间节点
		p = Stack[--top];
		printf("(%s)  ", p->name);
		//遍历右子树
		p = p->right;
	}
}

//初始化哈希表
HashTable* InitHashTable(int size) {
	if (size <= 0) {
		return NULL;
	}
	HashTable* H = malloc(sizeof(HashTable));
	H->size = size;
	H->Table = (Node**)malloc(sizeof(Node*) * size);
	//将每个指针置为NULL
	for (int i = 0; i < size; i++) {
		H->Table[i] = NULL;
	}
	return H;
}

//哈希函数(除留余数法)
int Hash(char* name, int size) {
	int seed = 11;//乘积种子
	int h = 0;
	while (*name) {
		h = h * seed + *name;
		h %= size;
		name++;
	}
	if (h < 0) {
		return -h;
	}
	return h;
}

//查询哈希节点
Node* Search(HashTable* H, char* name) {
	if (H == NULL) {
		return NULL;
	}
	int h = Hash(name, H->size);//计算哈希值
	//查找哈希表节点指向的树,并返回结果
	return searchnode(H->Table[h], name);;
}

//插入哈希节点
bool Insert(HashTable* H, char* name) {
	if (H == NULL) {
		return false;
	}
	//查询插入姓名是否已存在
	if (Search(H, name) != NULL) {
		return false;
	}
	int h = Hash(name, H->size);//计算哈希值
	H->Table[h] = InsertNode(H->Table[h], name);
	return true;
}

//删除哈希节点
bool Delete(HashTable* H, char* name) {
	if (H == NULL) {
		return false;
	}
	int h = Hash(name, H->size);
	//删除节点,并判断是否删除成功
	if (DeleteNode(H->Table[h], name) == NULL) {
		return false;
	}
	return true;
}

//销毁哈希表
void freehashtable(HashTable* H) {
	if (H == NULL) {
		return;
	}
	//将哈希表节点指向的每棵树释放内存
	for (int i = 0; i < H->size; i++) {
		if (H->Table[i] != NULL) {
			freenode(H->Table[i]);
		}
	}
}

//队列节点结构体
typedef struct Qnode {
	Node* data;
	struct Qnode* next;
}Qnode;
//队列结构体
typedef struct {
	Qnode* front;
	Qnode* rear;
}Queue;

//初始化队列
Queue* InitQueue() {
	Queue* Q = malloc(sizeof(Queue));
	Q->front = (Qnode*)malloc(sizeof(Qnode));
	Q->front->data = NULL;
	Q->front->next = NULL;
	Q->rear = Q->front;
	return Q;
}

//入队操作
void Push(Queue* Q, Node* p) {
	Qnode* newnode = malloc(sizeof(Qnode));
	newnode->data = p;
	newnode->next = NULL;
	Q->rear->next = newnode;
	Q->rear = newnode;
}

//出队操作
Node* Pop(Queue* Q) {
	if (Q->front == Q->rear) {
		return NULL;
	}
	Qnode* q = Q->front->next;
	Q->front->next = q->next;
	Node* p = q->data;
	if (Q->rear == q) {
		Q->rear = Q->front;
	}
	free(q);
	return p;
}

//扩容哈希表
bool ResizeHashTable(HashTable* H, int mup) {
	//判断倍数参数是否合法
	if (mup <= 0) {
		return false;
	}
	//创建一个新哈希表
	HashTable* newH = InitHashTable(H->size * (mup + 1));

	//拷贝旧哈希表数据到新哈希表
	for (int i = 0; i < H->size; i++) {
		if (H->Table[i] != NULL) {
			//用队列对树层序遍历并将节点数据插入到新哈希表
			Queue* Q = InitQueue();
			Push(Q, H->Table[i]);
			Node* p = NULL;
			while ((p = Pop(Q)) != NULL) {
				Insert(newH, p->name);
				if (p->left != NULL)
					Push(Q, p->left);
				if (p->right != NULL)
					Push(Q, p->right);
			}
			free(Q);
		}
	}

	//释放旧哈希表内存
	freehashtable(H);
	//更新数据
	H->Table = newH->Table;
	H->size = newH->size;
	free(newH);
	return true;
}

//生成人名
char** GenerateName(int n) {
	if (n <= 0) {
		return NULL;
	}
	//定义储存姓的数组
	char surname[][10] = { "zhao","qian","sun","li","zhou","wu","zheng","wang","feng","chen","chu","wei","jiang",
		"shen","han","yang","zhu","qin","you","xu","he","lv","shi","zhang","kong","cao","yan","hua","jin","wen" };
	//定义储存名的两个数组
	char givenname1[][10] = { "cheng","jin","kun","niu","shui","ma","diu","lei","lou","mou","cao","shi","tian",
		"ru","bu","ka","zu","dou","xin","ta","tuan","zuan","yuan","wen","hao","san","jiu","guan","cai","bing" };
	char givenname2[][10] = { "bo","","fa","run","","","","lan","yv","","","","","qian","ruan","liu","bian","",
	"shi","chen","","","fen","","","jiang","ta","","","pu","chu","ming","","","","ding","niang","wo","","","",
	"qing","zhuang","pian","","","","fan","mian","kan" };
	char** name = malloc(sizeof(char*) * n);//创建储存人名的数组
	int i;
	for (i = 0; i < n; i++) {
		char* temp = malloc(sizeof(char) * 30);//储存单个生成的人名
		srand((unsigned int)time(NULL) - i);//设置随机生成器的种子
		strcpy(temp, surname[rand() % 30]);//随机生成姓
		strcat(temp, givenname1[rand() % 30]);//随机生成名
		strcat(temp, givenname2[rand() % 50]);//随机生成名
		name[i] = temp;
	}
	return name;
}

//打印名单
void print_name(char** name, int name_size) {
	int i, j;
	for (i = 0; i < name_size;) {
		for (j = 1; j < 10 && i < name_size; j++) {
			printf("%s   ", name[i++]);
		}
		printf("\n");
	}
}

//打印哈希表
void print_hashtable(HashTable* H) {
	Node* p = NULL;
	for (int i = 0; i < H->size; i++) {
		printf("%d ——> ", i);
		p = H->Table[i];
		Inorder(H->Table[i]);
		printf("\n");
	}
}

//主菜单界面
void print_menu() {
	printf("\n==============================哈希查找程序====================================\n");
	printf("\n");
	printf("输入一个数字如:\n");
	printf("1:初始化一个大小为n的哈希表\n");
	printf("2:生成n个人名\n");
	printf("3:打印当前人名单\n");
	printf("4:将当前人名插入哈希表\n");
	printf("5:手动输入n个名字插入哈希表\n");
	printf("6:查找人名\n");
	printf("7:删除人名\n");
	printf("8:打印哈希表\n");
	printf("9:对哈希表动态扩容\n");
	printf("10:销毁哈希表\n");
	printf("0:退出程序\n");
	printf("\n");
	printf("==============================================================================\n\n");
}

int main()
{
	int i, n;
	int name_size = 0;
	HashTable* H = NULL;
	Node* p = NULL;
	char** name = NULL;
	char temp[30];

	while (true) {
		print_menu();
		scanf("%d", &i);
		//退出
		if (i == 0) {
			break;
		}

		//初始化哈希表
		else if (i == 1) {
			printf("请输入n:\n");
			scanf("%d", &n);
			H = InitHashTable(n);
			if (H != NULL) {
				printf("初始化大小为%d的哈希表成功!\n", n);
			}
			else {
				printf("初始化失败!\n");
			}

		}

		//生成人名
		else if (i == 2) {
			printf("请输入n:\n");
			scanf("%d", &n);
			name = GenerateName(n);
			if (name != NULL) {
				name_size = n;
				printf("生成%d个人名成功!\n", n);
			}
			else {
				name_size = 0;
				printf("生成失败!\n");
			}
		}

		//打印人名
		else if (i == 3) {
			if (name == NULL) {
				printf("请先生成人名!\n");
				continue;
			}
			print_name(name, name_size);
			printf("\n");
		}

		//将人名插入哈希表
		else if (i == 4) {
			if (H == NULL) {
				printf("请先初始化一个哈希表!\n");
				continue;
			}
			for (int j = 0; j < name_size; j++) {
				Insert(H, name[j]);
			}
			printf("插入成功!\n");
		}

		//输入插入人名
		else if (i == 5) {
			if (H == NULL) {
				printf("请先初始化一个哈希表!\n");
				continue;
			}
			printf("请输入n:\n");
			scanf("%d", &n);
			for (int j = 0; j < n; j++) {
				printf("请输入一个名字:\n");
				scanf("%s", temp);
				if (!Insert(H, temp)) {
					printf("插入该姓名失败!\n");
				}
				else {
					printf("插入成功!\n");
				}
			}
		}

		//查找人名
		else if (i == 6) {
			if (H == NULL) {
				printf("请先初始化一个哈希表!\n");
				continue;
			}
			printf("请输入要查找的人名:\n");
			scanf("%s", temp);
			p = Search(H, temp);
			if (p != NULL) {
				printf("查找人名 %s 成功!\n", p->name);
			}
			else {
				printf("查找失败!\n");
			}
		}

		//删除人名
		else if (i == 7) {
			if (H == NULL) {
				printf("请先初始化一个哈希表!\n");
				continue;
			}
			printf("请输入要删除人名:\n");
			scanf("%s", temp);
			if (Delete(H, temp)) {
				printf("删除成功!\n");
			}
			else {
				printf("删除失败!\n");
			}
		}

		//打印哈希表
		else if (i == 8) {
			if (H == NULL) {
				printf("请先初始化一个哈希表!\n");
				continue;
			}
			printf("哈希表:\n");
			print_hashtable(H);
		}

		//扩容哈希表
		else if (i == 9) {
			if (H == NULL) {
				printf("请先初始化一个哈希表!\n");
				continue;
			}
			printf("当前哈希表空间为:%d\n", H->size);
			printf("请输入扩容倍数n\n");
			scanf("%d", &n);
			if (ResizeHashTable(H, n)) {
				printf("扩容成功!\n");
				printf("当前哈希表空间为:%d\n", H->size);
			}
			else {
				printf("扩容失败,请重新操作!\n");
			}
		}

		//销毁哈希表
		else if (i == 10) {
			if (H == NULL) {
				printf("不存在哈希表!\n");
				continue;
			}
			freehashtable(H);
			free(H);
			H = NULL;
			printf("销毁成功!\n");
		}

		else {
			printf("请重新输入!\n");
		}
	}

	return 0;
}

运行结果

初始化一个哈希表

生成人名

 打印人名

将人名插入哈希表,然后打印。

 对哈希表扩容

再次打印哈希表

 

 


 总结

有惊无险,已通过。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Indifferent-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值