C数据结构与算法——哈希表/散列表创建过程中的冲突与聚集(哈希查找) 应用

实验任务

(1) 掌握散列算法(散列函数、散列存储、散列查找)的实现;
(2) 掌握常用的冲突解决方法。

实验内容

(1) 选散列函数 H(key) = key % p,取散列表长 m 为 10000,p 取小于 m 的最大素数;
(2) 测试 α 对于散列算法效率的影响;
     分别测试将随机生成的5000个、7500个以及 p 个不重复的随机数序列放入该表中,采用线性探测法作为解决冲突方法时,各自的冲突总次数和聚集总次数
(3) 测试不同冲突解决方法对于散列算法效率的影响:
     分别测试随机生成的5000个不重复的随机数序列放入该表中时,采用线性探测法和二次探测法各自的冲突总次数和聚集总次数。
(4) 自行设计实验输出,应使结果尽可能清晰地被展示。

实验源码

#include <malloc.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>#define HASHSIZE 10000 // 散列表长度
#define NULLKEY 0 // 空值标记int conflictCount; // 冲突次数
int gatherCount; // 聚集次数typedef struct {int key;int gather;
} ElemType;// 散列表
typedef struct {ElemType *elem;int count;
} HashTable;int InitHashTable(HashTable *table); // 初始化散列表
int Hash(int key); // 除留余数法
void CreateRandomTable(int arr[], int randMinNum, int randLength); // 生成哈希表数据,用数组存放
void knuthShuffle(int arr[], int length); // 洗牌算法
void swapInt(int *card_1, int *card_2); // 交换函数
void InsertHashByLD(HashTable *table, int key); // 线性探测-插入关键字到散列表
void InsertHashBySD(HashTable *table, int key); // 二次探测-插入关键字到散列表
void HashTableDestroy(HashTable *table); // 销毁哈希表/*** <h2>哈希表/散列表的查找 实验二</h2>*/
int main() {// 业务逻辑printf("~~~~~~~~~~ 实现散列算法并对不同α和不同冲突解决方法进行对比 ~~~~~~~~~~\n");printf("\t\t====================================\n");printf("\t\t 1  线性探测-自定义生成随机数序列\n");printf("\t\t 2  线性探测-系统随机生成随机数序列\n");printf("\t\t 3  二次探测-自定义生成随机数序列\n");printf("\t\t 4  二次探测-系统随机生成随机数序列\n");printf("\t\t 5  退出\n");printf("\t\t 6  清屏\n");printf("\t\t====================================\n");int change = 1;while (1) {// 创建随机数种子srand(time(NULL));// 创建哈希表HashTable table;// 初始化哈希表if (InitHashTable(&table) == -1) {printf("申请地址失败");return 0;}// 生成扑克+洗牌算法(可指定随机数范围,且不会重复)int randMinNum = 1;int randMaxNum = 500000;int arr[randMaxNum - randMinNum];CreateRandomTable(arr, randMinNum, (randMaxNum - randMinNum));printf("请选择:");scanf("%d", &change);if (change == 1) {// 从洗好的牌中连续抽前arrLength张出来int arrLength = 5000;printf("线性探测-请输入自定义随机数个数:");scanf("%d", &arrLength);for (int i = 0; i < arrLength; i++) {InsertHashByLD(&table, arr[i]);}} else if (change == 2) {// 从洗好的牌中连续抽前随机张出来int arrLength = randMinNum + (rand() % HASHSIZE + 1);for (int i = randMinNum; i <= arrLength; i++) {InsertHashByLD(&table, arr[i]);}} else if (change == 3) {// 从洗好的牌中连续抽前arrLength张出来int arrLength = 5000;printf("二次探测-请输入自定义随机数个数:");scanf("%d", &arrLength);for (int i = randMinNum; i <= (randMinNum + arrLength); i++) {InsertHashBySD(&table, arr[i]);}} else if (change == 4) {// 从洗好的牌中连续抽前随机张出来int arrLength = randMinNum + (rand() % HASHSIZE + 1);for (int i = randMinNum; i <= arrLength; i++) {InsertHashBySD(&table, arr[i]);}} else if (change == 5) {break;} else if (change == 6) {system("cls"); // 清屏printf("~~~~~~~~~~ 实现散列算法并对不同α和不同冲突解决方法进行对比 ~~~~~~~~~~\n");printf("\t\t====================================\n");printf("\t\t 1  线性探测-自定义生成随机数序列\n");printf("\t\t 2  线性探测-系统随机生成随机数序列\n");printf("\t\t 3  二次探测-自定义生成随机数序列\n");printf("\t\t 4  二次探测-系统随机生成随机数序列\n");printf("\t\t 5  退出\n");printf("\t\t 6  清屏\n");printf("\t\t====================================\n");} else {printf("你的输入有误!!!\n");}if (change == 1 || change == 2 || change == 3 || change == 4) {printf("冲突次数 %d\n", conflictCount);printf("聚集次数 %d\n", gatherCount);printf("\n");}conflictCount = 0;gatherCount = 0;// 销毁哈希表HashTableDestroy(&table);}return 0;
}// 初始化
int InitHashTable(HashTable *table) {if (!table) {return -1;}table->count = HASHSIZE; // 表长table->elem = (ElemType *) malloc(sizeof(ElemType) * HASHSIZE); // 表空间if (!table->elem) {return -1; // 如果没有申请到地址,退出}for (int i = 0; i < HASHSIZE; i++) {table->elem[i].key = NULLKEY; // 所有单元全部初始化为空}return 0; // 初始化成功
}// 使用除留余数法创建哈希表
int Hash(int key) {// 求出最大素数for (int i = HASHSIZE; i > 0; i--) {int j = 2;for (; j <= i; j++) {if (i % j == 0) {break;}}if (i == j) {return key % i;}}return -999; // 抛出一个错误
}void CreateRandomTable(int arr[], int randMinNum, int randLength) {if (randMinNum == 0) {printf("生成的随机数不能包含哈希表空值标记 ( 0 ) \n");return;}for (int i = randMinNum; i <= randLength; i++) {arr[i] = i;}knuthShuffle(arr, randLength);
}void knuthShuffle(int arr[], int length) {for (int i = length - 1; i >= 1; i--) {swapInt(&arr[i], &arr[rand() % (i + 1)]);}
}void swapInt(int *card_1, int *card_2) {int tCard;tCard = *card_1;*card_1 = *card_2;*card_2 = tCard;
}// 线性探测法创建哈希表(这里保证散列表有足够的空间)
void InsertHashByLD(HashTable *table, int key) {int hashIndex = Hash(key); // 除留取余的方式给新插入的值分配散列位置while (table->elem[hashIndex].key != NULLKEY) { // 在插入的时候如果出现不等于空的位置,则说明遇到冲突if (table->elem[hashIndex].gather == -1) {gatherCount++; // 聚集} else {table->elem[hashIndex].gather = -1;conflictCount++; // 冲突}hashIndex = (hashIndex + 1) % HASHSIZE; // 线性探测}table->elem[hashIndex].key = key; // 放入哈希表
}// 二次探测法创建哈希表(这里保证散列表有足够的空间)
void InsertHashBySD(HashTable *table, int key) {int count = 0;int hashIndex = Hash(key); // 除留取余的方式给新插入的值分配散列位置int pos = hashIndex;while (table->elem[hashIndex].key != NULLKEY) { // 在插入的时候如果出现不等于空的位置,则说明遇到冲突count++;if (table->elem[hashIndex].gather == -1) {gatherCount++; // 聚集} else {table->elem[hashIndex].gather = -1;conflictCount++; // 冲突}hashIndex = (pos + count * count) % (HASHSIZE / 2); // 二次探测}table->elem[hashIndex].key = key; // 放入哈希表
}void HashTableDestroy(HashTable *table) {if (table->elem != NULL) {free(table->elem);table->elem = NULL;table->count = 0;}
}

实验结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/75968.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云安全组设置

简介​ 云主机安全组必须打开如下端口&#xff1a; ssh&#xff1a;22http&#xff1a;80https&#xff1a;443ftp&#xff1a;21、20000&#xff5e;30000 阿里云安全组端口开放教程​ 腾讯云安全组端口开放教程​ 华为云安全组端口开放教程​

C 语言高级2-多维数组,结构体,递归操作

1. 多维数组 1.1 一维数组 元素类型角度&#xff1a;数组是相同类型的变量的有序集合内存角度&#xff1a;连续的一大片内存空间 在讨论多维数组之前&#xff0c;我们还需要学习很多关于一维数组的知识。首先让我们学习一个概念。 1.1.1 数组名 考虑下面这些声明&#xff1…

华为云低代码平台Astro Canvas 搭建汽车展示大屏——实验指导手册

实验背景 大屏应用Astro Canvas是华为云低代码平台Astro的子服务之一&#xff0c;是以数据可视化为核心&#xff0c;以屏幕轻松编排&#xff0c;多屏适配可视为基础&#xff0c;用户可通过图形化界面轻松搭建专业水准的数据可视化大屏。例如汽车展示大屏、监控大屏、项目开发大…

docker端口映射详解(随机端口、指定IP端口、随意ip指定端口、指定ip随机端口)

目录 docker端口映射详解 一、端口映射概述&#xff1a; 二、案例实验&#xff1a; 1、-P选项&#xff0c;随机端口 2、使用-p可以指定要映射到的本地端口。 Local_Port:Container_Port&#xff0c;任意地址的指定端口 Local_IP:Local_Port:Container_Port 映射到指定地…

从零开始理解Linux中断架构(24)软中断核心函数__do_softirq

1)概要 __do_softirq函数处理是总是尽可能的执行所有未决软中断。 (1)关闭软中断:在preempt_count设置软中断标志:SOFTIRQ_OFFSET 让in_interrupt检查条件为真,进入软中断处理临界区,后面进来的处理请求,需要检查in_interrupt(),从而达到禁止本cpu上的软中断嵌套的目…

【C语言进阶】指针的高级应用(上)

本专栏介绍&#xff1a;免费专栏&#xff0c;并且会持续更新C语言知识&#xff0c;欢迎各位订阅关注。 关注我&#xff0c;带你了解更多关于机器人、嵌入式、人工智能等方面的优质文章&#xff0c;坚持更新&#xff01; 大家的支持才是更新的最强动力&#xff01; 文章目录 一、…

【Spring框架】Spring AOP

目录 什么是AOP&#xff1f;AOP组成Spring AOP 实现步骤Spring AOP实现原理JDK Proxy VS CGLIB 什么是AOP&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;⾯向切⾯编程&#xff0c;它是⼀种思想&#xff0c;它是对某⼀类事情的集中处理。⽐如…

林大数据结构【2019】

关键字&#xff1a; 哈夫曼树权值最小、哈夫曼编码、邻接矩阵时间复杂度、二叉树后序遍历、二叉排序树最差时间复杂度、非连通无向图顶点数&#xff08;完全图&#xff09;、带双亲的孩子链表、平衡二叉树调整、AOE网关键路径 一、判断 二、单选 三、填空 四、应用题

Flutter运行app时向logcat输出当前打开的界面路径且点击可跳转

当一个项目大了目录文件多了&#xff0c;我们往往会为了找到一个文件花费大量的时间和精力&#xff0c;为了快捷方便的调试我们的项目&#xff0c;我们往往需要在打开app运行的时候需要知道当前打开的界面的文件在哪儿&#xff0c;我们这个代码就能快捷的知道我们app正在打开的…

MySQL存储过程(二十四)

你相信吗&#xff0c; 相信那一天的夕阳吗? 上一章简单介绍了 MySQL的索引(二十三),如果没有看过,请观看上一章 一. 存储过程 MySQL从5.0版本开始支持存储过程和函数。存储过程和函数能够将复杂的SQL逻辑封装在一起&#xff0c; 应用程序无须关注存储过程和函数内部复杂的S…

浪潮服务器硬盘指示灯显示黄色的服务器数据恢复案例

服务器数据恢复环境&#xff1a; 宁夏某市某单位的一台浪潮服务器&#xff0c;该服务器中有一组由6块SAS硬盘组建的RAID5阵列。 服务器上存放的是Oracle数据库文件&#xff0c;操作系统层面划分了1个卷。 服务器故障&初检&#xff1a; 服务器在运行过程中有两块磁盘的指示灯…

一个3年Android的找工作记录

作者&#xff1a;Petterp 这是我最近 1个月 的找工作记录&#xff0c;希望这些经历对你会有所帮助。 有时机会就像一阵风&#xff0c;如果没有握住&#xff0c;那下一阵风什么时候吹来&#xff0c;往往是个运气问题。 写在开始 先说背景: 自考本&#xff0c;3年经验&#xff0…

掌握 JVM 的参数及配置

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ JVM&#xff08;Java虚拟机&#xff09;是Java编程语言的核心组件之一&#xff0c;它负责执行Java程序&#xff0c;并提供一系列参数和配置选项&#xff0c;可以调整Java程…

JVM | 从类加载到JVM内存结构

引言 我在上篇文章&#xff1a;JVM | 基于类加载的一次完全实践 中为你讲解如何请“建筑工人”来做一些定制化的工作。但是&#xff0c;大型的Java应用程序时&#xff0c;材料&#xff08;类&#xff09;何止数万&#xff0c;我们直接堆放在工地上&#xff08;JVM&#xff09;…

AI Chat 设计模式:12. 享元模式

本文是该系列的第十二篇&#xff0c;采用问答式的方式展开&#xff0c;问题由我提出&#xff0c;答案由 Chat AI 作出&#xff0c;灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 给我介绍一下享元模式A.1Q.2 也就是说&#xff0c;其实共享的是对象的内部状态&…

TCP的三次握手四次挥手

TCP的三次握手和四次挥手实质就是TCP通信的连接和断开。 三次握手&#xff1a;为了对每次发送的数据量进行跟踪与协商&#xff0c;确保数据段的发送和接收同步&#xff0c;根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系&#xff0c;并建立虚连接。 四次挥手&a…

【数据结构】快速排序

快速排序是一种高效的排序算法&#xff0c;其基本思想是分治法。它将一个大问题分解成若干个小问题进行解决&#xff0c;最后将这些解合并得到最终结果。 快速排序的主要思路如下&#xff1a; 选择一个基准元素&#xff1a;从待排序的数组中选择一个元素作为基准&#xff08;…

gitlab CI/CD 安装 gitlab runner

一、为什么需要安装gitlab runner &#xff1f; 极狐GitLab Runner 极狐GitLab Runner 是在流水线中运行作业的应用&#xff0c;与极狐GitLab CI/CD 配合运作。 说白了就是你部署的一个agent。 二、如何安装&#xff1f; 1.介绍通过helm部署github runner 2.helm添加仓库 h…

openCV图像读取和显示

文章目录 一、imread二、namedWindow三、imshow #include <opencv2/opencv.hpp> #include <iostream>using namespace std; using namespace cv;int main(int argc,char** argv) {cv::Mat img imread("./sun.png"); //3通道 24位if (img.empty()) {std:…

多语言gRPC开发入门与避坑指南

目录 gRPC相关介绍 什么是gPRC gPRC的优点 gPRC的缺点 gPRC定位 协议缓冲区&#xff08;Protocol Buffers&#xff09; 四种调用方式 gRPC开发三大步骤 第一步&#xff1a;定义和编写proto服务文件 第二步&#xff1a;proto文件转化为gRPC代码 第三步&#xff1a;调…