第九章 查找

静态查找表 :顺序查找、折半查找
#include<stdio.h>
#include<stdlib.h>

//关键字比较的宏定义
#define EQ(a,b) ( (a)==(b) )
#define LT(a,b) ( (a)< (b) )    //小于
#define LQ(a,b) ( (a)> (b) )    //大于
//-------------------静态查找表的顺序存储结构-------------------
typedef int    KeyType;
const int MAXSIZE=20;
//数据元素类型定义为
typedef struct{
    KeyType key;        //关键字域
    int others;         //其他域
}SElemType;
typedef struct {
    SElemType *elem;
    int length;
}SSTable;

void Create(SSTable &ST, int n);
void Destory(SSTable &ST);
void Search(SSTable ST, KeyType key);
void Traverse(SSTable ST, void (*visit)(SElemType data));

void Create(SSTable &ST, int n){
    printf("Init(%d):\n",n);
    ST.elem = (SElemType*) malloc((n+1)*sizeof(SElemType));
    ST.length = n;
    for(int i=1;i<=n;i++) scanf("%d",&ST.elem[i].key);
}
void Visit(SElemType e){
    printf("%d ",e.key);
}
void Traverse(SSTable ST, void (*visit)(SElemType data)){
    for(int i=1;i<=ST.length;i++) visit(ST.elem[i]);
}

//-------------------顺序表的查找-------------------
/*
 * 顺序查找:从表中最后一个记录开始往前找
*/
int Search_Seq(SSTable ST, KeyType key){
    //若查找成功返回位置,否则返回0
    ST.elem[0].key = key;   //给ST.elem[0]赋值,是为了免去查找过程的每一步都去检测整个表是否查找完毕;起监视哨作用
    int i;
    for(i=ST.length; !EQ(ST.elem[i].key, key); --i);
    return i;
}

//-------------------有序表的查找-------------------
int Search_Bin(SSTable ST, KeyType key){
    //折半查找
    //若查找成功返回位置,否则返回0
    int low =1, high = ST.length, mid;
    while (low<=high){
        mid = (low+high)/2;
        if(EQ(key, ST.elem[mid].key)) return mid;
        else if(LT(key, ST.elem[mid].key)) high = mid-1;
        else low = mid+1;
    }
    return 0;
}
//--------------------------------------
int main(){
    SSTable ST;
    Create(ST,8);
    Traverse(ST,Visit);
    printf("\n");
    printf("%d\n",Search_Seq(ST,8));
    printf("%d\n",Search_Bin(ST,8));

    return 1;
}
// 2 3 4 5 6 7 8 9

动态查找表 :二叉排序树、平衡二叉树
#include<stdio.h>
#include<stdlib.h>

//关键字比较的宏定义
#define EQ(a,b) ( (a)==(b) )
#define LT(a,b) ( (a)< (b) )    //小于
#define LQ(a,b) ( (a)> (b) )    //大于

//-------------------二叉排序树:二叉链表-------------------
#define Status int
//数据元素类型定义为
typedef int KeyType;
typedef struct{
    KeyType key;        //关键字域
    int others;         //其他域
}TElemType;
typedef struct BiTNode{
    TElemType data;
    struct BiTNode * lchild, *rchild;   //左右孩子
}BiTNode, *BiTree;      //二叉树节点数据

//-------------------二叉排序树:构造-------------------
BiTree SearchBST(BiTree T, KeyType key){
    //在根指针T所指的二叉排序树中递归查找
    //查找成功返回指向该节点的指针,否则返回NULL
    if(!T || EQ(key, T->data.key)) return T;
    else if(LT(key,T->data.key)) return SearchBST(T->lchild,key);
    else return SearchBST(T->rchild,key);
}
//改写SearchBST,在查找失败时返回插入位置
Status SearchBST(BiTree T, KeyType key, BiTree f, BiTree &p){
    //查找成功时p返回该元素节点,函数返回True;
    //否则p指向查找路径上访问的最后一个节点,函数返回flase
    //f指向T的双亲,初值为NULL
    if(!T) {p=f; return 0; }
    else if(EQ(key, T->data.key)) { p=T; return 1;}
    else if(LT(key, T->data.key)) return SearchBST(T->lchild, key, T, p);
    else return SearchBST(T->rchild, key, T, p);
}

Status InsertBST(BiTree &T, TElemType e){
    //当二叉树中不存在关键字等于给定值的节点时,插入e并返回True;否则返回False
    BiTNode * p, *s;
    if(!SearchBST(T, e.key, NULL, p)){
        s = (BiTNode *)malloc(sizeof(BiTNode));
        s->data = e; s->lchild = s->rchild = NULL;
        if(!p) T = s;   //T为空,二叉排序树是空树
        else if(LT(e.key, p->data.key)) p->lchild = s;
        else p->rchild = s;
        return 1;
    }
    return 0;
}
//中序递归遍历二叉链表
Status InOrderTraverse(BiTree T, Status (*visit)(TElemType e)){
    if(!T) return 1;
    if(InOrderTraverse(T->lchild,visit))
        if(visit(T->data))
            if(InOrderTraverse(T->rchild,visit))
                return 1;
    return 0;
}
Status PrintElement(TElemType e){
    printf("%d ",e.key);                             //实用时注意格式串
    return 1;
}
//先序
Status PreOrderTraverse(BiTree T, Status (*visit)(TElemType e)){
    if(!T) return 1;
    if(visit(T->data))
        if(PreOrderTraverse(T->lchild, visit))
            if(PreOrderTraverse(T->rchild, visit))
                return 1;
    return 0;
}

//-------------------二叉排序树:删除结点-------------------
/*
 * 假设待删节点为 *p, 其双亲节点为 *f. 可设 *p 是 *f 的左孩子
 * 分3种情况讨论:
 * 1.若p是叶子节点,直接修改其双亲节点的指针
 * 2.若p只有左子树或者只有右子树,直接修改其双亲节点的左指针指向p的左(右)子树
 * 3.若p的左右子树均不空:方法一:
 *                      方法二:
 */
Status Delete(BiTree &p){
    //从二叉排序树中删除T节点
    BiTree q, s;
    if(!p->lchild){
        q = p;
        p = p->rchild;
        free(q);
    }else if(!p->rchild){
        q = p;
        p = p->lchild;
        free(q);
    }else{
        q = p; s = p->lchild;
        while (s->rchild){ q = s; s = s->rchild; }
        p->data = s->data;
        if(q!=p) q->rchild = p->lchild;
        else q->lchild = s->lchild;
        free(s);
    }
}
Status DeleteBFS(BiTree &T, KeyType key){
    if(!T) return 0;
    if(EQ(key, T->data.key)) return Delete(T);
    else if(LT(key, T->data.key)) return DeleteBFS(T->lchild, key);
    else return DeleteBFS(T->rchild, key);
}

//-------------------二叉排序树:分析-------------------
/*
 * 折半查找的判定树是唯一的,但是二叉排序树不唯一导致的ASL不同
 * 最差:二叉树变成单支树:ASL=(n+1)/2等同于顺序查找
 * 最好:二叉排序树的形态和折半查找的判定树相同
 * 平均性能:和logn是同等数量级
 */

//-------------------二叉平衡树AVL树-------------------
#define LH 1    //左高
#define EH 0    //等高
#define RH -1   //右高
//二叉链表的头节点中增加一个新的存储平衡因子的域 bf
typedef struct BSTNode{
    TElemType data;
    int bf;         //平衡因子
    struct BSTNode * lchild, *rchild;   //左右孩子
}BSTNode, *BSTree;      //二叉树节点数据
void R_Rotate(BSTree &p){
    //右旋处理
    BSTree lc = p->lchild;
    p->lchild = lc->rchild;
    lc->rchild = p;
    p = lc;
}
void L_Rotate(BSTree &p){
    //左旋处理
    BSTree lc = p->rchild;
    p->rchild = lc->lchild;
    lc->lchild = p;
    p = lc;
}
void LeftBalance(BSTree &T){
    BSTree lc = T->lchild, rd;
    switch (lc->bf) {
        case LH:T->bf = lc->bf = EH; R_Rotate(T); break;    //单向右旋平衡处理
        case RH:{
            rd=lc->rchild;
            switch (rd->bf) {
                case LH: T->bf = RH; lc->bf = EH; break;
                case EH: T->bf = lc->bf = EH;   break;
                case RH: T->bf = EH; lc->bf = LH; break;
            }
            rd->bf = EH;
            L_Rotate(T->lchild);
            R_Rotate(T);
        }
    }
}
void RightBalance(BSTree &T){
    BSTree rc = T->rchild, ld;
    switch (rc->bf) {
        case RH:T->bf = rc->bf = EH; L_Rotate(T); break;    //单向左旋平衡处理
        case LH:{
            ld=rc->lchild;
            switch (ld->bf) {
                case LH: T->bf = EH; rc->bf = RH; break;
                case EH: T->bf = rc->bf = EH;   break;
                case RH: T->bf = LH; rc->bf = EH; break;
            }
            ld->bf = EH;
            L_Rotate(T->rchild);
            R_Rotate(T);
        }
    }
}
Status InsertAVL(BSTree &T, TElemType e, bool &taller){
    //若在平衡二叉排序树T中不存在和e有相同关键字的节点,则插入一个新节点,返回1;否则返回0
    //如果因为插入而使得二叉排序树失衡,则作旋转处理
    //taller反映T长高与否
    if(!T){                               //1.如果T为空:
        T = (BSTree)malloc(sizeof(BSTNode)); T->data = e; T->bf = 0;
        T->lchild = T->rchild = NULL; taller = 1;
    }else{                                //2.如果T不为空:
        if(EQ(e.key, T->data.key)){     //2.1如果T中存在和e有相同关键字的节点:不再插入
            taller = 0;
            return 0;
        }
        if(LT(e.key, T->data.key)){     //2.2如果e关键字小于T关键字:在T左子树中搜索
            if(!InsertAVL(T->lchild, e, taller)) return 0;  //未插入
            if(taller)                             //插入了,使得T左子树长高了
                switch (T->bf) {
                    case LH:LeftBalance(T); taller = 0; break;  //2.2.1 原本T左子树高于右子树,T的左子树再插入一个e,需要左平衡处理,T不增高
                    case EH:T->bf = LH; taller = 1; break;         //2.2.2 原本T左子树等高右子树,T的左子树再插入一个e,T平衡因子变1,T增高,
                    case RH:T->bf = EH; taller = 0; break;         //2.2.3 原本T左子树低于右子树,T的左子树再插入一个e,T平衡因子变0,T不增高
                }
        }else{                               //2.3如果e关键字大于T关键字:在T右子树中搜索
            if(!InsertAVL(T->rchild, e, taller)) return 0;  //未插入
            if(taller)                             //插入了,使得T右子树长高了
                switch (T->bf) {
                    case LH:T->bf = EH; taller = 0; break;          //2.3.1 原本T左子树高于右子树,T的右子树再插入一个e,T平衡因子变0,T不增高
                    case EH:T->bf = RH; taller = 1; break;         //2.3.2 原本T左子树等高右子树,T的右子树再插入一个e,T平衡因子变-1,T增高,
                    case RH:RightBalance(T); taller = 0; break;         //2.3.3 原本T左子树低于右子树,T的右子树再插入一个e,需要作右平衡处理,T不增高
                }
        }
    }
    return 1;
}
//中序递归遍历二叉链表存储的平衡二叉树
Status InOrderTraverse_BST(BSTree T, Status (*visit)(TElemType e)){
    if(!T) return 1;
    if(InOrderTraverse_BST(T->lchild,visit))
        if(visit(T->data))
            if(InOrderTraverse_BST(T->rchild,visit))
                return 1;
    return 0;
}
Status PreOrderTraverse_BST(BSTree T, Status (*visit)(TElemType e)){
    if(!T) return 1;
    if(visit(T->data))
        if(PreOrderTraverse_BST(T->lchild, visit))
            if(PreOrderTraverse_BST(T->rchild, visit))
                return 1;
    return 0;
}

//--------------------------------------
int main(){
    //---------二叉排序树
    BiTree T = NULL;
    TElemType e;
    for(int i=1;i<=7;i++){
        scanf("%d",&e.key); //45 24 53 45 12 24 90
        InsertBST(T,e);
    }
    InOrderTraverse(T,PrintElement);
    printf("\n");

    DeleteBFS(T,90);
    InOrderTraverse(T,PrintElement);
    printf("\n");
    PreOrderTraverse(T,PrintElement);
    printf("\n\n");

    //---------平衡二叉树
    BSTree TT = NULL; bool taller;
    for(int i=1;i<=7;i++){
        scanf("%d",&e.key); //5 13 24 37 53 90 99
        InsertAVL(TT,e,taller);
    }
    InOrderTraverse_BST(TT,PrintElement);   //中序
    printf("\n");
    PreOrderTraverse_BST(TT,PrintElement);  //先序
    printf("\n");

    return 1;

}
B-树

查找,插入

#include<stdio.h>
#include <cstdlib>

typedef int Status;

//-------------------B-树的节点类型说明-------------------
#define m 3
typedef int KeyType;
typedef struct BTNode{
    int keynum;
    struct BTNode *parent = {NULL};      //指向双亲节点
    KeyType key[m+1];           //关键字,0号单元未用   1 2
    struct BTNode *ptr[m+1] = {NULL};    //子树指针            0 1 2
    //Record *recptr[m+1];        //记录指针向量,0号单元未用
}BTNode, *BTree;
typedef struct {
    BTNode *pt;     //指向找到的节点
    int i;          //1..m 在节点中的关键字序号
    int tag;        //1为查找成功,0为查找失败
}Result;            //B-树的查找结果类型

//-------------------B-树的查找-------------------
int Search(BTree T, KeyType K){
    for(int i=0;i<=T->keynum;i++){
        if(i==0)
            if(T->key[1]>K) return i;
        if(i==T->keynum)
            if(T->key[i]>K) return i;
        if(T->key[i]<=K && K<=T->key[i+1]) return i;

    }
}
Result SearchBTree(BTree T, KeyType K){
    //在m阶B-树上查找关键字K,返回结果  (pt, i, tag)
    //若查找成功:特征值tag=1,指针pt所指节点中第i个关键字等于K
    //否则,tag=0,等于K的关键字应该插在指针pt所指节点中第i和第i+1个关键字之间
    BTree p = T, q = NULL; int found = 0; int i=0;
    while (p && !found){
        i = Search(p, K);   //在p所指节点的key域中,寻找
                            //返回i表示 p->key[i]<=K<=p->key[i+1]
        if (i>0 && p->key[i]==K) found = 1;
        else{ q=p; p=p->ptr[i]; }
    }
    if(found) return {q, i, 1};
    else return {q, i, 0};
}

/*
 B-树查找分析
 查找包括两种基本操作:
    1在B树种找节点
    2在节点种找关键字
 由于B-树通常存储在磁盘上,前一查找操作是在磁盘上进行;
 后一操作是在内存中进行,即在磁盘上找到指针p所指节点后,先将节点中的信息读入内存,然后再利用顺序查找或折半查找查询等于K的关键字
 显然,磁盘上查找比在内存中进行查找耗费时间多得多
 因此,在磁盘上进行查找的次数、即待查关键字所在节点在B-树上的层次数,是决定B-树查找效率的首要因素

 */

//-------------------B-树的插入-------------------
/*
 * B-树的生成也是从空树起,逐个插入关键字而得。
 * 但是由于B-树的每个节点的关键字个数必须>=ceil(m/2)-1
 * 因此,每次插入一个关键字不是在树中添加一个叶子节点,而是首先在最低层的某个非终端节点中添加一个关键字
 *      若该节点的关键字个数<=m-1,则插入完成;否则要产生节点分裂
 */
//把分裂出的节点ap和关键字K插入上一层节点q中
Status Insert(BTree &q, int i, KeyType K, BTree &ap){
    //将K插入到q->key[i+1];将ap插入到q->ptr[i+1]        1 2
    for(int j=q->keynum+1;j>i+1;--j){
        q->key[j] = q->key[j-1];
        q->ptr[j] = q->ptr[j-1];
    }
    q->key[i + 1] = K;
    q->ptr[i + 1] = ap;

    q->keynum++;
    return 1;
}
//插入之后,先分裂节点q为: q和ap
Status split(BTree &q, int s, BTree &ap){
    //将q->key[s+1..m], q->ptr[s..m]和q->recptr[s+1..m]移入新节点 *ap
    ap = (BTree)malloc(sizeof(BTNode));
    ap->keynum = m-s; int i, j;
    ap->parent = q->parent;
    for(i=s+1, j=1;i<=m;i++,j++) ap->key[j] = q->key[i];
    for(i=s, j=0;i<=m;i++,j++) ap->ptr[j] = q->ptr[i];
    q->keynum = s-1;
    return 1;
}
Status NewRoot(BTree &T, BTree q, KeyType x, BTree ap){
    q = (BTree)malloc(sizeof(BTNode));
    q->keynum = 1;
    q->key[1] = x;
    q->ptr[0] = T;
    q->ptr[1] = ap;
    T = q;
    return 1;
}
Status InsertBTree(BTree &T, KeyType K, BTree q, int i){
    //在m阶B-树T上节点 q 的key[i] 与  key[i+1] 之间插入关键字K
    //若引起节点过大,则沿着双亲链进行必要的节点分裂调整
    BTree ap = NULL;
    int finished = 0; int mm = m; KeyType x = K;
    while(q && !finished){
        Insert(q, i, x, ap);
        if(q->keynum<mm) finished = 1;
        else {
            int s = (mm%2==0)?mm/2:(mm/2)+1;
            split(q, s, ap);
            x = q->key[s];
            q = q->parent;
            if(q) i = Search(q, x);
        }
    }
    //如果T为空,或者根节点被分裂,需要重新出现一个根节点
    if(!finished) NewRoot(T, q, x, ap); //生成含信息(T,x,ap)的新根节点,T和ap为子树指针
    return 1;
}
//对已经存在的B-树T进行插入,首先判断是否已经存在同等关键字的节点
//如果不存在,则进行插入
//否则退出
Status InsertKey(BTree &T, KeyType K){
    Result R = SearchBTree(T,K);
    if(R.tag!=1) InsertBTree(T, K, R.pt, R.i);
    return 1;
}

//-------------------B-树的删除-------------------


//-------------------B-树的构造-------------------
//笨办法构造
Status CreateBTree(BTree &T){
    T = (BTree)malloc(sizeof(BTNode));
    BTree t1 = (BTree)malloc(sizeof(BTNode));
    BTree t2 = (BTree)malloc(sizeof(BTNode));
    BTree t3 = (BTree)malloc(sizeof(BTNode));
    BTree t4 = (BTree)malloc(sizeof(BTNode));
    BTree t5 = (BTree)malloc(sizeof(BTNode));
    BTree t6 = (BTree)malloc(sizeof(BTNode));
    BTree t7 = (BTree)malloc(sizeof(BTNode));
    *t1 = {2,t6,{0,3,12},{NULL,NULL,NULL}};
    *t2 = {1,t6,{0,37},{NULL,NULL}};
    *t3 = {1,t7,{0,50},{NULL,NULL}};
    *t4 = {2,t7,{0,61,70},{NULL,NULL,NULL}};
    *t5 = {1,t7,{0,100},{NULL,NULL}};
    *t6 = {1,T,{0,24},{t1,t2}};
    *t7 = {2,T,{0,53,90},{t3,t4,t5}};
    *T = {1,NULL,{0,45},{t6,t7}};
    return 1;
}
//中序输出
Status Print(BTree T){
    BTree p = T;
    if (p){
        for(int i=1;i<=p->keynum;i++)
            printf("%d ",p->key[i]);
        printf("\n");
        for(int i=0;i<=p->keynum;i++)
            Print(p->ptr[i]);
    }
    return 1;
}
//先序输出
Status Print_(BTree T){
    BTree p = T;
    if (p){
        for(int i=0;i<=p->keynum;i++){
            Print_(p->ptr[i]);
            if(i<p->keynum) printf("%d ",p->key[i+1]);
        }
    }
    return 1;
}

//--------------------------------------
int main(){
    BTree T;
    CreateBTree(T);
    Print_(T);
    printf("\n");

    int in[] = {30,26,85,7};
    for(int i=0;i<4;i++){
        InsertKey(T,in[i]);
        Print_(T);
        printf("\n");
    }
    return 1;
}

B+树

哈希表
#include<stdio.h>
#include<stdlib.h>

typedef int Status;
//关键字比较的宏定义
#define EQ(a,b) ( (a)==(b) )
#define LT(a,b) ( (a)< (b) )    //小于
#define LQ(a,b) ( (a)> (b) )    //大于

//-------------------开放定址哈希表的存储结构-------------------
int hashsize[] = {2,3,5,7,11,13,16,19,23,29,31,37,41,43,47,53,59,61,67,71};  //哈希表容量递增表
typedef int KeyType;
const int NULLKEY=-1;
typedef struct{
    KeyType key;
}ElemType;
typedef struct {
    ElemType * elem;    //数据元素存储基址,动态分配数组
    int count;          //当前数据元素个数
    int sizeindex;      //hashsize[sizeindex]为当前容量
}HashTable;

//-------------------开放定址哈希表-------------------
int Hash(KeyType K, int di, int size){
    //使用线性探测再散列取递增序列
    return (K+di)%size;
}

//开放定址哈希表的查找
Status SearchHash(HashTable H, KeyType K, int &p, int &c) {
    //在哈希表中查找关键字为K的元素。若查找成功,以p指示待查数据元素在表中的位置,并返回1
    //                          否则,以p指示插入位置,返回0
    //c用来计查找长度,初值置1
    int di = 0;
    int k = p = Hash(K, di++, 13);
    while (H.elem[p].key != NULLKEY && !EQ(K, H.elem[p].key)){     //该位置不空,且关键字不相等
        p = Hash(k, di++, hashsize[H.sizeindex]);  //求得下一探测地址
        c++;
    }
    if (EQ(K, H.elem[p].key)) return 1;
    else return 0;      //p返回了插入位置
}

//开放定址哈希表的插入
Status InsertHash(HashTable &H, ElemType e){
    //先查找,不成功时插入数据,并返回1
    int c = 1, p = 0;
    if(SearchHash(H, e.key, p ,c)) return 0;
    H.elem[p] = e;
    ++H.count;
    return c;
}

Status CreateHash(HashTable &H, int n){
    H.count = n;
    H.sizeindex = 6;
    H.elem = (ElemType*)malloc(hashsize[H.sizeindex]*sizeof(ElemType));
    for(int i=0;i<hashsize[H.sizeindex];i++) (H.elem+i)->key = NULLKEY;
    ElemType e;
    int c=0;    //查找次数
    for(int i=0; i<n; i++){
        scanf("%d",&e.key);
        c += InsertHash(H,e);
    }
    return c;
}

void Print(HashTable H){
    for(int i=0; i<hashsize[H.sizeindex]; i++) printf("%d ",(H.elem+i)->key);
    printf("\n");
}

int main(){
    HashTable H;
    int c = CreateHash(H,12);   //返回查找长度
    Print(H);
    printf("ASL:%.2f\n",float(c)/12);
    return 1;
}
// 19 14 23 01 68 20 84 27 55 11 10 79
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在学C++

2角不嫌多,1角不嫌少

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值