对于哪个步骤不明白的可以在这里看下实际过程
思想:从头到尾挨个找(反过来也行)
顺序查找时间复杂度: O ( n ) O(n) O(n)
代码:
#define MAXSIZE 100
typedef int ElemType;
typedef struct{ //查找表的数据结构(顺序表)
ElemType *elem; //动态数组基址
int TableLen; //表的长度
}SSTable;
//顺序查找
int Search_Seq(SSTable ST, ElemType key){
int i;
for(i=0; i<ST.TableLen && ST.elem[i] !=key; i++);
//查找成功,则返回元素下标;查找失败,则返回-1
return i==ST.TableLen? -1 : i;
}
//“哨兵”
int Search_Seq_(SSTable ST, ElemType key){
//数据从下标1开始存储
ST.elem[0]=key; //“哨兵”
int i;
for(i=ST.TableLen; ST.elem[i]!=key; i--); //从后往前找
return i; //查找成功,则返回元素下标;查找失败,则返回0
}
又称“二分查找”,仅适用于有序的顺序表
折半查找时间复杂度= O ( log 2 n ) O(\log_2n) O(log2n)
代码:
//结构体
typedef struct{
ElemType *elem;
int length;
}SSTable;
int Search_Bin(SSTable ST,ElemType key){
// 在有序表ST中折半查找其关键字等于key的数据元素。
// 若找到,则函数值为该元素在表中的位置,否则为-1。
int low=0,high=ST.length-1; //置查找区间初值
int mid;
while(low <= high){
mid = (low+high)/2;
if(key==ST.elem[mid])
return mid; //找到待查元素
else if(key<ST.R[mid].key)
high = mid -1; //继续在前半部分查找
else
low = mid + 1; //继续在后半部分查找
}
return -1; //表中不存在待查元素
}// Search_Bin
思想:
索引表中记录每个分块的最大关键字、分块的区间;
先查索引表(顺序或折半),再对分块内进行顺序查找。
块内无序,块间有序
设索引查找和块内查找的平均查找长度分别为 L 1 L_1 L1、 L s L_s Ls,则分块查找的平均查找长度为 A S L = L 1 + L s ASL=L_1 + L_s ASL=L1+Ls
A L S = s 2 + 2 s + n 2 s , 当 s = n 时 , A L S 最 小 = n + 1 ALS = \frac{s^2+2s+n}{2s},当s=\sqrt n时,ALS_{最小}=\sqrt{n}+1 ALS=2ss2+2s+n,当s=n时,ALS最小=n+1
n个结点的二叉树最小高度为 ⌊ log 2 n ⌋ + 1 \lfloor{\log_2n}\rfloor + 1 ⌊log2n⌋+1 或是 ⌈ log 2 ( n + 1 ) ⌉ \lceil{\log_2(n+1)}\rceil ⌈log2(n+1)⌉
对具有n个关键字的树型结构,具有n+1个叶结点
粘一下定义,简单的说就是,左子树节点值 ≤ \leq ≤ 根节点值 ≤ \leq ≤ 右子树结点值
[-] 查找思路:
若树非空,目标值与节点的值比较:
若相等,则查找成功;
若小于根节点,则在左子树上查找,否则在右子树上查找;
查找成功返回节点指针,失败返回NULL
下面是个完整的代码,包括构建二叉树,查找,删除和中序遍历:
#include
using namespace std;
//结构体,二叉排序树节点
typedef struct BSTNode{
int data;
struct BSTNode *lchild, *rchild;
}BSTNode, *BSTree;
//二叉排序树中查找值为data的节点
//非递归,循环
BSTNode *BST_Search(BSTree T, int key){
while(T != NULL && key != T->data){
//若树空(未找到),或等于根节点值,则结束循环
if(key < T->data) T = T->lchild; //小于,在左子树上查找
else T = T->rchild; //大于,在右子树上查找
}
return T;
}
//递归查找
BSTNode *SearchBST(BSTree T, int key){
if(T == NULL) return NULL; //查找失败
if(key == T->data) return T; //查找成功
else if (key < T->data)
return SearchBST(T->lchild, key); //在左子树查找
else
return SearchBST(T->rchild, key); //在右子树查找
}
//插入 ,递归
void InsertBST(BSTree &T,int e){
if(T == NULL){ //!T //找到插入位置,叶子节点(或根)
BSTree S = new BSTNode; //生成新结点S
S->data = e; //新结点S的关键字为e
S->lchild = S->rchild = NULL; //新结点S作为叶子结点
T = S; //把新结点S链接到已找到的插入位置
}
//这里不考虑相同的数字,有相同的数只算一个
else if (e< T->data)
InsertBST(T->lchild, e ); //将S插入左子树
else if (e> T->data)
InsertBST(T->rchild, e); //将S插入右子树
}// InsertBST
void CreateBST(BSTree &T ) {
//依次读入一个关键字为key的结点,将此结点插入二叉排序树T中
T=NULL;
int e;
cout<<"请输入若干整数,用空格分开,以-1结束输入"<<endl;
cin>>e;
while(e != -1){ //-1,结束标志
InsertBST(T, e); //将此结点插入二叉排序树T中
cin>>e;
}
}//CreatBST
void DeleteBST(BSTree &T,int key) {
//从二叉排序树T中删除关键字等于key的结点
BSTree p=T;
BSTree f=NULL; //初始化
BSTree q,s;
while(p){
//从根开始查找关键字等于key的结点*p
if (p->data == key) break; //找到关键字等于key的结点*p,结束循环
f=p; //*f为*p的双亲结点
if(p->data> key)
p=p->lchild; //在*p的左子树中继续查找
else
p=p->rchild; //在*p的右子树中继续查找
}//while
if(!p) return; //p==NULL //找不到被删结点则返回
/*―考虑三种情况实现p所指子树内部的处理:*p左右子树均不空、无右子树、无左子树―*/
if((p->lchild) && (p->rchild)){ //被删结点*p左右子树均不空
q = p;
s = p->lchild;
while (s->rchild) //在*p的左子树中继续查找其前驱结点,即最右下结点
{q = s; s = s->rchild;} //向右到尽头
p->data = s->data; //s指向被删结点的“前驱”
if(q!=p){
q->rchild = s->lchild; //重接*q的右子树
}
else q->lchild = s->lchild; //重接*q的左子树
delete s;
}else{
if(!p->rchild){ //被删结点*p无右子树,只需重接其左子树
q = p; p = p->lchild;
}
else if(!p->lchild){ //被删结点*p无左子树,只需重接其右子树
q = p; p = p->rchild;
}
/*―――――将p所指的子树挂接到其双亲结点*f相应的位置―――――*/
if(!f) T=p; //被删结点为根结点
else if (q==f->lchild) f->lchild = p; //挂接到*f的左子树位置
else f->rchild = p; //挂接到*f的右子树位置
delete q;
}
}//DeleteBST
//中序遍历
void InOrderTraverse(BSTree &T){
if(T){
InOrderTraverse(T->lchild);
cout<<T->data<<" ";
InOrderTraverse(T->rchild);
}
}
int main()
{
BSTree T;
CreateBST(T); //相同的数只算一个
cout<<"当前有序二叉树中序遍历结果为:"<<endl;
InOrderTraverse(T);
int key;//待查找或待删除内容
cout<<"\n\n请输入待查找关键字(整数):"<<endl;
cin>>key;
BSTree result_1=BST_Search(T,key); //循环查找
BSTree result_2=SearchBST(T,key); //递归查找
if(result_1 && result_2)
cout<<"循环和递归都,找到了关键字"<<key<<endl;
else
cout<<"未找到"<<key<<endl;
cout<<"\n请输入待删除的关键字:"<<endl;
cin>>key;
DeleteBST(T,key);
cout<<"当前有序二叉树中序遍历结果为(删除后):"<<endl;
InOrderTraverse(T);
return 1;
}
AVL Tree链接
∣ 左 子 树 高 − 右 子 树 高 ∣ ≤ 1 \lvert 左子树高 -右子树高 \rvert \leq 1 ∣左子树高−右子树高∣≤1,平衡因子只可取-1、0或1。
删除,步骤:
①删除节点(方法同,二叉排序树)
②一路向上找到最小不平衡子树,找不到就over
③找最小不平衡子树下,”个头“最高的儿子,孙子
④根据孙子的位置,调整平衡(LL/RR/LR/RL)
⑤如果不平衡向上传导,继续②
Red/Black Tree链接
简要特点:
左右跟,根叶黑
不红红,黑路同
详细特点:
左子树结点值 ≤ \leq ≤ 根结点值 ≤ \leq ≤ 右子树结点值
①每个结点或是红色,或是黑色的;
②根节点是黑色的;
③叶结点(外部结点、NULL结点、失败结点)均是黑色的;
④不存在两个相邻的红结点(即红结点的父节点和孩子结点均是黑色);
⑤对每个结点,从该节点到任一叶结点的简单路径上,所含黑结点的数目相同;
B-Tree 链接
令 k = ⌈ m / 2 ⌉ k=\lceil{m/2}\rceil k=⌈m/2⌉
高 度 为 h 的 m 阶 B 树 , 含 有 关 键 字 个 数 至 少 是 : 2 ⋅ k h − 1 − 1 高度为h的m阶B树,含有关键字个数至少是:2 \cdot k^{h-1}-1 高度为h的m阶B树,含有关键字个数至少是:2⋅kh−1−1
1 + 2 ( k − 1 ) ( k 0 + k 1 + ⋯ + k h − 2 ) = 1 + 2 ( k h − 1 − 1 ) = 2 ⋅ k h − 1 − 1 1+2(k-1)(k^0+k^1+\cdots+k^{h-2}) = 1+2(k^{h-1}-1) = 2 \cdot k^{h-1}-1 1+2(k−1)(k0+k1+⋯+kh−2)=1+2(kh−1−1)=2⋅kh−1−1
高度为h的3阶B树,含有关键字个数至少是: 2 h − 1 2^h-1 2h−1,同完全二叉树(满二叉)
高度为h的5阶B树,含有关键字个数至少是: 2 ⋅ 3 h − 1 − 1 2 \cdot 3^{h-1}-1 2⋅3h−1−1
高度为h的完全二叉树至少 2 h − 1 2^{h-1} 2h−1个结点,最多 2 h − 1 2^{h}-1 2h−1个结点
下面说明下B树特征和形状,先来个5叉树,B树就是在多叉树加一些约束,
B树结构:
对于高度问题,
装填因子 α \alpha α越大,平均查找长度变大,“冲突”越多,查找效率越低
常见的散列函数
处理冲突方法
①线性探测法: d i = 0 , 1 , 2 , 3 , ⋯ , m − 1 d_i=0,1,2,3,\cdots,m-1 di=0,1,2,3,⋯,m−1,即发生冲突时,每次往后探测相邻的下一个单元是否为空。线性探测法很容易造成同义词、非同义词的“聚集(堆积)”现象,严重影响查找效率产生原因——冲突后再探测一定是放在某个连续的位置。
查找效率分析:
②平方探测法,当 d i = 0 2 , 1 2 , − 1 2 , 2 2 , − 2 2 , ⋯ , k 2 , − k 2 d_i=0^2, 1^2, -1^2, 2^2, -2^2, \cdots , k^2, -k^2 di=02,12,−12,22,−22,⋯,k2,−k2时,称为平方探测法,又称二次探测法,其中 k ≤ m / 2 k \leq m/2 k≤m/2。比起线性探测法更不容易产生“聚集(堆积)”问题。
③伪随机序列法, d i d_i di是一个伪随机序列,自己定义的,如 d i = 0 , 3 , 5 , 11 , … di= 0, 3, 5, 11, \ldots di=0,3,5,11,…
④再散列法(再哈希法):除了原始的散列函数 H ( k e y ) H(key) H(key)之外,多准备几个散列函数,当散列函数冲突时,用下一个散列函数计算一个新地址,直到不冲突为止。