1. 検索アルゴリズム
1. 二分探索: (前提条件: 順序付けされたシーケンスである必要があります)
#include <stdio.h>
//二分查找 value代表的是被查找的值
int findByHalf(int *p, int n, int value)
{
int low = 0;//low低
int high = n-1;//high高
int middle;//用来保存中间位置的下标
while(low <= high)//注意此处循环结束的条件,需要加上 =
{
//不断获取中间位置的下标
middle = (low + high) / 2;
if(value < p[middle])//说明在前半段,移动high
{
high = middle-1;
}
else if(value > p[middle])//说明在后半段,移动low
{
low = middle + 1;
}
else//对应p[middle] == value 情况
{
return middle;
}
}
return -1;//代表没有找到
}
int main(int argc, const char *argv[])
{
int a[] = {12,34,56,77,89,342,567,7898};
int i;
for(i = 0; i < sizeof(a)/sizeof(a[0]); i++)//把数组中的每个元素都找一遍,进行测试程序
{
printf("%d post is %d\n",a[i],findByHalf(a,sizeof(a)/sizeof(a[0]),a[i]));
}
//查找10000返回 -1
printf("%d post is %d\n",10000,findByHalf(a,sizeof(a)/sizeof(a[0]),10000));
return 0;
}
2. ブロック検索:(ブロック間で順序付け、ブロック内で無秩序)
インデックステーブル + ソースデータテーブル
アイデア:
(1) まずインデックステーブルのどのブロックを決定するか
(2) 次に、このブロックを横断して検索します。
//インデックステーブル
typedef struct
{ int max; //ブロック内の最大値 int post;//ブロックの開始位置の添字、ポスト配列の添字}index_t; //インデックス//ソースデータテーブルint a[19] = {18, 10, 9, 8, 16, 20, 38, 42, 19, 50, 84, 72, 56, 55, 76, 100, 90, 88, 108}; 0 5 10 15 //インデックス テーブルindex_t b [4] = { {18,0}、{50,5}、{84,10}、{108,15}};
#include <stdio.h>
//索引表
typedef struct
{
int max; //块中最大值
int post;//块的起始位置下标,post数组下标
}index_t; //索引
//a原数据表 index_list 索引表 value 被查找的值
int findByBlock(int *a, index_t *index_list,int value)
{
//start和end作为源数据表的下标取搜索
int start;//保存起始下标
int end;//终止下标的后一个位置
//1.思想,先确定value在哪一块中,遍历索引表与每块中的最大值进行比较
int i;
for(i = 0; i < 4; i++)
{
if(value <= index_list[i].max)//说明value有可能在i块中
{
//确定当前i块中的起始下标和终止
start = index_list[i].post;
//假设value在最后一块中,i+1数组越界,所以end的赋值,需要进行条件判断
if(i == 3)//说明在最后一个块
{
end = 19;
}
else
{
end = index_list[i+1].post;
}
break;//注意此处一定要有break
}
}
//确定块的起点和终点后,对源数据表进行遍历
for(i = start; i < end; i++)
{
if(a[i] == value)
return i;
}
return -1;
}
int main(int argc, const char *argv[])
{
int i;
//源数据表
int a[19] = {18, 10, 9, 8, 16, 20, 38, 42, 19, 50, 84, 72, 56, 55, 76, 100, 90, 88, 108};
// 0 4 5 10 15
//索引表,结构体数组
index_t index_list[4] = {
{18,0},{50,5},{84,10},{108,15}};
for(i = 0; i < 19; i++)//把源数据表中的每一个数据查询一遍,测试程序
{
printf("%d post is %d\n",a[i],findByBlock(a,index_list,a[i]));
}
printf("%d post is %d\n",22,findByBlock(a,index_list,22));
return 0;
}
3. ハッシュテーブル(ハッシュハッシュ関数)
ハッシュストレージ
データ内のキーワードと対応する保存場所の関係を保存するテーブルがあります
キーを選択する際は、データ内で重複しないキーワードをキーとして選択してください。
保存する場合は対応関係に従って保存してください
対応関係に従って取る
####演習 1####
//セーブデータの年齢+年齢に相当する人口
10 100
20 300年齢と人口を入力し、対応関係に従ってハッシュテーブルに保存し、
照会する年齢を入力して、その年齢の人口を出力します
3.1 ダイレクトアドレス方式
#include <stdio.h>
//哈希函数,代表了数据中的关键字与存储位置之间的关系
//调用哈希函数能够得到数据的存储位置
int hashFun(int key)
{
int post = key-1;//key-1代表关系 post通过关系得到存储位置
return post;
}
//存储数据到哈希表,存的时候按照对应的关系存
void saveAgeNum(int *hash_list,int key,int num)
{
//1.通过key得到数据存储的位置,调用哈希函数
int post = hashFun(key);
//2.将数据存储到哈希表中
hash_list[post] = num;
}
//从哈希表中取数据,取的时候按照对应的关系取
int getAgeNum(int *hash_list,int key)
{
//1.通过key得到数据存储的位置,调用哈希函数
int post = hashFun(key);
//2.将数据取出
return hash_list[post];
}
int main(int argc, const char *argv[])
{
int i;
int age,num;//年龄和对应年龄的人口数
int hash_list[200] = { 0 };//哈希表,用来保存年龄和对应年龄的人口数,之所以长度为200,暂定人的寿命为200岁
for(i = 0; i < 4; i++)
{
printf("请您输入年龄和年龄对应的人口数:\n");//输入四组数据保存到哈希表中
scanf("%d %d",&age,&num);
//将输入的数据保存到哈希表中
saveAgeNum(hash_list,age,num);
}
//进行查找对应年龄的人口数
for(i = 0; i < 6; i++)
{
printf("请您输入要查询的年龄:\n");
scanf("%d",&age);
printf("%d: %d人\n",age,getAgeNum(hash_list,age));
}
return 0;
}
3.2 予約剰余方式
以下のコードデモを参照してください
データ数はnです
n = 11
ハッシュ テーブルの長さ m = n/a //n にはデータの数が格納されます。 a は充填係数で、0.7 ~ 0.8 の間が最も合理的です。
m = 11 / 0.75 == 15 0 ~ 15 の最大の指数は 13 です。
//素数はテーブル長以下の素数です
// 剰余メソッドを保持する
int hashFun(int key)
{
int post = key % prime; //key % 13
return post;
}
3.3 オープンアドレスメソッド//ハッシュストレージ中に生成された競合を解決する
競合が発生した場合は、ハッシュ関数を使用して配列の添字を取得するのではなく、配列内の空きを見つけてデータを埋めます。
3.4 競合を解決する方法
(1) 線形探査法
#include <stdio.h>
//哈希函数
int hashFun(int key)
{
int post = key % 13;//取余13的原因是因为 选不大于哈希表长的最大质数
return post;
}
//哈希查找
int hashSearch(int *hash_list,int key)
{
int d = 1;//d 取值 1 2 3 4 5 当冲突发生的时候采用一次线性探查法
int post;//用来保存存储位置
int remnum;//用来保存余数
post = remnum = hashFun(key);
while(d < 15 && hash_list[post] != 0 && hash_list[post] != key)
{
//采用一次线性探查法
post = (remnum + d) % 15;
d++;
}
if(d >= 15)
return -1;//代表表已经溢出
return post;
//hash_list[post] == 0//意味着当前post这个位置可以存放数据,初始化哈希表所有位置全为0,代表没有存放数据
//hash_list[post] == key //意味着当前表中的key已经存在
}
//向哈希表中存储数据
void hashSave(int *hash_list, int key)
{
//先通过key获取存储位置,对key存储位置进行判断
int post = hashSearch(hash_list,key);
if(post == -1 || hash_list[post] == key)
{
printf("表溢出或key已经存在!!!\n");
return;
}
//将数据存储到哈希表中
hash_list[post] = key;
}
int main(int argc, const char *argv[])
{
int i;
int a[11] = {23,34,14,38,46,16,68,15,7,31,26};
int hash_list[15] = { 0 };//哈希表长度为15 因为 数据长度n / 装填因子a 11 / 0.75,装填因子通常采用0.7-0.8之间最为合理
//将数据全部保存到哈希表中
for(i = 0; i < 11; i++)
{
hashSave(hash_list,a[i]);
}
//打印哈希表
for(i = 0; i < 15; i++)
{
printf("%d ",hash_list[i]);
}
printf("\n");
return 0;
}
(2) チェーンアドレス方式
#include <stdio.h>
#include <stdlib.h>
typedef struct node_t
{
int key;
struct node_t *next;
}link_node_t,*link_list_t;
//哈希函数
int hashFun(int key)
{
int post = key % 13;
return post;
}
link_list_t hashSearch(link_list_t *hash_list,int key)
{
link_list_t h = NULL;//用来保存无头链表的头指针
//1.先通过key调用哈希函数获取位置
int post = hashFun(key);//需要将判断key放入第条链表
h = hash_list[post];//h指向key对应存储位置的那条链表
while(h != NULL && h->key != key)//相当于遍历无头链表,同时检查key
{
h = h->next;
}
return h;
//h == NULL 说明没找到key可以进行存储
//h != NULL h->key == key 说明key已经存在
}
//存储数据
void hashSave(link_list_t *hash_list,int key)
{
int post = hashFun(key);
link_list_t pnew = NULL;//用来保存新创建的节点
link_list_t p = hashSearch(hash_list,key);
if(p == NULL)//key不存在,可以进行插入数据
{
//1.创建一个新的节点用来保存key
pnew = (link_list_t)malloc(sizeof(link_node_t));
if(NULL == pnew)
{
perror("pnew malloc failed");
return;
}
//2.将key保存到新节点中
pnew->key = key;
pnew->next = NULL;
//3.将新的节点插入到对应存储位置的链表中,将新节点每次插入无头链表头的位置
pnew->next = hash_list[post];
hash_list[post] = pnew;
}
else
{
printf("key已经存在了!!!\n");
}
}
int main(int argc, const char *argv[])
{
int i;
link_list_t h = NULL;//临时保存每条链表的头
int a[11] = {23,34,14,38,46,16,68,15,7,31,26};
//hash_list是一个结构体指针数组,每一个元素都是结构体指针
link_list_t hash_list[13] = { 0 };//为什么长度是13,因为保留余数法每次%13 得到的位置在0-12之间
//将所有的key保存起来
for(i = 0; i < 11; i++)
{
hashSave(hash_list,a[i]);
}
//遍历哈希表 有13条链表
for(i = 0; i < 13; i++)//hash_list中保存的是13条无头链表的头指针
{
printf("%d:",i);
h = hash_list[i];
while(h != NULL)//相当于遍历无头链表
{
printf("%d ",h->key);
h = h->next;
}
printf("\n");
}
return 0;
}