查找
根据给定的某个值,在查找表中确定一个其关键字等于给定值的记录或数据元素。
1 线性表的查找
//顺序存储结构
typedef struct{
ElemType *elem; //数据元素存储空间基址,建表时按实际长度分配,0号单元留空
int length; //表长度
}SSTable;
顺序查找:
从表中最后一个记录开始,逐个进行记录的关键字和给定值的比较,若某个记录的关键字和给定值比较相等,则查找成功,找到查找记录;反之,若直至第一个记录,其关键字和给定值比较都不等,则表明表中没有所查记录,查找不成功。
int
Search_Seq( SSTable ST, keyType key ){
//在顺序表ST中顺序查找其关键字等于key的数据元素。若找到,则函数值为
//该元素在表中的位置,否则为0
ST.elem[ 0 ].key = key; //哨兵
for( i = ST.length; ST.elem[ i ].key != key; --i );//从后往前找
return i; //找不到时,i为0
}
二分查找:
先确定待查记录所在的范围,然后逐步缩小范围直到找到或找不到该记录为止。
int
Search_Bin( SSTable ST, keyType key ){
low = 1;
high = ST.length; //置区间初值
while( low <= high ){
mid = ( low + high ) / 2;
if( ST.elem[ mid ].key == key )
return mid; //找到待查元素
else if( key < ST.elem[ mid ].key ) //缩小查找区间
high = mid - 1; //继续在前半区间进行查找
else
low = mid + 1; //继续在后半区间进行查找
}
return 0 //顺序表中不存在待查元素
}
索引顺序表查找:
索引表按关键字有序,则表或者有序或者分块有序。所谓“分块有序”指的是第二个子表中所有记录的关键字均大于第一个子表中最大的关键字,第三个子表中的所有关键字均大于第二个子表中的最大关键字,…,依次类推。
2 二叉排序树
二叉排序树或者是一颗空树;或者具有以下性质的二叉树:
(1) 若它的左子树不为空,则左子树上的所有结点的值均小于它的根结点的值;
(2) 若它的右子树不空,则右子树上所有的结点的值均大于它的根结点的值;
(2) 它的左、右子树也分别为二叉排序树。
//二叉链表的二叉排序树的存储结构
typedef struct{
KeyType key; //关键字项
InfoType otherinfo; //其他数据域
}ElemType;
typedef struct BSTNode{
ElemType data; //数据域
struct BSTNode *lchild,*rchild; //左右孩子
}BSTNode,*BSTree;
BSTree
SearchBST( BSTree T, KeyType key,BSTree f, BSTree &p ){
//在根指针T所指二叉排序树中递归的查找其关键字等于key的数据元素
//若查找成功,则指针p指向该数据元素结点,并返回TRUE,否则指针p指向查
//找路径上访问的最后一个结点并返回FALSE,指针f指向T的双亲,其初始调用值为NULL
if( !T ){
p = f;
return FALSE;
}
else if ( key == T->data.key )
p = T;
return TRUE;
else if( key < T->data.key )
return SearchBST( T->lchild, key, T, p ); //在左子树继续查找
else
return SearchBST( T->rchild, key, T, p ); //在右子树继续查找
}
Status
InsertBST( BSTree &T, ElemType e ){
//当二叉排序树T中不存在关键字等于e.key的数据元素时,插入e并返回TRUE
//否则返回FALSE
if( !SearchBST( T, e.key, NULL, p )){ //查找不成功
s = ( BSTree )malloc( sizeof( BSTNode ));
s->data = e;
s->lchild = s->rchild = NULL;
if( !p )
T = s; //被插结点s为新的根结点
else if( e.key < p->data.key )
p->lchild = s; //被插结点s为左孩子
else
p->rchild = s; //被插结点s为右孩子
return OK;
}else
return FALSE;
}
Status
DeleteBST( BSTree &T, KeyType key ){
//若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素结点
//并返回TRUE,否则返回FALSE
if( !T )
return FALSE;
else{
if( key == T->data.key )
return Deldet( T );
else if( key < T->data.key )
return DeleteBST( T->lchild, key );
else
return DeleteBST( T->rchild, key );
}
}
Status
Delete( BSTree &p ){
//从二叉排序树中删除结点p,并重接它的左子树或右子树
if( !p->rchild ){ //右子树空则只需要重连它的左子树
q = p;
p = p->lchild;
free( q );
}
else if( !p->lchild ){ //左子树空则只需要重连它的右子树
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的右子树
q->rchild = s->lchild;
else
q->lchild = s->lchild; //重接q的左子树
delete s;
}
return TRUE;
}
3 平衡二叉树
平衡二叉树又称AVL树。它或者是一颗空树,或者是具有下列性质的二叉树: 它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1.若将二叉树上的结点的平衡因子BF定义为该结点是左子树的深度减去它的右子树的深度,则平衡二叉树上所有结点是平衡因子只可能是-1,0和1。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。
4 哈希表(散列表)
在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和结构中一个唯一的存储位置相对应。我们称这个对应关系f为哈希函数,按这个思想建立的表为哈希表。
对不同关键字可能得到同一哈希地址,这种现象称冲突,具有相同函数值的关键字对该哈希函数来说称做同义词。
(1)哈希函数的构造方法
直接地址法:
取关键字或关键字的某个线性函数值为哈希地址。即
H(key)=key 或 H(key)=a * key + b
除留余数法:
取关键字被某个不大于哈希表长m的质数p除后所得余数为哈希地址。即
H(key) = key MOD p , p<=m
(2)处理冲突的方法
开放定址法:
Hi = ( H(key) + di ) MOD m
di为增量序列,可以下列三种取法:
1.di = 1,2,3,…,m-1,称线性探测再散列
2.di = 12,-12,22,-22,32,…,称做二次探测再散列
3.di=伪随机数序列,称伪随机探测再散列
链地址法:
将所有关键字为同义词的记录在同一线性表中。
(3)哈希表的查找
int hashsize[] = { 997,...}; //哈希表容量递增表,一个合适的素数序列
typedef struct{
ElemType *elem; //数据元素存储基址,动态分配数组
int count; //当前数据元素个数
int sizeindex; //hashsize[sizeindex]为当前容量
}HashTable;
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
Status
SearchHash( HashTable H, KeyType key, int &p, int &c ){
//在开放定址哈希表H中查找关键码为K的元素,若查找成功,以p指示待查数据
//元素在表中的位置,并返回SUCCESS;否则,以p指示插入位置,并返回UNSUCCESS
//c用以计冲突次数,其初始值置零,供建表插入时参考
p = Hash( k ); //求得哈希地址
while( H.elem[ p ].key != NULLkEY && k != H.elem[ p ].key ) //该位置中填有记录且关键字不相等时
collision( p, ++c ); //求得下一探查地址p
if( k == H.elem[ p ].key )
return SUCCESS; //查找成功,p返回待查数据元素位置
else
return UNSUCCESS; //查找不成功,p返回的是插入位置
}