目录算法
\[ASL=\sum_{i=1}^n P_iC_i\]数据库
其中P是查找低i个数据元素的几率,通常认为几率相同即1/n。C是找到第i个数据元素所须要的比较次数。数据结构
对表进行之间的扫描app
int Search(int a[], int len, int k){ for(int i=0;i<len-1;i++) if(a[i]==k) return i; return -1; }
\[ ASL_{成功}=\sum P_i(n-i+1)=\frac{n+1}{2} \]函数
\[ ASL_{失败}=n+1 \]性能
int Search(int a[], int len, int k){ int low=0, high=len-1, mid; while(low<=high){ mid=(low+high)/2; if(a[mid]==k) return mid; else if(a[mid]>k) high=mid-1; else low=mid+1; } return -1; }
查找的过程相似于二叉查找树,因此能够引入二叉树来描述,称为断定树。
查找次数不会超过树的深度。spa
\[ ASL= \frac { 1 } { n } \sum _ { i = 1 } ^ { n } l _ { i } = \frac { 1 } { n } \left( 1 \times 1 + 2 \times 2 + \cdots + h \times 2 ^ { h - 1 } \right) = \frac { n + 1 } { n } \log _ { 2 } ( n + 1 ) - 1 \approx \log _ { 2 } ( n + 1 )-1 \]3d
分块查找将查找表分为若干个块,为每一个区块创建一个索引,肯定下标的上下界后使用顺序查找。查找索引使用二分法。
指针
若索引查找和块内查找的ASL为Li和Ls,那么分块查找的ASL:code
\[ ASL=L_i+L_s \]
二叉查找树又称二叉排序树(BST)。是一种特殊性质的二叉树,其中左子树的结点的关键字都小于根结点,右子树大于根结点。
typedef struct BNode{ int key; struct BNode *lchild; struct BNode *rchild; }BNode, *BTree;
BNode* BSTSearch(BTree T, int key){ if(T==NULL) return NULL; if(T->key==key) return T; if(key<T->key) return BSTSearch(T->lchild, key); else return BSTSearch(T->rchild, key); }
int BSTInsert(BTree& T, int key){ if(T==NULL){ T=(BTree)malloc(sizeof(BNode)); T->key=key; T->lchild=T->rchild=NULL; return 1; } else if(key=T->key) return 0; else if(k<T->key) return BSTInsert(T->lchild, key); else return BSTInsert(T->rchild, key); }
void CreateBST(BTree &T, int[] a, int len){ T=NULL; int i=0; while(i<n){ BSTInsert(T, a[i]); i++; } }
删除这里通常不会考代码,知道有那么三种状况便可。
第三种状况可能有点难理解,结合图来理解一下。
二叉搜索的搜索时间取决于树的长度,因而可能会出现一种极端的状况。
二叉平衡树(AVL)是一种删除和插入结点时任意结点的左右子树相差不会超过1的二叉搜索树。这样能够加强搜索的性能。
定义平衡因子:左子树和右子树的高度差。
图中结点的值为其平衡因子:
保证平衡的想法就是:当咱们插入或者删除结点的时候,先检查插入路径上的结点的平衡因子的绝对值是否大于1。若是是,找到离插入点最近的一个绝对值大于1的结点,对其进行调整。
即咱们调整的对象都是最小的不平衡树。
对于平衡操做这里分了四种状况讨论:
状况:A的左孩(L)的左子树(L)插入新结点。
操做:B右旋(R),BR替换AL。
状况:A的右孩(R)的右子树(R)插入新结点。
操做:B左旋(L),BL替换AR。
状况:A的左孩(L)的右子树(R)。
操做:将A的左孩子的右子树的结点C(BR)先左后右旋转到A。(注意这里的操做对于的都是上面的操做的复合)。
状况:A的右孩(R)的左子树(L)。
操做:将A的右孩的左子树的结点C(BL)先右后左旋转A
记住全部调整都是为了给插入结点空出位置来的,按照这个思路记忆会方便不少。
B树能够视做为二叉平衡树的一种拓展,也称多路平衡搜索树。
知足以下特性:(m做为B树的阶数,通常≥3)
B 树的查找是跟二叉树相似的多路查找。
分为两步:
因为 B 树通常用于数据库中,即在根据指针在磁盘中找到结点并读到内存中,再在内存中对有序表进行二分搜索,找不到就根据指针读下一个结点,一直找到叶结点为止。
分裂
这里分删除的节点是否在终端节点来讨论。
删除的关键在于要使得结点的关键字数量≥[m/2]-1。
在终端节点上,分三类状况:
不在终端节点上,这种状况要转化为上面那种状况来讨论:
即咱们将要删除的非终端节点与终端节点进行交换,再按终端节点的方式进行删除。
这里引入一个相邻关键字的概念。对于不在终端节点上的结点来讲,其相邻关键字为左子树中值最大的关键字和右子树中最小的关键字。
找相邻关键字的方法与找二叉排序树中前驱和后继的方法相似。
沿着左子树右指针或者右子树左指针一直到终端结点就是相邻关键字了。
一棵m阶的 B+树知足:
B+树与 B 树的主要区别在于:
即理想状况下,散列表的查找复杂度应该为 O(1)。
散列函数的构造要求:
下面是一些经常使用的散列函数:
通常来讲,散列函数不可能避免冲突,因此会对 key 进行再散列,用Hi表示第 i 次探测到的散列地址。
开放定址法即若是发生了冲突,能够经过一个递推公式一直递推去找空闲的空间。
递推公式为:
\[H_i=(H(key)+d_i)\%m\]
其中 m 表示散列表表长,di为增量的一个序列。
在开放定值法的状况下, 物理表中的元素不能随意删除,由于删除元素会截断其余具备相同散列地址的元素的查找地址。暂时只能在逻辑上删除。
为了不同义词冲突,能够把全部同义词存储在一个线性表中。
若是关键词序列为{19,14,23,01,68,20,84,27,55,11,10,79}
散列函数为 H(key)=key%13。
拉链法图示为:
散列表的性能通常取决于三个因素,散列函数,处理冲突,和装填因子。
装填因子 α = 表中记录 n / 散列长度 m。
串的模式匹配即求模式串在主串中的位置。
int Index(String S,String T){ for(int i=0;i<T.len;i++) for(int j=0;j<S.len;j++){ if(S[j]=T[i]) if(j==S.len-1) return TRUE; else break; } return FALSE; }
复杂度为:O(n*m),n 和 m 分别为主串和模式串的长度。
void GetNext(String S, int next[]){ int i=1,j=0; next[1]=0; while(i<S.len){ if(j==0||S.ch[i]==S.ch[j]){ i++;j++; next[i]=j; } else j=next[j]; } } int KMP(String S, String T, int next[]) { int i=1;j=1; while(i<T.len && j<=S.len){ if(j==0||T.ch[i]==S.ch[j]){ i++; j++; } else{ j=next[j]; } if(j>S.len) return i-S.len; else return 0; } }