查找
基本概念
查找 :在数据集合中寻找满足某种条件的数据元素的过程。
查找结果分为 查找成功 和 查找失败。
查找表 :用于查找的数据集合, 由同一种数据类型(或记录)的组成, 可以是一个数组或链表等数据类型。
操作:
1、检索满足条件的某个特定的数据元素的各种属性;
2、查询某个特定的数据元素是否在查找表中;
3、在查找表中插入一个数据元素;
4、从查找表中删除一个数据元素。
操作1和2合起来称作 静态查找表 , 全部操作合起来称作 动态查找表。
关键字: 数据元素中唯一标识该元素的某个数据项的值, 使用基于关键字的查找, 查找结果应该是唯一的。
平均查找长度 : 查找时,关键字比较次数的平均值:
一般用于衡量一个查找算法的查找效率。
查找之顺序查找
定义
顺序查找 :又称线性查找,主要用于在线性表中进行查找。
无序线性表的顺序查找
对无序线性表进行顺序查找,查找失败时要 遍历整个线性表。
参考代码实现
typedef struct{
ElemType *elem;
int TableLen;
}SSTable; //查找表
int search_Seg(SStable ST,ElemType key){
ST.elem[0] = key; //哨兵
for(int i = ST.Tablelen; ST.elem[i] != key; i--);
return i;
}
哨兵 作用: 查找失败返回哨兵下标即0,查找成功返回查找到的元素下标。如果没有哨兵,则需要加一个if判断,是否查找到指定元素。使用哨兵可以精简代码量。
有序线性表的顺序查找
对关键字有序线性表进行顺序查找,查找失败时 不一定要遍历整个线性表。
判定树
查找之折半查找
定义
折半查找:又称二分查找,仅适用于 有序的顺序表。
算法思想
1、首先将给定值key与表中中间位置元素的关键字比较,
2、若相等,则返回该元素的位置;
3、若不等,则在前半部分或者是后半部分进行查找。
4、重复该过程,直到找到查找的元素为止;或查找失败。
查找序列升序时,
若key小于中间元素,则查找前半部分;
若key大于中间元素,则查找后半部分。
参考代码实现
int Binary_Search(SeqList L, ElemType key){
int low=0, high=L.TableLen-1, mid;
while(low <= high){
mid = (low + high)/2;
if(L.elem[mid] == key)
return mid;
else if(L.elem[mid] > key)
high = mid - 1;
else
Low = mid + 1;
}
return -1;
}
判定树
若该判定树是 满二叉树 时,
查找之分块查找
定义
分块查找: 又称索引顺序查找,它吸取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找。
如何分块
1、将查找表分为若干子块。块内的元素可以无序,但块间是有序的,即对于所有块有第i块的最大关键字小于第i+1块的所有记录的关键字。
2、建立索引表,索引表中的每个元素含有各块的最大关键字和各块中的第一个元素的地址,索引表按关键字有序排列。
如何查找
1、在索引表中确定待查记录所在的块,可以 顺序查找或折半查找 索引表。
2、在块内进行 顺序查找。
分块查找的平均查找长度为 索引查找(LI)和块内查找(LS) 之和。
设长度为n的查找表均匀分为b块,每块有s个记录
若块内和块间均采用 顺序查找
若块内采用顺序查找,块间均采用 折半查找
查找之二叉排序树
定义
二叉排序树 BST,也称 二又查找树 。
二叉排序树或者为空树,或者为非空树,当为非空树时有如下特点:
1、若左子树非空,则左子树上所有结点关键字值 均小于 根结点的关键字。
2、若右子树排空,则右子树上所有结点关键字值 均大于 根结点的关键字。
3、左、右子树本身也分别是一棵二叉排序树。
基本操作
查找
二叉树非空时,查找根结点,若相等则查找成功;
若不等,则当小于根结点值时,查找左子树;当大于根结点的值时,查找右子树。
当查找到叶节点仍没查找到相应的值,则查找失败。
查找代码实现
BSTNode *BST_Search(BiTree T, Elemrype key, BSTNode * &p){
//传入三个参数 二叉排序树, 查找值, 记录当前结点的双亲系结点指针
p = NULL;
while(T != NULL && key != T->data){
p = T;
if(key < T->data)
T = T->lchild;
else
T = T->rchild;
}
return T; //返回查找结点的指针
}
插入
若二叉排序树为空, 则直接插入结点;
若二叉排序树非空, 当值小于根结点时,插入左子树; 当值大于根结点时, 插入右子树;
当值等于根结点时不进行插入。
插入代码实现
int BST_Insert(BiTree &T, KeyType k){
if(T == NULL){ //为空时
T = (BiTree)malloc(sizeof(BSTNode));
T->key = k;
T->lchild = T->rchlid = NULL;
return 1;
}
else if(k = T->key) //等于时
return 0;
else if(k < T->key) //非空小于
return BST_Insert(T->lchild, k);
else //非空大于
return BST_Insert(T->rchild, k);
}
创建二叉排序树
读入一个元素并建立结点, 若二叉树为空将其作为根结点;
若二叉排序树非空, 当值小于根结点时,插入左子树; 当值大于根结点时,插入右子树;
当值等于根结点时不进行插入。
创建代码实现
void Create_BST(BiTree &T, KeyType str[], int n){
T = NULL;
int i =0;
while(i < n){
BST_Insert(T, str[i]);
i++;
}
}
删除
若被删除结点z是叶结点, 则直接删除。
若被删除结点z只有一棵子树,则让z的子树成为z父结点的子树, 代替z结点。
若被删除结点z有两棵子树,则让z的中序序列直接后继代替z,并删去直接后继结点。
删除代码实现
BSTreeNode* DeleteNode(BSTNode* BT,KeyType data) {
BSTNode* p;
if (BT == NULL)
return NULL;
else if (data < BT->data)
BT->lchild = DeleteNode(BT->lchild, data);
else if (data > BT->data)
BT->rchild = DeleteNode(BT->rchild, data);
else {
if (BT->lchild && BT->rchild) {
p = FindMin(BT->rchild);
BT->data = p->data;
BT->rchild = DeleteNode(BT->rchild, p->data);
}
else {
p = BT;
if (BT->lchild == NULL)
BT = BT->rchild;
else if (BT->rchild == NULL)
BT = BT->lchild;
free(p);
}
}
return BT;
}
查找之二叉平衡树
定义
AVL, 任意结点的平衡因子的 绝对值不超过一。平衡二叉树是一种二叉排序树
平衡因子: 左子树高度 - 右子树高度
二叉平衡树的判断
利用递归的后序遍历过程:
判断左子树是一棵平衡二叉树
判断右子树是一棵平衡二叉树
判断以该结点为根的二叉树为平衡二叉树
判断条件:
若左子树和右子树均为平衡二叉树且左子树与右子树高度差的绝对值小于等于1, 则平衡。
判断代码的实现
void Judge_AVL(BiTree bt, int &balance, int &h){
int bl = 0, br = 0, hl = 0, hr = 0;
if(bt == NULL){
h = 0;
balance = 1;
}
else if(bt->lchild == NULL && bt->rchild == NULL){
h = 1;
balance = 1;
}
else{
Judge_AVL(bt->lchild, bl, hl);
Judge_AVL(bt->rchild, br, hr);
if(hl > hr)
h = hl + 1;
else
h = hr + 1;
if(abs(h1 - hr) < 2 && bl == 1 && br == 1)
balance = 1;
else
balance = 0;
}
}
插入
先插入后调整,每次调整 最小不平衡子树。
LL平衡旋转(右单旋转)
原因: 在结点A的左孩子的左子树上插入了新结点。
调整方法: 右旋操作 : 将A的左孩子B代替A,将A结点称为B的右子树根结点,而B的原右子树则作为A的左子树。
RR平衡旋转(左单旋转)
原因: 在结点A的右孩子的右子树上插入了新结点
调整方法: 左旋操作 : 将A的右孩子B代替A, 将A结点称为B的左子树 根结点, 而B的原左子树则作为A的右子树。
LR平衡旋转(先左后右双旋转)
原因: 在结点A的左孩子的右子树上插入了新结点
调整方法: 先左旋后右旋操作 :将A的左孩子B的右孩子结点C代替B,然后再将C结点向上代替A的位置。
RL平衡旋转(先右后左旋转)
原因: 在结点A的右孩子的左子树上插入了新结点。
调整方法: 先右旋后左旋操作 :将A的右孩子B的左孩子结点C代替B, 然后再将C结点向上代替A的位置。