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

news2024/9/24 7:23:43

实验任务

(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.coloradmin.cn/o/839311.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

javaWeb项目--二级评论完整思路

先来看前端需要什么吧&#xff1a; 通过博客id&#xff0c;首先需要显示所有一级评论&#xff0c;包括评论者的头像&#xff0c;昵称&#xff0c;评论时间&#xff0c;评论内容 然后要显示每个一级评论下面的二级评论&#xff0c;包括&#xff0c;评论者的头像&#xff0c;昵称…

python:基于Kalman滤波器的移动物体位置估计

CSDN@_养乐多_ Kalman滤波器是一种经典的估计方法,广泛应用于估计系统状态的问题。本篇博客将介绍Kalman滤波器的基本原理,并通过一个简单的Python代码示例,演示如何使用Kalman滤波器来估计移动物体的位置。 通过运行代码,我们将得到一个包含两个子图的图像,分别展示了估…

数学知识(三)

一、容斥原理 #include<iostream> #include<algorithm>using namespace std;const int N 20;typedef long long LL; int n,m; int p[N];int main() {cin>>n>>m;for(int i 0;i < m;i ) cin>>p[i];int res 0;//从1枚举到2^m(位运算)for(int …

SpringBoot+vue 大文件分片下载

学习链接 SpringBootvue文件上传&下载&预览&大文件分片上传&文件上传进度 VueSpringBoot实现文件的分片下载 video标签学习 & xgplayer视频播放器分段播放mp4&#xff08;Range请求交互过程可以参考这个里面的截图&#xff09; 代码 FileController …

HTML|计算机网络相关

1.三次握手 第一次握手&#xff1a;客户端首先向服务端发送请求。 第二次握手&#xff1a;服务端在接收到客户端发送的请求之后&#xff0c;需要告诉客户端已收到请求。 第三次握手&#xff1a;客户端在接收到服务端发送的请求和确认信息之后&#xff0c;同样需要告诉服务端已…

python并发编程(多线程、多进程、多协程)

文章截图来源来源B站&#xff1a;蚂蚁学python 引入并发&#xff0c;就是为了提升程序运行速度 1、基础介绍 1-1 CPU密集型计算、IO密集型计算 1-2 多进程、多线程、多协程对比 2、全局解释器锁GIL 2-1 python速度慢的两大原因 2-2 GIL是什么 2-3 为什么有GIL这个东西 2-4 怎样…

Vue [Day3]

Vue生命周期 生命周期四个阶段 生命周期函数&#xff08;钩子函数&#xff09; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale…

企业服务器数据库中了devos勒索病毒怎么办如何解决预防勒索病毒攻击

随着科学技术的不断发展&#xff0c;计算机可以帮助我们完成很多重要的工作&#xff0c;但是随之而来的网络威胁也不断提升。近期&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器数据库遭到了devos勒索病毒攻击&#xff0c;导致系统内部的许多重要数据被加密无法正…

1310. 数三角形

题目链接&#xff1a;https://www.acwing.com/problem/content/1312/ 首先不考虑三点共线的情况一共有 种&#xff0c;现在来计算三点共线的情况 1.三点在一条直线上 2.三点在一条竖线上 3.三点在一条斜线上&#xff0c;正反斜线对称&#xff0c;仅需考虑一边的情况 如果…

考研数学Note1—划分框架

calculus 微积分教会我为什么椭圆的面积 π \pi πab. 隐函数求导Rule 如何理解Lagrange求函数极值&#xff1f; 万物可积&#xff08;所有的函数都能找到原函数?&#xff09;——数即宇宙 线性代数 It’s doubtless that Gitmind&Blog is best place for taking note…

Django Rest_Framework(二)

文章目录 1. http请求响应1.1. 请求与响应1.1.1 Request1.1.1.1 常用属性1&#xff09;.data2&#xff09;.query_params3&#xff09;request._request 基本使用 1.1.2 Response1.1.2.1 构造方式1.1.2.2 response对象的属性1&#xff09;.data2&#xff09;.status_code3&…

“三个高度”写作提纲30例

1.充分把握“三个高度” 全面推进全过程人民民主的基层实践 从坚定政治信仰的高度坚持正确方向 从坚定制度自信的高度把握完整链条 从确保落地见效的高度强化组织保障 2. “三个高度”扎实推进安全生产工作 一是着眼大局&#xff0c;高度负责。 二是立足长远&#xff0c;高…

macOS下Django环境搭建

1. macOS升级pip /Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip 2. 卸载Python3.9.5版本 $ sudo rm -rf /usr/local/bin/python3 $ sudo rm -rf /usr/local/bin/pip3 $ sudo rm -rf /Library/Frameworks/Python.framework 3. 安装P…

servlet接受参数和乱码问题

servlet接受参数和乱码问题 1、乱码问题 1&#xff09;get请求 传输参数出现中文乱码问题&#xff1a; 如果还存在问题&#xff1a; 2&#xff09;post请求 传输参数出现中文乱码问题&#xff1a; 2、接受参数&#xff1a; 3、登录注册案例

【瑞吉外卖项目复写】基本部分复写笔记

Day1 瑞吉外卖项目概述 mysql的数据源配置 spring:datasource:druid:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/regie?serverTimezoneAsia/Shanghai&useUnicodetrue&characterEncodingutf-8&zeroDateTimeBehaviorconvertTo…

智慧工地云平台源码,基于微服务+Java+Spring Cloud +UniApp +MySql开发

智慧工地可视化系统利用物联网、人工智能、云计算、大数据、移动互联网等新一代信息技术&#xff0c;通过工地中台、三维建模服务、视频AI分析服务等技术支撑&#xff0c;实现智慧工地高精度动态仿真&#xff0c;趋势分析、预测、模拟&#xff0c;建设智能化、标准化的智慧工地…

MySQL数据库面试题:如何定位慢查询?

MySQL数据库面试题&#xff1a;如何定位慢查询&#xff1f; 面试官&#xff1a;MySQL中&#xff0c;如何定位慢查询&#xff1f; 候选人&#xff1a;嗯~&#xff0c;我们当时做压测的时候有的接口非常的慢&#xff0c;接口的响应时间超过了2秒以上&#xff0c;因为我们当时的系…

【关于反馈电路的放电问题】2022-1-16

缘由关于反馈电路的放电问题 - 电源技术论坛 - 电子技术论坛 - 广受欢迎的专业电子论坛!图中的副绕组反馈给三极管基极&#xff0c;一般都是说通过三极管充电正反馈三极管导通&#xff0c;放电时负反馈三极管截止&#xff0c;负反馈时&#xff0c;电容C3是通过哪个回路放电的呢…

基于Open3D的点云处理15-特征点

Intrinsic shape signatures (ISS) 参考 ISS关键点: 基本原理是避免在沿主要方向表现出类似分布的点上检测关键点&#xff0c;在这些点上无法建立可重复的规范参考框架&#xff0c;因此后续描述阶段很难变得有效。在剩余点中&#xff0c;显着性由最小特征值的大小决定,以便仅包…

2685. 统计完全连通分量的数量;2718. 查询后矩阵的和;1600. 王位继承顺序

2685. 统计完全连通分量的数量 核心思想&#xff1a;枚举所有的连通分量&#xff0c;然后判断这些连通分量是不是完全连通分量&#xff0c;完全连通分量满足边数2e 点数v(v-1)。 2718. 查询后矩阵的和 核心思想&#xff1a;后面的改变更重要&#xff0c;所以我们直接逆向思维…