[数据结构]查找的基本概念以及基本实现

一、查找的基本概念

1、查找表:
用于查找的数据元素集合。它由同一类型的数据元素构成。
2、关键字、主关键字、次关键字:
关健字:数据元素中的某个数据项;
主关键字:唯一能标识数据元素的关键字,即每个元素的关键字值互不相同;
次关健字:若查找表中某些元素的关健字值相同,比如学生信息表中的学号是主关键字,姓名是次关键字。
3、查找:
指在数据元素集合中查找满足某种条件的数据元素的过程。
4、静态查找:
若只对查找表在查找表中查看某个特定的数据元素是否在查找表中,或检索某个特定元素的各种属性。
5、动态查找:
在查找过程中可以将查找表不存在的数据元素插入,或从查找表中删除某个数据元素。
6、内查找和外查找:
整个查找过程全部在内存中进行,则称为内查找;
若查找过程中还需要访问外存,则称为外查找。

二、常见的几种查找方法

1、静态查找:
静态查找一般以顺序或线性表表示,线性表可以有不同的表示方法,在不同的表示方法中,实现查找操作的方法也不同。
(1)顺序查找:从表的一端开始,顺序扫描整个线性表,依次将扫描到的结点关键字与给定值k进行比较,若当前扫描到的结点关键字与k相等,则查找成功,返回该结点在表中的位置;若扫描结束后,仍然未找到关键字等于k的结点,则查找失败,返回特定的值(0或NULL)

int Seq_search(int a[],int n,int key){
   int i=n;
   while(a[i]!=x){
     i--;
   }
   return i;///返回查找到的元素的位置
}

(2)折半查找(二分查找):是一种效率较高的查找方法,折半查找要求查找表用顺序存储结构存放且各数据元素按关键字序列有序(升序或降序)排列,并且要求线性顺序存储。
比如:
在这里插入图片描述

int bin_search(int a[],int k){
      low=1;hign=n;
      while(low<=high){
          mid=(low+high)/2;
          if(k==a[mid]) break;
          else if(k<a[mid]) high=mid-1;
          else low=mid+1;
      }
      if(low<=high) return mid;///查找成功
      else return 0;///查找失败
}

(3)分块查找:又称为索引顺序查找,是对顺序查找的一种改进。性能介于顺序查找和二分查找之间。
基本思想:查找表可以分为若干个子块。块内元素可以无序,但块之间是有序的,即第一块的最大关健字小于第二块中的所有记录的关键字。其中索引表是按索引值递增(或递减)排序的。
算法流程:
1)先选取各块中的最大关健字构成一个索引表;
2)查找分两部分:先对索引表进行二分查找或顺序查找,以确定待查记录在哪一块;然后在已确定的块中用顺序法进行查找
在这里插入图片描述

struct table{
     int key;///块的关键字
     int strart;///块的起始值
     int end;///块的结束值
}t[4];///定义结构数组
int serch(int key,int a[]){
       int i,j;
       i=1;
       while(i<=3&&key>t[i].key)   i++;///确定在哪一块
       if(i>3) return 0;
       j=t[i].start;///块范围的起始值
       while(j<=t[i].end&&a[i]!=key) j++;///在确定的块内进行顺序查找
       if(j>t[i].end) j=0;
       return j;
}
int main(){
   int j=0;
   for(int i=1;i<16;i++) scanf(&a[i])///输入15个从小到大的15个数。

  for(int i=1;i<=3;i++){
       t[i].start=j+1;
       j++;\\\j=1
       t[i].end=j+4;///j=5,块范围的结束值
       j+=4;///j=5;
       t[i].key=a[j];///块范围中元素的最大值
   }
   scanf(&key)///输入要查询的元素key;
   k=search(key,a);///调用函数查找
   如果k不等0,就找到,否则就找不到
}

2、动态查找:
(1)二叉排序树(二叉查找树)
它是具有以下性质的二叉树:
1)若它的左子树非空,则左子树上所有结点的值均小于它的根结点的值
2)若它的右子树非空,则右子树上所有结点的值均大于(若允许具有相同关键字的结点存在,则大于等于)它的根结点的值。
3)它的左、右子树本身又是一棵二叉排序树。
在这里插入图片描述

///结点结构
typedef int DataType;
typedef struct Node 
{
    DataType data;///结点
    struct Node *lchild, *rchild;///左右指针域
}BiTree;

二叉排序树查找关键字(递归版、非递归版):

BiTree SearchBST(BiTree T,int x){///二叉树查找关健字递归版:
    //如果递归过程中 T 为空,则查找结果,返回NULL;或者查找成功,返回指向该关键字的指针
    if (!T || x==T->data) {
        return T;
    }else if(x<T->data){
        //递归遍历其左孩子
        return SearchBST(T->lchild, x);
    }else{
        //递归遍历其右孩子
        return SearchBST(T->rchild, x);
    }
}
BiTree Search_BST(BitTree T,DataType x)///二叉树查找关健字非递归版:
{
    BiTree *p = T;
    while (p) 
    {       
        if (p->data == x)  return p;
        p = (x < p->data) ? p->lchild : p->rchild;
    }
    return NULL;
}

二叉排序树插入关键字:

///插入关健字
BOOL SearchBST(BiTree T,KeyType key,BiTree f,BiTree *p){
    //如果 T 指针为空,说明查找失败,令 p 指针指向查找过程中最后一个叶子结点,并返回查找失败的信息
    if (!T){
        *p=f;
        return false;
    }
    //如果相等,令 p 指针指向该关键字,并返回查找成功信息
    else if(key==T->data){
        *p=T;
        return true;
    }
    //如果 key 值比 T 根结点的值小,则查找其左子树;反之,查找其右子树
    else if(key<T->data){
        return SearchBST(T->lchild,key,T,p);
    }else{
        return SearchBST(T->rchild,key,T,p);
    }
}
//插入函数
BOOL InsertBST(BiTree T,ElemType e){
    BiTree p=NULL;
    //如果查找不成功,需做插入操作
    if (!SearchBST(T, e,NULL,&p)) {
        //初始化插入结点
        BiTree s=(BiTree)malloc(sizeof(BiTree));
        s->data=e;
        s->lchild=s->rchild=NULL;
        //如果 p 为NULL,说明该二叉排序树为空树,此时插入的结点为整棵树的根结点
        if (!p) {
            T=s;
        }
        //如果 p 不为 NULL,则 p 指向的为查找失败的最后一个叶子结点,只需要通过比较 p 和 e 的值确定 s 到底是 p 的左孩子还是右孩子
        else if(e<p->data){
            p->lchild=s;
        }else{
            p->rchild=s;
        }
        return true;
    }
    //如果查找成功,不需要做插入操作,插入失败
    return false;
}

二叉排序树删除关键字:

///删除关健字
int Delete(BiTree *p)
{
    BiTree q, s;
    //情况 1,结点 p 本身为叶子结点,直接删除即可
    if(!(*p)->lchild && !(*p)->rchild){
        *p = NULL;
    }
    else if(!(*p)->lchild){ //左子树为空,只需用结点 p 的右子树根结点代替结点 p 即可;
        q = *p;
        *p = (*p)->rchild;
        free(q);
    }
    else if(!(*p)->rchild){//右子树为空,只需用结点 p 的左子树根结点代替结点 p 即可;
        q = *p;
        *p = (*p)->lchild;//这里不是指针 *p 指向左子树,而是将左子树存储的结点的地址赋值给指针变量 p
        free(q);
    }
    else{//左右子树均不为空,采用第 2 种方式
        q = *p;
        s = (*p)->lchild;
        //遍历,找到结点 p 的直接前驱
        while(s->rchild)
        {
            q = s;
            s = s->rchild;
        }
        //直接改变结点 p 的值
        (*p)->data = s->data;
        //判断结点 p 的左子树 s 是否有右子树,分为两种情况讨论
        if( q != *p ){
            q->rchild = s->lchild;//若有,则在删除直接前驱结点的同时,令前驱的左孩子结点改为 q 指向结点的孩子结点
        }else{
            q->lchild = s->lchild;//否则,直接将左子树上移即可
        }
        free(s);
    }
    return TRUE;
}
int DeleteBST(BiTree *T, int key)
{
    if( !(*T)){//不存在关键字等于key的数据元素
        return FALSE;
    }
    else
    {
        if( key == (*T)->data ){
            Delete(T);
            return TRUE;
        }
        else if( key < (*T)->data){
            //使用递归的方式
            return DeleteBST(&(*T)->lchild, key);
        }
        else{
            return DeleteBST(&(*T)->rchild, key);
        }
    }
}

(2)平衡二叉树(AVL树)
它或者是一颗空树,或者具有以下性质的二叉排序树;它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。
平衡因子:将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor),BF范围[-1,1];
最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树.。
一颗AVL树具有的必要条件:
1) 它必须是二叉查找树。
2) 每个结点的左子树和右子树的高度差至多为1。
下面举一个例子:平衡二叉树生成过程:
在这里插入图片描述

///平衡二叉树结点结构:
typedef struct node{
int key;
struct node *left;
int height;
}BTnode;

平衡二叉树通过插入元素,然后树会出现不平衡,然后我们可以用四种类型去调整。
①LL型调整:由于在A的左孩子(L)的左子树(L)上插入新结点。
在这里插入图片描述
例如:
在这里插入图片描述

BTnode *ll_rotate(BTnode *y)
{
    BTnode *x = y->left;
    y->left = x->right;
    x->right = y;   
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
    return x;
}

②RR型调整:由于在A的右孩子®的右子树®上插入新结点。
在这里插入图片描述
在这里插入图片描述

BTnode *rr_rotate(struct node *y)
{
    BTnode *x = y->right;
    y->right = x->left;
    x->left = y;
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
    return x;
}

③LR型调整:在A的左孩子(L)的右子树®上插入新结点
在这里插入图片描述
在这里插入图片描述

BTNode* lr_rotate(BTNode* y)
{
    BTNode* x = y->left;
    y->left = rr_rotate(x);
    return ll_rotate(y);
}

④RL型调整:在A的右孩子®的左子树(L)上插入新结点
在这里插入图片描述
在这里插入图片描述

BTnode* lr_rotate(BTnode* y)
{
    BTnode* x = y->left;
    y->left = rr_rotate(x);
    return ll_rotate(y);
}

三、参考资料
1、数据结构
2、https://blog.csdn.net/isunbin/article/details/81707606

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值