查找的基本概念
被查找对象是由一组元素(或记录)组成的表或文件,称为查找表。
给定一个值k,在含有n个元素的表中找出关键字等于k的元素,称为查找。
若在查找的同时对表做修改操作(如插入和删除),则相应的查找表称为动态查找表。
若在查找中不涉及表的修改操作,则相应的查找表称为静态查找表。
若在整个查找过程都在内存中进行,则称之为内查找。
若查找过程的需要访问外存,则称之为外查找。
线性表的查找
为了算法通用,用于查找运算的顺序表采用数组表示,该数组元素的类型声明:
typedef int KeyType; //定义关键字类型为int
typedef struct{
KeyType key; //关键字项
InfoType data; //其他数据项,类型为InfoType
} RecType; //查找元素的类型
顺序查找
顺序查找的基本思路是从表的一端向另一端逐个将元素的关键字和给定值k比较,若相等,则查找成功,给出该元素在查找表中的位置;若整个查找表扫描结束后仍未找到关键字等于k的元素,则查找失败。
顺序查找表的算法:(在顺序表R[0……n-1]中查找关键字等于k的元素,成功返回找到的元素的逻辑序号,失败返回0)
int SeqSearch(RecType R[],int n,KeyType k){
int i=0;
while(i<n&&R[i].key!=k) //从表头往后找
i++;
if(i>=n) //未找到返回0
return 0;
else
return i+1; //找到返回逻辑序号i+1
}
在R的末尾增加一个关键字为k的记录,称为哨兵,这样查找过程不再需要判断i是否超界:
int SeqSearch1(RecType R[],int n,KeyType k){
int i=0;
R[0].key=k;
while(R[i].key!=k) //从表头往后找
i++;
if(i==n) //未找到返回0
return 0;
else
return i+1; //找到返回逻辑序号i+1
}
折半查找
折半查找又称二分差找,是一种效率较高的查找方法。折半查找要求线性表是有序表,即表中的元素按关键字有序。
算法:
int BinSearch(RecType R[],int n,KeyType k){ //折半查找算法
int low=0,high=n-1,mid;
while(low<=high){ //当前区间存在元素时循环
mid=(low+high)/2;
if(k==R[mid].key) //查找成功返回其逻辑序号mid+1
return mid+1;
if(k<R[mid].key) //继续在R[low……mid-1]中查找
high=mid-1;
else //k>R[mid].key
low=mid+1; //继续在R[mid+1……high]中查找
}
return 0; //未找到时返回0
}
索引存储结构和分块查找
索引存储结构:
先在索引表中快速查找到相应的关键字,然后通过对应的地址找到主数据表中的元素。
分块查找:
先查找索引表,然后在已确定的快中进行顺序查找。
#define MAXI<索引表的最大长度>
typedef struct{
KeyType key; //KeyType为关键字的类型
int link; //指向对应块的起始下标
} IdxType; //索引表元素的类型
树表的查找
二叉排序树
二叉树排序树又称二叉搜索树,其定义为二叉排序树或者空树,或是满足以下性质的二叉树。
- 若根节点的左子树非空,则左子树上的所有结点关键字均小于根结点关键字。
- 若根结点的右子树非空,则右子树上的所有结点关键字均大于根结点关键字。
- 根结点的左,右子树本身又各是一棵二叉排序树。
typedef struct node{ //元素类型
KeyType key; //关键字项
InfoType data; //其他数据域
struct node *lchild,*rchild; //左,右孩子指针
} BSTNode;
平衡二叉树
一棵二叉树中每个结点的左,右子树的高度最多相差1,则称此二叉树为平衡二叉树。在算法中,通过平衡因子来具体实现上述平衡二叉树的定义。
若一棵二叉树的所有结点都是平衡的,称之为平衡二叉树
B_树
在B_树的存储结构中,结点的类型声明:
#define MAXM 10 //定义B_树的最大阶数
typedef int KeyType //KeyType为关键字类型
typedef struct node{
int keynum; //结点当前拥有的关键字的个数
KeyType key[MAXM]; //key[1……keynum]存放关键字,key[0]不用
struct node *parent; //双亲结点指针
struct node *ptr[MAXM]; //孩子结点指针数组ptr[0……keynum]
} BTNode; //B_树结点类型
int m; //m阶B_树,这里的m,Max和Min几个变量均定义为全局
变量
int Max; //m阶B_树中每个结点的最多关键字个数,Max=m-1
int Min; //m阶B_树中每个结点的最少关键字个数,Min=[m/2]-1
B+树
哈希表的查找
哈希表的基本概念
哈希表又称散列表,其基本思路是,设要存储的元素个数为n,设置一个长度为m(m≥n)的连续内存单元,以每个元素的关键字ki(0≤i≤n-1)为自变量,通过一个哈希函数的函数h(ki)把ki映射为内存单元的地址(或下标)h(ki),并把该元素存储在这个内存单元中,h(ki)也称为哈希地址,把如此构造的线性表存储结构称为哈希表。
在构建哈希表时可能存在这样的问题,两个关键字ki和kj(i≠j)有ki≠kj,但会出现h(ki)=h(kj)的情况,把这种情况称为哈希冲突。通常把这种具有不同关键字面具有相同哈希地址的元素称为同义词,这种冲突也称为同义词冲突。
哈希函数的构造方法
- 直接定址法
- 除留余数法
- 数字分析法
哈希冲突的解决方法
- 开放定址法:出现哈希冲突时在哈希表中找一个新的空闲位置存放元素。
- 拉链法:把所有同义词用单链表链接起来的方法。
哈希表的运算算法
开放地址法构造的哈希表的运算算法:
#define NULLKEY -1 //定义空关键字值
#define DELKEY -2 //定义被删关键字值
typedef int KeyType; //关键字类型
typedef struct{
KeyType key; //关键字域
int cout; //探测次数域
} HashTable; //哈希表单元类型
用拉链法构造的哈希表的运算:
typedef int KeyType; //关键字类型
typedef struct node{
KeyType key; //关键字域
struct node *next; //下一个结点指针
} NodeType; //单链表结点类型
typedef struct{
NodeType *firstp; //首结点指针
} HashTable; //哈希表单元类型