1、查找
查找表(Search Table)是由同一类型的数据元素构成的集合。
关键字(key)是数据元素中某个数据项的值,又称为键值,用它可以标识一个数据元素。
若此关键字可以唯一地标识一个记录,则称此关键字为主关键字(Primary Key)
查找(Searching)就是根据指定的某个值,在查找表中确定一个其关键字等于给定值得数据元素。
2、顺序表查找
顺序查找(Sequential Search)又叫线性查找,时间复杂度O(n)
有序查找:
折半查找又称为二分查找,时间复杂度O(logn),需要有序表顺序存储才可以
插值查找是根据要查找的关键字key与查找表中最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式(key-a[low])/(a[high]-a[low])时间复杂度O(logn)
斐波那契查找(Fibonacci Search),利用了黄金分割原理来实现的。
二叉树排序它或者是一棵空树,或者是具有下列性质的二叉树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有的结点的值均大于它的根结点的值。
-
它的左、右子树也分别为二叉排序树
比如:{35,37,47,51,58,62,73,88,93,99}排序如下
平衡二叉树(AVL树)是一种二叉排序树,其中每一个结点的左子树和右子树的高度差至多等于1。
我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF(Balance Factor),那么平衡二叉树上所有结点的平衡因子只可能是-1、0和1。
3、散列表查找(哈希表)
概念
散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。查找时,根据这个确定的对应关系找到给定值key得映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。
我们把这种对应关系f称为散列函数,又称为哈希(Hash)函数。
采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表。
散列技术即是一种存储方法,也是一种查找方法。
散列函数的构造方法
计算简单、 散列地址分布均匀
最常用方法:除留余数法 随机数法
对于散列表长为m的散列函数公式为:
f(key) = key mod p (p<=m)
散列表查找实现
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
#define SUCCESS 1
#define UNSUCCESS 0
#define HASHSIZE 12 /* 定义散列表长为数组的长度 */
#define NULLKEY -32768
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef struct
{
int *elem; /* 数据元素存储基址,动态分配数组 */
int count; /* 当前数据元素个数 */
}HashTable;
int m=0; /* 散列表表长,全局变量 */
/* 初始化散列表 */
Status InitHashTable(HashTable *H)
{
int i;
m=HASHSIZE;
H->count=m;
H->elem=(int *)malloc(m*sizeof(int));
for(i=0;i<m;i++)
H->elem[i]=NULLKEY;
return OK;
}
/* 散列函数 */
int Hash(int key)
{
return key % m; /* 除留余数法 */
}
/* 插入关键字进散列表 */
void InsertHash(HashTable *H,int key)
{
int addr = Hash(key); /* 求散列地址 */
while (H->elem[addr] != NULLKEY) /* 如果不为空,则冲突 */
{
addr = (addr+1) % m; /* 开放定址法的线性探测 */
}
H->elem[addr] = key; /* 直到有空位后插入关键字 */
}
/* 散列表查找关键字 */
Status SearchHash(HashTable H,int key,int *addr)
{
*addr = Hash(key); /* 求散列地址 */
while(H.elem[*addr] != key) /* 如果不为空,则冲突 */
{
*addr = (*addr+1) % m; /* 开放定址法的线性探测 */
if (H.elem[*addr] == NULLKEY || *addr == Hash(key)) /* 如果循环回到原点 */
return UNSUCCESS; /* 则说明关键字不存在 */
}
return SUCCESS;
}
int main()
{
int arr[HASHSIZE]={12,67,56,16,25,37,22,29,15,47,48,34};
int i,p,key,result;
HashTable H;
key=39;
InitHashTable(&H);
for(i=0;i<m;i++)
InsertHash(&H,arr[i]);
result=SearchHash(H,key,&p);
if (result)
printf("查找 %d 的地址为:%d \n",key,p);
else
printf("查找 %d 失败。\n",key);
for(i=0;i<m;i++)
{
key=arr[i];
SearchHash(H,key,&p);
printf("查找 %d 的地址为:%d \n",key,p);
}
return 0;
}
散列表查找性能分析
如果没有冲突查找效率最高,时间复杂度为O(1),在实际应用中,冲突不可避免。散列表的平均查找长度取决于哪些因素?
1.散列函数是否均匀
2.处理冲突的方法
3.散列表的装填因子
装填因子α = 填入表中的记录个数/散列表长度。α越大,产生冲突的可能性就越大,散列表的平均查找长度取决于装填因子,而不是取决于查找集合中的记录个数。
通常都是将散列表的空间设置得比查找集合大,虽然浪费了一定空间,但是换来查找效率的大大提升。