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

本文介绍了如何实现散列算法,包括散列函数的选择、散列表操作(如散列查找),并详细探讨了线性探测和二次探测两种冲突解决方法在处理随机数序列插入时的效果。实验通过生成随机数并分析冲突和聚集情况,展示了不同策略对散列性能的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验任务

(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;
    }
}

实验结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小丶象

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

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

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

打赏作者

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

抵扣说明:

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

余额充值