本次继续更新数据结构与算法这门课的上机实验,并且从本次开始将持续更新三篇关于排序和查找的内容——最根本的操作,主要涉及折半查找和哈希表查找。
特此感谢, 实验过程中邓老师的指导和帮助!
对于想要获取此实验报告和源代码的同学,欢迎光顾小生寒舍 GitHub:https://github.com/ChromeWei?tab=repositories
实验内容
一、编写函数实现折半查找。
二、实现一个最简单的哈希表的插入和查找。
备注:鉴于方便大家对折半查找的理解,我查找了很多相关CSDN平台上也没有写得较为细致的博文,所以我打算将我之前学习时用的平台推荐给大家GeeksforGeeks;对于哈希表的插入和查找我推荐一个博客的讲解,连接在下面。(小生先谢过作者,如涉及侵权,亲联系我即删 🙏)
折半查找(二分法)
- 折半查找https://blog.csdn.net/weixin_44321600/article/details/86671672.
- Binary Search https://www.geeksforgeeks.org/binary-search/.
- [C语言] 选择排序之直接选择排序的特性及实现https://www.cnblogs.com/usingnamespace-caoliu/p/9428115.html.
哈希表的插入和查找
本算法中采用的构造哈希表的方法有:
- 构造哈希函数的方法:【除留余数法】
H(key) = key MOD p (p ≤ m),其中m为表长,本算法中取p=m;(一般情况下,可选p为质数或不包含小于20的质因数的合数) - 处理冲突的方法:【开放定址法】
Hi = (H(key) + di) MOD m, i=1,2,3,…,k(k <= m - 1),其中di为增量序列,可有下列三种取法:
(1) 线性探索再散列:di = 1,2,3,…,m-1
(2) 二次探索再散列:di = 1,-12,2,-22,…,±k^2(k <= m - 1)
(3) 伪随机探索再散列:di = 伪随机数序列
引用自:哈希表查找C实现(详细注释)http://blog.csdn.net/shengwusuoxi/article/details/8148460.
本篇重点: 但是最为推荐的是GeeksforGeeks官网链接这个平台,不止有较为详尽的算法介绍,图文都会比较直观,掌握数据结构和算法等知识足够!!
链接:https://www.geeksforgeeks.org/sorting-algorithms/.
附件: 源代码
(测试环境:Win10, VisualC++ 6.0)
折半查找(二分法)
#include <stdio.h>
// A recursive binary search function. It returns location of x in
// given array arr[l..r] is present, otherwise -1
int binarySearch(int arr[], int l, int r, int x)
{
if (r >= l)
{
int mid = l + (r - l)/2;
// If the element is present at the middle itself
if (arr[mid] == x) return mid;
// If element is smaller than mid, then it can only be present
// in left subarray
if (arr[mid] > x) return binarySearch(arr, l, mid-1, x);
// Else the element can only be present in right subarray
return binarySearch(arr, mid+1, r, x);
}
// We reach here when element is not present in array
return -1;
}
int main(void)
{
int arr[] = {2, 3, 4, 10, 40};
int n = sizeof(arr)/ sizeof(arr[0]);
//int x = 10;
int x;
printf("Please input a element will be found:");
scanf("%d",&x);
int result = binarySearch(arr, 0, n-1, x);
(result == -1)? printf("%d is not present in array.", x)
: printf("%d is present at index %d.", x, result+1);
return 0;
}
哈希表查找
#include <stdio.h>//NULL等
#include <stdlib.h>//exit()、 rand()
#include <malloc.h>//malloc
#include <math.h>//OVERFLOW、pow()
#define NULL_KEY 0//0为无记录标志
#define N 15//数组可容纳的记录个数
static int m;//内部链接静态变量,m为哈希表表长
typedef int KeyType;//关键字类型
typedef struct ElemType
{//数据元素(记录)类型
KeyType key;
int order;
}ElemType;
//对两个数值型关键字的比较约定为如下的宏定义。
#define EQ(a,b) ((a) == (b))
#define LT(a,b) ((a) < (b))
#define LE(a,b) ((a) <= (b))
void Visit(int p,ElemType r)
{
printf("address = %d(%d,%d)\n",p,r.key,r.order);
}
static int hashsize[] = {11,19,29,37};//内部链接静态变量,哈希表容量(表长m)递增表,一个合适的素数序列。
typedef struct HashTable
{//哈希表的存储结构
ElemType *elem;//记录存储基址变量,动态分配数组
int count;//当前记录个数
int sizeindex;//hashsize[sizeindex]为当前表长
}HashTable;
//宏定义
typedef int Status; // Status是函数的类型,其值是函数结果状态代码,如下边三个宏定义
#define SUCCESS 1 //查找成功
#define UNSUCCESS 0//查找失败
#define DUPLICATE -1//记录重复
void InitHashTable(HashTable &H)
{//初始化哈希表,构造一个记录为空的哈希表
int i;
H.sizeindex = 0;//初始化表长数组索引为0
m = hashsize[H.sizeindex];//初始化表长为hashsize[0]
H.count = 0;//初始化记录数为0
H.elem = (ElemType *)malloc(m * sizeof(ElemType));//动态分配记录数组
if (!H.elem)
{//分配失败
exit(OVERFLOW);
}
for (i = 0; i < m; ++i)
{//初始化记录数组关键字为空
H.elem[i].key = NULL_KEY;//未填记录的标志
}
}
void DestroyHashTable(HashTable &H)
{//销毁哈希表
free(H.elem);//释放动态记录数组
H.elem = NULL;//指针置为空
H.count = 0;//记录数置为为0
H.sizeindex = 0;//表长索引项置为0
}
unsigned Hash(KeyType k)
{//返回哈希函数求得的哈希地址(用除留余数法构造的一个简单的哈希函数)
return k % m;// H(key) = key MOD p (p ≤ m),取p=m
}
void collision(KeyType &k,int &p,int i)
{//用开放定址法处理冲突,其中p为处理后所得的哈希地址,i为冲突次数。
p = (Hash(k) + i) % m;
}
Status SearchHash(HashTable H,KeyType k,int &p,int &c)
{//在开放地址哈希表中查找关键字为K的记录,若查找成功,以p指示记录在表中的位置,并返回SUCCESS;
//否则以p指示插入位置,并返回UNSUCCESS。c以计冲突次数,其初值为0,供建表插入时参考
p = Hash(k);//用哈希函数计算哈希地址
while (H.elem[p].key != NULL_KEY && !EQ(H.elem[p].key,k))
{//该位置中填有记录,并且与待查记录不相等
c++;
if (c < m)
{//处理冲突次数未超出m - 1,则继续处理冲突
collision(k,p,c);
}
else
{//超出最大处理次数,H中位找到记录
break;
}
}
if (EQ(H.elem[p].key,k))
{//查找成功
return SUCCESS;
}
else
{//查找失败
return UNSUCCESS;
}
}
void RecreateHashTable(HashTable &);//对函数RecreateHashTable()的声明
Status InsertHash(HashTable &H,ElemType e)
{
int p,c = 0;//冲突次数初始为0
if (SearchHash(H,e.key,p,c))
{//查找成功
return DUPLICATE;//H中已有与e有相同关键字的记录,不插入
}
else if (c < hashsize[H.sizeindex]/2)
{//未找到,冲突次数c也未达到上限(c的阀值可调,但最大不超过hashsize[H.sizeindex] - 1)
H.elem[p] = e;//在H中插入数据元素e
++H.count;
return SUCCESS;//插入成功
}
else
{//未找到,但冲突次数c已达到上限
RecreateHashTable(H);//重建哈希表
return UNSUCCESS;//插入不成功
}
}
void RecreateHashTable(HashTable &H)
{
int i,count = H.count;//H中原有记录个数
ElemType *p,*elem = (ElemType *)malloc(count *sizeof(ElemType));//动态生成存放哈希表H原有数据的空间
p =elem;
for (i = 0; i < m; ++i)
{//将原有的所有记录,保存到elem中
if (!EQ(H.elem[i].key,NULL_KEY))
{//H在该单元有记录
*p++ = H.elem[i];//将记录依次存入elem
}
}
H.count = 0;//将原有记录数置为0,为下面调用InserHash做准备
H.sizeindex++;//表长数组索引加1
m = hashsize[H.sizeindex];//新的存储容量(表长)
H.elem = (ElemType *)realloc(H.elem,m*sizeof(ElemType));//以新的存储容量重新生成空哈希表H
for (i = 0; i < m; ++i)
{//初始化新的哈希表
H.elem[i].key = NULL_KEY;//未填记录
}
for (p = elem; p < elem + count; ++p)
{//将原表中的记录重新存储到新的哈希表中
InsertHash(H,*p);
}
free(elem);//释放elem的存储空间
}
void TraverseHash(HashTable H,void (*Visit)(int,ElemType))
{//按哈希地址的顺序遍历哈希表H
int i;
printf("哈希地址 0 ~ %d\n",m - 1);
for (i = 0; i < m; ++i)
{//对于整个哈希表H
if (!EQ(H.elem[i].key,NULL_KEY))
{//H在第i个单元有记录
Visit(i,H.elem[i]);//访问第i个数据
}
}
}
int main(void)
{
ElemType r[N];//记录数组
HashTable h;
int i,n = 0,p = 0;
Status s;
KeyType k;
FILE *f;//文件指针类型
f = fopen("Records.txt","r");//打开记录文件Record.txt
do
{
i = fscanf(f,"%d%d",&r[p].key,&r[p].order);//先将记录暂存入记录数组r[p]
if (i != -1)
{//输入数据成功
p++;
}
} while (!feof(f) && p < N);//未到达数据文件结尾且记录数组未满
fclose(f);//关闭数据文件
InitHashTable(h);
for (i = 0; i < p - 1; ++i)
{//在h中插入前p-1个记录(最后一个记录不插入,插入最后一个记录会重建哈希表)
s = InsertHash(h,r[i]);
if (DUPLICATE == s)
{
printf("表中已有关键字为%d的记录,无法再插入记录(%d,%d)\n",r[i].key,r[i].key,r[i].order);
}
}
printf("按哈希地址的顺序遍历哈希表:\n");
TraverseHash(h,Visit);
printf("请输入待查找记录的关键字:");
scanf("%d",&k);
s = SearchHash(h,k,p,n);
if (SUCCESS == s)
{//查找成功
Visit(p,h.elem[p]);//输出该记录
}
else
{//查找失败
printf("未找到\n");
}
s = InsertHash(h,r[i]);//插入最后一个记录(需重建哈希表)
if (UNSUCCESS == s)
{//插入不成功
s = InsertHash(h,r[i]);//重建哈希表后重新插入
}
printf("按哈希表地址的顺序遍历重建后的哈希表:\n");
TraverseHash(h,Visit);
printf("请输入待查找记录的关键字:");
scanf("%d",&k);
s = SearchHash(h,k,p,n);
if (SUCCESS == s)
{//查找成功
Visit(p,h.elem[p]);//输出该记录
}
else
{//查找失败
printf("未找到\n");
}
DestroyHashTable(h);
return 0;
}
Records.txt
12 23 43 23 32 5 31 56 78 34 8 45 63 55 30
下一篇:数据结构与算法上机实验专栏
欢迎各位订阅我,谢谢大家的点赞和专注!我会继续给大家分享我大学期间详细的实践项目。
知识星球
专为求职面试中算法与数据结构的小伙伴,创了学习交流/刷题群(知识星球)!想要最快的提升算法与数据结构技能,和更多小伙伴一起来吧!
进群获取互联网大厂高频coding题库,告别刷题400道,分类整理的题库,算法思路和源代码实现配套,各个类型总结出来的解题模板,远比你一个人要强!
MaiweiAI-com | WeChat ID: Yida_Zhang2
机器学习+计算机视觉