查找表:同一数据类型的数据元素构成的集合。
主关键字:可以唯一标识一条记录的关键字称为主关键字。
次关键字:可以识别多个数据元素(或记录)的关键字。
1、顺序查找(线性查找):
/*一般顺序查找,a为数组,n为要查找的数组个数,key为要查找的关键字*/
int Sequential_search(int *a,int key)
{
int i;
for(i=1;i<=n;i++)
{
if(a[i]=key)
return i;
}
return 0;
}
· 优化过的一般顺序查找
/*优化的顺序查找(有哨兵的顺序查找)*/
int Sequential_search2(int *a,int n,int key)
{
int i;
a[0]=key;//设置a[0]为关键字,“哨兵”
i=n; //循环从尾部开始
while(a[i]!=key)
i--;
return i; //返回0则说明查找失败
}//这样就可以避免判断i是否越界,是否小于等于n的判断,节省了时间
2、有序表查找
· 折半查找int Binary_search(int*a,int n,int key)
{
int low,high,mid;
low=1;//定义最低下标为记录首位
high=n;//定义最高下标为记录末位
while(low<=high)
{
mid=(low+high)/2;//折半
if(key<a[mid])//若查找值比中值小
high=mid-1;//最高下标调整到中值下标小一位
else if(key>a[mid])
low=mid+1;
else
return mid;
}
return 0;
· 斐波那契查找(黄金分割查找)
int Fibonacci_Search(int*a,int n,int key)
{
int low,high,mid,i,k;
low=1;
high=n;
k=0;
while(n>F[k]-1)//计算n位于斐波那契数列的位置
k++;
for(i=n,i<F[k]-1;i++)//将不满的数值补全
a[i]=a[n];
while(low<=high)
{
mid=low+F[k-1]-1;//计算当前分隔的下标
if(key<a[mid])
{
high=mid-1;//最高下标调整
k=k-1;
}
else if(key>a[mid])//若查找记录大于当前分隔记录
{
low=mid+1;//最低下标调整到分隔下标mid+1处
k=k-2;//斐波那契数列下标减两位
}
else
{
if(mid<=n)
return mid;//若相等则说明mid即为查找到的位置
else
return n;//若mid>n说明是补全数值,返回n
}
}
return 0;
}
海量数据的查找---线性索引查找
· 稠密索引:在线性索引中,将数据集中每个记录对应一个索引项(优点:计算量少,时间快。缺点:内存小的计算机性能下降)
· 分块索引:数据分成若干块,每块对应不同索引项(虽然不如折半查找,但大大增加了整体查找速度,普遍用于数据库)
· 倒排索引:记录关键字记录号。
3、二叉排序树
/*二叉排序树*/
//二叉链表节点结构定义
typedef struct BiTNode //节点结构
{
int data; //节点数据
struct BiTNode *lchild,*rchild;//左右孩子指针
}BiTNode,*BiTree;
/*递归查找二叉树T中是否存在key,指针f指向T的双亲
其初始调用值为NULL,若查找成功,则指针p指向该数据元素节点,并返回TRUE
否则指针p指向查找路径上访问的最后一个节点并返回FALSE*/
//二叉排序树查找操作
Status SearchBST(BiTree T,int key,BiTree f,BiTree *p)
{
if(!T) //查找不成功
{
*p=f;
return FALSE;
}
else if(key==T->data)//查找成功
{
*p=T;
return TRUE;
}
else if(key<T->data)
return SearchBST(T->lchild,key,T,p);//在左子树继续查找
else
return SearchBST(T->rchild,key,T,p);//在右子树继续查找
}
//二叉排序树插入操作
Status InsertBST(BiTree *T,int key)
{
BiTree p,s;
if(!SearchBST(*T,key,NULL,&p))//查找不成功
{
s=(BiTree)malloc(sizeof(BiTNode));
s->data=key;
s->lchild=s->rchild=NULL;
if(!p)
*T=s; //插入s为新的根节点
else if(key<p->data)
p->lchild=s;//插入s为左孩子
else
p->rchild=s;//插入s为右孩子
return TRUE;
}
else
return FALSE; //树中已有关键字相同节点,不再插入
}
//二叉排序树删除操作
//若二叉排序树T中存在关键字=key的数据元素时,则删除该数据元素站点并返回TRUE,否则返回FALSE
Status DeleteBST(BiTree *T,int key)
{
if(!*T)//不存在关键字=key的数据元素
return FALSE;
else
{
if(key==(*T)->data)//找到关键字=key的数据元素
return Delete(T);
else if(key<(*T)->data)
return DeleteBST(&(*T)->lchild,key);
else
return DeleteBST(&(*T)->rchild,key);
}
}
//从二叉排序树中删除节点p,并重接它的左or右生成树
Status Delete(BiTree *p)
{
BiTree q,s;
if((*p)->rchild==NULL)//右子树空则只需重接它的左子树
{
q=*p;*p=(*p)->lchild;free(q);
}
else if((*p)->lchild=NULL)//只需重接右子树
{
q=*p;*p=(*p)->rchild;free(q);
}
else//左右子树均不空
{
q=*p;s=(*p)->lchild;
while(s->rchild)//转左,然后向右到尽头(找待删除节点前缀)
{
q=s;s=s->rchild;
}
(*p)->data=s->data;//s指向被删除节点的直接前驱
if(q!=*p)
q->rchild=s->lchild;//重接q右子树
else
q->lchild=s->lchild;//重接q左子树
free(s);
}
return TRUE;
}
· 平衡二叉树
/*平衡二叉树*/
//二叉树二叉链表节点结构定义
typedef struct BiTNode
{
int data;//节点数据
int bf;//节点的平衡因子
struct BiTNode *lchild,*rchild;//左右孩子指针
}BiTNode,*BiTree;
//右旋操作
//对以p为根的二叉排序树做右旋处理,
//处理之后p指向新的树根节点,即旋转处理之前的左子树的根节点
void R_Rotate(BiTree*p)
{
BiTree L;
L=(*p)->lchild;//L指向P的左子树根节点
(*p)->lchild=L->rchild;//L的右子树挂接为P的左子树
L->rchild=(*p);
*p=L; //P指向新的根节点
}
//左旋处理
void L-Rotate(BiTree *p)
{
BiTree R;
R=(*p)->rchild;//R指向P的右子树根节点
(*p)->rchild=R->lchild;//R的左子树挂接为P的右子树
R->lchild=(*P);
*P=R; //P指向新的根节点
}
//左旋平衡旋转处理函数代码
const LH +1;//左高
const EH 0;//等高
const RH -1;//右高
//对以指针T所指节点为根的二叉树作为左平衡旋转处理
//算法结束,指针T指向新的根节点
void LeftBalance(BiTree *T)
{
BiTree L,Lr;
L=(*T)->lchild;//L指向T的左子树根节点
switch(L->bf)
{//检查T的左子树平衡度,并做相应处理
case LH://新节点插入在T的左孩子的左子树上,要做单右旋处理
(*T)->bf=L->bf=EH;
R_Rotate(T);break;
case RH://新节点插入在T的左孩子右子树上,做双旋处理
Lr=L->rchild;//Lr指向T的左孩子的右子树根
switch(Lr->bf)//修改T及其左孩子平衡因子
{
case LH:(*T)->bf=RH;
L->bf=EH;break;
case EH:(*T)->bf=L->bf=EH;break;
case RH:(*T)->bf=EH;
L-bf=LH;break;
}
Lr->bf=EH;
L_Rotate(&(*T)->Lchild);//对T的左子树做左旋平衡处理
R_Rotate(T);//对T做右旋平衡处理
}
}
//插入并形成平衡二叉树
/*若在平衡二叉排序树T中不存在和e有相同关键字的节点,则插入一个数据元素
为e的新节点并返回1,否则返回0.若因插入而使二叉排序树失去平衡,做平衡旋转
处理,bool变量taller反应T长高与否*/
Status InsertAVL(BiTree *T,int e,Status *taller)
{
if(!*T)//当前T为空,则申请曾在新节点
{//插入新节点,树长高,置taller为TRUE
*T=(BiTree)malloc(sizeof(BiTNode));
(*T)->data=e;
(*T)->lchild=(*T)->rchild=NULL;
(*T)->bf=EH;
*taller=TRUE;
}
else
{
if(e==(*T)->data)
{//树中已存在和e有相同关键字的节点则不再插入
*taller=FALSE;
return FALSE;
}
if(e<(*T)->data)
{
if(!InsertAVL(&(*T)->lchild,e,taller))//未插入
return FALSE;
if(taller)//已插入到T的左子树并左子树“长高”
{
switch((*T)->bf)//检查T的平衡度
{
case LH://左高
LeftBalance(T);
*taller=FALSE;break;
case EH://原本左右子树等高,现左子树增高树增高
(*T)-bf=LH;
*taller=TRUE;break;
case RH://原本右子树比左子树高,现左右子树等高
(*T)->bf=EH;
*taller=FALSE;break;
}
}
}
}
else
{//应继续在T的右子树中进行搜索
if(!InsertAVL(&(*T)->rchild,e,taller))//未插入
return FALSE;
}
}
· 多路查找树(B树)
为了避免因为数据量大导致树的度非常庞大,打破一个节点只能存储一个元素的限制,为此引入多路查找树。
特殊的B树如 ① ②
① 2-3树
其中每一个节点都有两个孩子(2节点,一个2节点包含一个元素和两个孩子(或没有孩子))
或三个孩子(3节点,一个三节点包含一小一大两个元素和三个孩子(或没有孩子))。
② 2-3-4树
一个4节点包含小中大三个元素四个孩子(或没有孩子)。
③ B树
节点最大的孩子数称为B树的阶(order),2-3树是3阶B树,2-3-4树是4阶B树。
④ B+树
B树的优化树,好像没看大懂。。。
4、散列表查找(哈希表)
散裂技术是在记录和存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。
/*散列表查找算法*/
#define SUCCESS 1
#define UNSUCCESS 0
#define HASHSIZE 12 //定义散列表长为数组长度
#define NULLKEY -32768
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=(*add+1)%m;//开放定址法的线性探测
if(H.elem[*addr]==NULLKEY||*addr==Hash(key))
{//如果循环回到原点
return UNSUCCESS;//则说明关键字不存在
}
}
return SUCCESS;
}