使用线性查找结构来组织数据,这样可以使用顺序查找折半查找等高效查找
考虑二叉排序树和散列表结构
//顺序查找,a为数组,n为要查找的数组个数,key为要查找的关键字
int Seq_search(int a[], int n, int key)
{
int i;
for (i = 0; i < n;i++)
{
if (a[i] == key)
return i;
}
return 0;
}
int Seq_search(int a[], int n, int key)
{
int i = n;
a[0] = key; //设置哨兵
while(a[i] != key){ //如果不是要找的元素
i--; //从后往前找
}
return i; //返回0表示没有找到,返回其它数字代表找到
}
有序表查找要求,数据是有序的,是排序好的,我们只需要进行查找
int Binary_Search(int a[], int n, int key)
{
int low, high, mid;
low = 0;
high = n - 1;
//[low,high]
while (low<=high)
{
mid = (low + high) / 2; //二分查找
if (a[mid] < key)
low = mid + 1; //[mid+1,high]
else if (a[mid]>key)
high = mid - 1; //[low,mid-1]
else
return mid;
}
return -1;
}
对于折半查找,为什么一定要,而不是1/4或者其他?
比如:我们查字典Apple,我们会先从中间查找,还是有一定目的的向前找。
对折半查找到的优化,将查找点的选择改为自适应选择
对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。
其余部分不变,只对mid值进行优化
int Insert_Search(int a[ ], int n, int key)
{
int low, high, mid;
low = 0;
high = n - 1;
while (low <= high)
{
mid = low + (key - a[low]) / (a[high] - a[low])*(high - low); //修改mid值后的插值查找
if (a[mid] < key)
low = mid + 1;
else if (a[mid]>key)
high = mid - 1;
else
return mid;
}
return -1;
}
对二分法进行改造,mid值为黄金分割点
斐波那契数列:后面的数等于前面两个数的和 F i = F i - 1 + F i - 2
如果与给定关键字相同,则查找成功,返回在表中的位置;
如果给定关键字大,向右查找并减小2个斐波那契区间;
如果给定关键字小,向左查找并减小1个斐波那契区间;
重复过程,直到找到关键字(成功)或区间为空集(失败)。
索引是为了加快查找速度而设计的一种数据结构。索引就是把一个关键字与他对应的记录相关联的过程
a: 最大关键码–存储每一块中的最大关键字。
b: 存储每一块中记录的个数以便于循环时使用。
c: 用于指向块首数据元素的指针,便于开始对这一块中记录进行遍历。
由属性来确定记录位置,查找块维护困难
左小右大
因为数据集是有序存放,查找的方法可以使用折半,插值,斐波那契等,但是因为有序,在插入和删除操作上的效率并不高
这时我们就需要一种动态查找方法,既可以高效实现查找,又可以使得插入和删除效率不错,这时我们可以考虑二叉排序树
/*
BiTree T 我们要搜索的二叉树
ElemType key 我们要搜索的关键字
BiTree F 记录下我们的当前搜索子树的双亲结点
BiTree* P 查找成功,指针p指向该数据元素节点,并返回true 、 查找失败,指针p指向查找路径上访问的最后一个节点,并返回Flase
*/
Status SearchBST(BiTree T, ElemType key, BiTree F, BiTree* P)
{
if (!T)
{
*P = F; //若是未找到则返回父节点位置
return FALSE;
}
else
{
if (T->data == key)
{
*P = T; //若是找到则P返回该结点位置
return TRUE;
}
else if (T->data < key) //查找值大于节点值
return SearchBST(T->rchild, key, T, P);
else //查找值小于节点值
return SearchBST(T->lchild, key, T, P); //下一个节点,查找值,双亲节点,查找到的结果
}
}
Status InsertBST(BiTree* T, int key)
{
BiTree P,s;
if (!T)
return ERROR;
if (!SearchBST(*T, key, NULL, &P))
{
//没有查找到有重复数据,获取到了应该插入的位置
s = (BiTree)malloc(sizeof(BiTNode));
s->data = key;
s->lchild = s->rchild = NULL;
if (!P) //空树直接插入
*T = s; //p指针为查找时的最接近的叶子节点
else if (key < P->data) //如果该节点比p节点还小则插入到p节点的左边
P->lchild = s;
else
P->rchild = s; //如果该节点比p节点大则插入到p节点的右边
return OK;
}
else
return ERROR; //树中已经有了相同节点,不再插入
}
二叉排序树通过中序遍历可以变成一个有序的序列
方法:找到待删除节点的直接前驱或者直接后继,用该节点来替换待删除节点,再删除该节点
也就是选择与待删除节点最接近的值,左子树中最大的,或者右子树中最小的
Status Delete(BiTree* T)
{
BiTree q,f; //q为双亲节点,s每一次迭代用到的节点
if (!*T)
return ERROR;
if (!(*T)->lchild) //若是左子树不存在,我们只需要接到右子树
{
q = *T; //将当前节点赋值给p节点,便于删除free
*T = (*T)->rchild; //将当前节点的右孩子赋值给当前节点
free(q); //释放删除节点
}
else if (!(*T)->rchild) //若右子树不存在,接入左子树
{
q = *T; //将当前节点赋值给p节点,便于删除free
*T = (*T)->lchild; //将当前节点的左孩子赋值给当前节点
free(q);
}
else //两边都存在,这里选择右子树最小(右子树中最左边的)作为接入点
{
f = *T; //f作为q的双亲结点 (做了一点点修改)
q = (*T)->rchild;
while (q)
{
f = q;
q = q->lchild; //找到右子树最小,注意其可能存在右子树,我们要进行保存,接入其父节点
}
//将找到的右子树最小节点的数据与待删除的数据进行替换,然后记录该点将其删除
(*T)->data = q->data;
if (f != (*T))
f->lchild = q->rchild;
else
f->rchild = q->rchild; //当右子树是一个右斜树时(寻找时没有找到右子树左端的节点)画图
free(q); //释放已经替换过的节点
}
return TRUE;
}
Status DeleteBST(BiTree* T, int key)
{
if (!*T)
return ERROR;
else
{
if ((*T)->data == key) //找到了,开始删除
{
Delete(T); //删除该结点,由于要分情况讨论,所以另外写一个函数
}
else if ((*T)->data < key)
DeleteBST(&(*T)->rchild, key);
else
DeleteBST(&(*T)->lchild, key);
}
}
构建平衡二叉树对的基本思想:在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。在保存二叉排序树的前提下,调整最小不平衡子树中各个结点,进行相应的旋转,使之成为新的平衡子树。
一个节点的右子树的最左边的孩子可以放在这个节点左子树的最右边的孩子的位置(不满足二叉树性质时稍作调整)
typedef struct _BiTNode
{
ElemType data;
int bf;
struct _BiTNode* lchild, *rchild;
}BiTNode,*BiTree;
代码有空再写
所有元素之间存在某种特定的排序关系
每个节点的孩子树可以多于两个,且每一个节点处可以存储多个元素
1.对于空树插入一个2节点即可
3.向一个父节点为2节点的3节点插入数据:临时创建4节点,并将中间的值移至父节点中,再将剩余节点分裂为2个节点
4.向一个父节点为3节点的3节点插入数据:继续向上寻找2节点,找到后重复3步骤
1.删除元素位于3节点的叶子节点上:直接删除元素即可
2.所删除元素位于2节点上,此节点双亲也是2节点,且拥有一个3节点的右孩子
3.所删除元素位于2节点上,此节点双亲也是2节点,且拥有一个2节点的右孩子
4.当删除节点是满二叉树时
比如说要查找7,首先从外存读取得到根节点3,5,8三个元素,发现7不在,但是5、8之间,因此就通过A2再读取外存的6,7节点找到结束
B+树是常用于数据库和操作系统的文件系统中的一种用于查找的数据结构
1)
在B+树中,具有n个关键字的结点只含有n棵子树,即每个关键字对应一棵子树
在B树中,具有n个关键字的结点含有(n+1)棵子树。
2)
在B+树中,每个结点(非根内部结点)关键字个数n的范围是 ⌈m/2⌉≤n≤m(根结点1≤n≤m)
在B树中,每个结点(非根内部结点)关键字个数n的范围是⌈m/2⌉ -1≤n≤m-1(根结点:1≤n≤m-1)。
3)
在B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶结点中
在B树中,叶结点包含的关键字和其他结点包含的关键字是不重复的。
4)在B+树中,叶结点包含信息,所有非叶结点仅起到索引作用,非叶结点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。
在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。
适合一对一查找,不适合范围行查找
计算简单,散列地址分布均匀
查找成功与查找不成功的平均长度 散列地址、关键字、比较次数参考例子
1.类似于构造散列表,给定一个关键字Key。
2.先根据散列函数计算出其散列地址。然后检查散列地址位置有没有关键字。
如果没有,表明该关键字不存在,返回查找失败。
如果有,则检查该记录是否等于关键字。
如果等于关键字,返回查找成功。
如果不等于,则按照给定的冲突处理办法来计算下一个散列地址,再用该地址去执行上述过程。