查找算法分为静态查找和动态查找两种。静态查找是指没有插入和删除结点操作,只需查找到按给定关键字的结点,而动态查找是指存在查找过程中插入和删除结点的操作。
对于静态查找,一般倾向于将数据组织成顺序表的形式。无序查找的复杂度为O(n)。
有序的序列分两种情况:
当每个结点被查找的概率相等时可以采用折半查找的算法,此时的复杂度为O(n).查找的过程对应于一棵二分查找树。
当各个结点被查找的概率不等时如果要得到最优查找效率(期望查找时间为最小),需要构造一棵最优二叉查找树。
无序查找由于时间复杂度为O(n),复杂度较高,当数据量比较大时效率会很低,可以建立一个索引表,将记录按照一定区间划分,区间之间的大小是有序的,区间内是无序的,这样可以有效提高查找效率。这样的组织结构称为索引顺序结构。
动态查找时由于有插入和删除的操作,数据一般组织为链式的结构。动态查找的主要组织结构有二叉排序树,平衡二叉树,B树和B+树。
二叉排序树是这样的一棵二叉树:
它或者为空树,或者为一棵满足这样要求的二叉树:
1: 根节点的左子树的所有节点的值都小于根节点,右子树中所有节点的值都大于根节点
2:左子树和右子树本身也是一棵二叉排序树
二叉排序树的平均查找时间复杂度为O(logn)。但是由于二叉排序树的构造过程中可能存在每个分支都只有一棵子树的情况,导致其实际的树深度接近n,因此二叉排序树如果构造不恰当,最坏查找复杂度可能达到O(n)。
由此提出了一种平衡二叉树的概念:平衡二叉树本身是一棵足二叉排序树且满足其任何一个节点的左子树和右子树的深度值之差的绝对值最大为1,当插入或者删除二叉平衡树中的一个结点导致失去平衡性结构时,会通过旋转调整算法重新调整其为一棵二叉平衡树。
B-树(又称B树)的定义如下:
B-树是一棵平衡的多路查找树。
m阶的B-树上每个非终端结点可能含有n个关键字Ki(1<=i<=n),n<m;
n个指向记录的指针Di
n+1个指向子树的指针Ai(0<=i<=n)
非叶子结点的关键字按照从小到大排列K1<K2<k3<...<Kn,
Ai-1所指向的子树的关键字均小于Ki,Ai所指向的子树中的关键字均大于Ki。
树中所有叶子结点不带任何信息,且在同一层次上。
根节点或者为空树或者至少有两颗子树,其余非叶子结点至少有⌈m/2⌉棵子树,至多有m棵子树.
B-树的描述代码如下:
#define m 3
typedef struct BTNode{
int KeyNum;//结点关键字个数
struct BTNode* parent;
KeyType Key[m+1];//关键字数组,0号单元不用
struct BTNode* ptr[m+1];
Record * RecPtr[m+1];
}BTNode* BTree;
B-树的查找:
从根节点出发沿指针进行搜索和在结点内进行顺序(或者折半)查找,两个过程交替进行.
B-树的插入:
若插入结点后仍然满足B-树的要求,则完成.
否则插入数据可能导致被插入位置所在结点的关键字个数大于m-1,此时需要分裂操作,将当前结点的中间位置关键字插入到父亲结点中,其左子树指向该关键字左边的关键字分裂后形成的子树,右指针指向该关键字右侧形成的子树.若此时父节点由于此分裂导致也不满足B-树的结构,则对父节点做同样分裂操作.
删除操作要求删除B-树中给定的关键字,若删除后导致不满足B-树定义,则需重新调整.
这里有两种情况
1:删除关键字后当前结点关键字个数小于⌈m/2⌉-1,若其兄弟结点的关键字个数大于⌈m/2⌉,则可以通过调整父亲结点的关键字,兄弟结点的关键字和当前结点使得满足平衡结构.
2:若兄弟结点的关键字也小于⌈m/2⌉,则若借用兄弟结点依然不能满足平衡结构,此时应合并当前结点和父亲结点及其兄弟结点.