数据结构与算法8-查找
参考《大话数据结构》
静态查找
1. 顺序表查找
从头到尾逐个遍历
int Sequential(int *a, int n, int key)
{
a[0] = key; //设置哨兵
int i=n;
while(a[i] != key)
{
i--;
}
return i; //若返回 0,则说明查找失败
}
2.有序表查找
这类算法要求有序查找表
2.1 折半查找算法
int Binary_search(int *a, int n, int key)
{
int high = n;
int low = 1; //这里定义从1位置开始查找
int mid = 0;
while(high >= low)
{
mid = (high + low)/2;
if(a[mid] > key)
low = mid+1;
else if(a[mid < key])
high = mid - 1;
else
return mid;
}
return 0;
}
2.2 插值查找算法
对折半查找的改进,主要的改进点在分割点上:
int Insert_search(int *a, int n, int key)
{
int high = n;
int low = 1; //这里定义从1位置开始查找
int mid = 0;
while(high >= low)
{
mid = low + (high-low)*(key-a[low])/(a[high]-a[low]); //仅这里改进一处
if(a[mid] > key)
low = mid+1;
else if(a[mid < key])
high = mid - 1;
else
return mid;
}
return 0;
}
2.3 斐波那契查找算法
这个算法是使用黄金分割点来“折半查找”,其实也是分割点的不同。
菲波那切数列在这里仅仅是用作分割点,而我们查找的查找表可以是满足有序的任意表。
//假设我们已经定义了一个斐波那切数列 *F,如上图
int Fibonacci_Search(int *a, int n, int key)
{
int low,high,mid,i,k;
int high = n;
int low = 1;
k = 0;
while(n > F[k]-1) //计算n位于菲波那切数列的位置
k++; //因为想要用菲波那切数列做分割点,所以需要和查找表长度n比较
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 = low+1;
k = k-2; //控制数据范围
}
else
{
if(mid<=n)
return mid;
else
return n; //若mid>n说明是不全数值,返回0
}
}
}
3.线性索引查找
3.1 稠密索引
3.2 分块索引
原则:块间有序,快内无序
3.3 倒排索引
动态查找
4.二叉排序树
4.1 二叉排序树的定义:
实际上二叉排序树不一定是上图所示的满二叉树。
4.2 二叉排序树查找
//构造二叉排序树的数据结构
typedef struct BiTNode
{
int data;
struct BiTNode *lrchild, *rchaild;
}BiTNode, *BiTree;
//算法主体
//指针 f 指向 T 的双亲,初始值为Null
//若查找成功,则指针p指向该数据元素结点
//否则指针p指向查找路径上访问的最后一个结点
int BiTree_Search(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); //此时T (f)为T->lchild (T)的双亲
else
return SearchBST(T->rchild, key, T, p);
}
4.2 二叉排序树插入
//当T中不存在关键字k时,插入key并返回True,否则返回FALSE
int 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) //p 是遍历了整棵树的指针,若树空则为 p 为空;若树非空,则 p 指向最后一个访问的结点,不为空;
(*T)=s; //插入新的根节点
else if (key < p->data) //??若不是插在叶子节点呢?或者插在叶子结点不能保证其仍是二叉排序树呢????
p->lchild = s;
else
p->rchild = s;
return TRUE;
}
else
return FALSE;
}
4.2 二叉排序树删除
int DeleteBST(BiTree *T, int key)
{
if(!*T)
return FALSE;
else
{
if(key == (*T)->data)
return Delete(T);
else if(key<(*T)->data)
return DeleteBST(&(*T)->lchild, key);
else
return DeleteBST(&(*T)->rchild, key);
}
}
int 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) //这是也说明待删除的p有右结点
q->rchild=s->lchild; //重接q的右子树
else
q->lchild=s->lchild; //重接q的左子树
free(s);
}
return TRUE;
}