采用顺序存储结构的数据表称为顺序表。顺序表适合作静态查找。
设有n个数据元素的顺序表,从表的一端(前端或后端)开始,用给定的值依次和表中各数据元素的关键字进行比较,若在表中找到某个数据元素的关键字和给定值相等,则查找成功,给出该数据元素在表中的位置;若查遍整个表,不存在关键字等于给定值的数据元素,则查找失败,给出失败信息。
template<class ElemType> int SqSearch(ElemType elem[],int n,ElemType key)
//一般顺序查找比较
{
int i;
for(i=0; i<n && elem[i]!=key ;i++);
if(i<n)
return i;
else
return -1;
}
template<class ElemType> int SqSearch(ElemType elem[],int n)
//使用哨兵elem[0],n的传入的是带上哨兵的长度
{
int i;
for(i=n;elem[i]!=elem[0];i--);
if(i==0)
return -1;
else
return i;
}
当然,数据表也可以用链表存储如果顺序表中各个数据元素按关键字从小到大或从大到小有序排列,即为有序表,对有序表进行顺序查找,其查找失败的平均查找长度可减少,因为不需要查遍整个表就能确定表中不存在要找的数据元素。
对有序表通常可用折半查找的方法进行查找。设有n个数据元素按其关键字从小到大的顺序存放在一个顺序表中(开始时,查找区间的下限low-0,上限high=n-1)。
1)如果查找区间长度小于1(low>high)则表示查找失败,返回-1;否则继续以下步骤。
2)求出查找区间中间位置的数据元素下标mid(mid=(low+high)/2)。
3)用区间中间位置的数据元素的关键字elem[mid]与给定值key进行比较,比较的结果有以下三种可能。
在折半查找过程中,每比较一次,如果数据元素的关键字和给定值不相等,则查找区间缩小一半。直到查找区间已缩小到只有一个数据元素,如果仍未找到想要找的数据元素,则表示查找失败。
例如,设有序表为{ 8,11,23 34,39,46,68,71,86},下图展示查找关键字为23的数据元素时的查找过程。
//递归算法
template<class ElemType> int BinSearch(ElemType elem[],int low,int high,ElemType key)
{
int mid;
if(low>high)
mid=-1;//查找失败
else
{
mid=(low+high)/2;
if(key<elem[mid])//左半边继续查找
mid=BinSearch(elem,low,mid-1,key);
else if(key>elem[mid])//右半边继续查找
mid=BinSearch(elem,mid+1,high,key);
//两个条件都不满足或者已经调用过,mid就是最后的结果
}
return mid;
}
//非递归算法
template<class ElemType> int BinSearch(ElemType elem[],int n,ElemType key)
{
int low=0,high=n-1;//设置查找到的左右边界
int mid;
while(low<=high)
{
mid=(low+high)/2;
if(key==elem[mid])
return mid;
else if(key<elem[mid])
high=mid-1;
else
low=mid+1;
}
return -1;
}
//二叉搜索树的创建
//递归算法,确定一个根节点,比根节点小的值放左边,比根节点大的值放右边。
#include
using namespace std;
typedef struct TreeNode
{
char data;
struct TreeNode* lchild;
struct TreeNode* rchild;
};
//创建
template<class T> //中序遍历是递增序列(检测时候使用)
TreeNode* SortBinaryTree(TreeNode* root, const T& val)
{
if (root == NULL)
{
root = new TreeNode(val); //根节点为空,插入,返回。
return root;
}
else
{
if (val <= root->data) //小于等于根节点,递归插入左子树
{
root->lchild = SortBinaryTree(root->lchild, val);
}
else //大于根节点,递归插入右子树
{
root->rchild = SortBinaryTree(root->rchild, val);
}
}
return root;
}
//查找操作
template<class T>
TreeNode* find(TreeNode* root, const T&key)
{
TreeNode* cur = root;
while (cur != NULL&&cur->data != key) //当查找元素不为空且与根节点不同
{
if (key < cur->data) //小于根节点左子树找
{
cur = cur->lchild;
}
else //大于根节点右子树找
{
cur = cur->rchild;
}
}
if (cur == NULL) //若cur=NULL则说明遍历完没有找到,
return NULL;
return cur;
}
//插入节点
//类似查找,只需要查找到左子树或者右子树为空的位置插入
template<class T>
void Insert(TreeNode* root, const T& val)
{
if (root == NULL) //根节点为空,直接插入,返回。
{
root = new TreeNode(val);
return;
}
TreeNode* cur = root;
TreeNode* parent = NULL;
bool isLeftchild = true; //设置flag判断插入的位置在左子树还是右子树中
while (cur != NULL) //遍历查找空位置
{
parent = cur;
if (val < cur->data) //插入值小于根节点
{
cur = cur->lchild; //在左子树中
isLeftchild = true;
}
else //插入值大于根节点
{
cur = cur->rchild; //在右子树中
isLeftchild = false;
}
}
TreeNode* new_node = new TreeNode(val);//创建新节点
if (isLeftchild) //根据标志位判断插入位置是否在左子树
{
parent->lchild=new_node; //插入当前节点的左孩子
}
else
{
parent->rchild = new_node; //插入当前节点的右孩子
}
}
进行插入的关键字与给定值的比较次数正好等于该结点所在的层次号。因此,折半查找成功时所进行的关键字与给定值的比较次数最多为这棵二叉找树的高度。
描述查找过程的二叉树叫判定树。
查找不成功是有扩充的判定树,可以得到查找不成功结点的范围。
折半查找法在查找过程中进行的比较次数最多不超过其判定树的深度。
以有序表表示静态查找表时,除可用折半查找之外,还有斐波那契查找和插值查找。
查找算法 | 静态树表(次优查找树)详细分析
#include
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define UNDERFLOW -3
typedef int Status;
typedef int KeyType;//定义关键字类型
typedef int WeightType;//定义关键字类型
typedef struct{
KeyType key;
WeightType key;
}ElemType;//定义元素类型
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode;
typedef struct
{
int length;//树的结点个数
BiTNode * root;
}BiTree;
//次优查找树采用二叉链表的存储结构
typedef BiTree SOSTree;
Status CreateSOSTree(SOSTree &T,SSTable ST){
//由有序表ST构造一棵次优查找树。ST的数据元素含有权域weight
if(ST.length==0) T=NULL;
else{
FindSW(sw,ST);//按照由有序表ST中各数据元素含有的weight域求累计权值表SW
SecondOptimal(T,ST.elem,sw,1,ST.length);
}
return OK;
}
//定义变量
int i;
int min;
int dw;
//创建次优查找树,R数组为查找表,sw数组为存储的各关键字的概率(权值),low和high表示的sw数组中的权值的范围
void SecondOptimal(BiTree T, ElemType R[], float sw[], int low, int high){
//由有序表R[low...high]及其累计权值表sw(其中sw[0]==0)递归构造次优查找树
i = low;
min = abs(sw[high] - sw[low]);
dw = sw[high] + sw[low - 1];
//选择最小的△Pi值
for (int j = low+1; j <=high; j++){
if (abs(dw-sw[j]-sw[j-1])<min){
i = j;
min = abs(dw - sw[j] - sw[j - 1]);
}
}
T = (BiTree)malloc(sizeof(BiTNode));
T->data = R[i];//生成结点(第一次生成根)
if (i == low) T->lchild = NULL;//左子树空
else SecondOptimal(T->lchild, R, sw, low, i - 1);//构造左子树
if (i == high) T->rchild = NULL;//右子树空
else SecondOptimal(T->rchild, R, sw, i + 1, high);//构造右子树
}
补充:演示之后存在的问题是:由于在构造次优查找树的过程中,没有考察单个关键字的相应权值,则有可能出现被选为根的关键字的权值比它相邻的关键字的权值小。此时应作适当调整:选取邻近的权值较大的关键字作次优查找树的根节点。——可以采用左旋子树或右旋子树(类似于平衡二叉树的旋转)。参考:次优查找树的建立
长度n | 顺序查找 | 二分查找 |
---|---|---|
查找方式 | 监视哨(0),从后往前匹配 | 记录查找区间的上下限,mid=(low+high)/2与key比较 |
查找成功(等概率p=1/n) | 查找第i个元素,比较(n-i+1)次;ASLss=(n+1)/2 | 第i层结点有2i-1个,查找第i层结点要比较i次;ASLss=log 2 (n+1) -1~O(log 2 n) |
查找不成功(等概率p=1/(2n)) | 3/4*(n+1) | ASL=log 2 (n+1) -1 ~ O(log 2 n) |
优点 | 算法简单、适应面广,对表的结构或关键字是否有序无任何要求。 | 当n很大时,能够显现时间效率。 |
缺点 | 查找效率低,特别是当n较大时,查找效率较低,不宜采用。 | 由于折半查找的表仍是线性表,若经常要进行插入、删除操作,则元素排列费时太多,因此折半查找比较适合于一经建立就很少改动而又需要经常查找的线性表。较少查找而又经常需要改动的线性表可以采用链接存储,使用顺序查找。 |