【笔记】大话数据结构-查找

查找概论

静态查找表:只做查找操作。
动态查找表:查找时插入/删除数据元素。

顺序表查找

最基础查找算法

for循环1到n。

优化

设置“哨兵”,令a[0]=key(哨兵),从i=n开始,i–直到找到或者i=0。此方法免去了查找过程中每一次判断是否i>n越界。

时间复杂度O(n)。

有序表查找

折半查找(二分查找)

前提是线性表有序,取中间记录为比较对象,若给定值与中间记录的关键字相等,查找成功;若小于/大于,在中间记录的左/右半区重复上述操作。
使用low=1,high=n,mid=(low+high)/2。while(low<=high),若小于,high=mid-1;若大于,low=mid+1。

时间复杂度O(logn)。

插值查找

折半查找的mid=(low+high)/2,变化得到mid=low+ 1 2 \frac{1}{2} 21(high-low)。将1/2替换为 k e y − a [ l o w ] a [ h i g h ] − a [ l o w ] \frac{key-a[low]}{a[high]-a[low]} a[high]a[low]keya[low] ,称为插值计算公式。

时间复杂度O(logn)。

斐波那契查找
int Fibonacci_Search(int a[],int n,int key){
    
    
 //a数组为目标数组,n为a数组的长度,key为要在目标数组中具体查找的数据
 int low,hign,mid,i,k;
 k=0;
 //下面的F数组即斐波那契数列
 //0 1 1 2 3 5 8 13 21 34 ... 
 while(n>F[k]-1){
    
    
  k++;	//计算n位于斐波那契数列的位置
 } 
 for(i=n;i<F[k]-1;i++){
    
    
  a[i] = a[n];	//将不满的数值补全
 }
 
 while(low<=high){
    
    
  mid = low + F[k-1]-1;	//计算当前分隔的下标 
  if(key<a[mid]){
    
    	//查找记录小于当前分隔记录 
   high = mid-1;	//调整最高下标 
   k-=1;	//斐波那契数列下标减一位 
  }
  else if(key>a[mid]){
    
    //与上同理 
   low = mid+1;
   k-=2;
  }
  else{
    
    
   if(mid<=n){
    
    
    return mid;//若相当且mid小于n,则直接返回mid 
   }
   else
    return n;//若mid大于n,则说明是补全数值,返回n 
  }
 }
 return 0;
}

先计算n位于斐波那契数列的位置k,然后将不满的数列补全。当low<=high,mid=low+F[k-1]-1。若key<a[mid],high=mid-1,k=k-1;若key>a[mid],low=mid+1,k=k-2;若相等,若mid<=n,mid就是查找到的位置;若mid>n,n就是。

时间复杂度O(logn)。

线性索引查找

稠密索引

在线性索引中,将数据集中的每个记录对应一个索引项,索引项按照关键码有序的排列,这样在查找关键字时,可用前面的有序查找算法。

分块索引

分块有序,把数据集的记录分成了若干块,且满足块内无序、块间有序。
索引项三个数据项:
最大关键码、块个数、指向块首元素指针。

时间复杂度 最佳时(即块数与块内数相等)O( n \sqrt{n} n )。

倒排索引

索引表:次关键码和记录号表。
记录号表存储具有相同次关键字的所有记录的记录号。

二叉排序树

查找

Status SearchBST(BiTree T, int key, BiTree f, BiTree *p) 
{
    
          
	if (!T) /*  查找不成功 */    
	{
    
    
		*p = f;
		return FALSE;     
	}    
	else if (key==T->data) /*  查找成功 */    
	{
    
             
		*p = T;          
		return TRUE;     
	}     
	else if (key<T->data)         
		return SearchBST(T->lchild, key, T, p);  /*  在左子树中继续查找 */   
	else          
		return SearchBST(T->rchild, key, T, p);  /*  在右子树中继续查找 */} 

插入

Status InsertBST(BiTree *T, int key) 
{
    
          
	BiTree p,s;    
	if (!SearchBST(*T, key, NULL, &p)) /* 查找不成功 */    
	{
    
            
		s = (BiTree)malloc(sizeof(BiTNode));        
		s->data = key;          
		s->lchild = s->rchild = NULL;          
		if (!p)             
			*T = s;         /*  插入s为新的根结点 */        
		else if (key<p->data)             
			p->lchild = s;  /*  插入s为左孩子 */        
		else             
			p->rchild = s;  /*  插入s为右孩子 */        
		return TRUE;    
	}     
	else
        	return FALSE;  /*  树中已有关键字同样的结点。不再插入 */
}

删除

Status DeleteBST(BiTree *T,int key)
{
    
     
    if(!*T) /* 不存在关键字等于key的数据元素 */ 
        return FALSE;
    else
    {
    
    
        if (key==(*T)->data) /* 找到关键字等于key的数据元素 */ 
            return Delete(T);
        else if (key<(*T)->data)
            return DeleteBST(&(*T)->lchild,key);
        else
            return DeleteBST(&(*T)->rchild,key);

    }
}


Status Delete(BiTree *p)
{
    
    
    BiTree q,s;
    if((*p)->rchild==NULL) /* 右子树空则仅仅需重接它的左子树(待删结点是叶子也走此分支) */
    {
    
    
        q=*p; *p=(*p)->lchild; free(q);
    }
    else if((*p)->lchild==NULL) /* 仅仅需重接它的右子树 */
    {
    
    
        q=*p; *p=(*p)->rchild; free(q);
    }
    else /* 左右子树均不空 */
    {
    
    
        q=*p; s=(*p)->lchild;
        while(s->rchild) /* 转左,然后向右到尽头(找待删结点的前驱) */
        {
    
    
            q=s;
            s=s->rchild;
        }
        (*p)->data=s->data; /*  s指向被删结点的直接前驱(将被删结点前驱的值代替被删结点的值) */
        if(q!=*p)
            q->rchild=s->lchild; /*  重接q的右子树 */ 
        else
            q->lchild=s->lchild; /*  重接q的左子树 */
        free(s);
    }
    return TRUE;
}

平衡二叉树(AVL树)

高度平衡的二叉排序树。左右子树都是平衡二叉树,深度差不超过1。
距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树。
在这里插入图片描述

实现原理

构建二叉排序树时,每插入一个结点,检查是否因为插入破坏了平衡性,若是,找出最小不平衡子树;调整最小不平衡子树中各结点之间链接关系。

实现算法
typedef  struct BiTNode /* 结点结构 */
{
    
    
    int data;   /* 结点数据 */
    int bf; /*  结点的平衡因子 */ 
    struct BiTNode *lchild, *rchild;    /* 左右孩子指针 */
} BiTNode, *BiTree;
/* 对以p为根的二叉排序树作右旋处理。 */
/* 处理之后p指向新的树根结点。即旋转处理之前的左子树的根结点 */
void R_Rotate(BiTree *P)
{
    
     
    BiTree L;
    L=(*P)->lchild; /*  L指向P的左子树根结点 */ 
    (*P)->lchild=L->rchild; /*  L的右子树挂接为P的左子树 */ 
    L->rchild=(*P);
    *P=L; /*  P指向新的根结点 */ 
}

/* 对以P为根的二叉排序树作左旋处理, */
/* 处理之后P指向新的树根结点。即旋转处理之前的右子树的根结点0  */
void L_Rotate(BiTree *P)
{
    
     
    BiTree R;
    R=(*P)->rchild; /*  R指向P的右子树根结点 */ 
    (*P)->rchild=R->lchild; /* R的左子树挂接为P的右子树 */ 
    R->lchild=(*P);
    *P=R; /*  P指向新的根结点 */ 
}

#define LH +1 /*  左高 */ 
#define EH 0  /*  等高 */ 
#define RH -1 /*  右高 */ 

/*  对以指针T所指结点为根的二叉树作左平衡旋转处理 */
/*  本算法结束时,指针T指向新的根结点 */
void LeftBalance(BiTree *T)
{
    
     
    BiTree L,Lr;
    L=(*T)->lchild; /*  L指向T的左子树根结点 */ 
    switch(L->bf)
    {
    
     /*  检查T的左子树的平衡度,并作对应平衡处理 */ 
         case LH: /*  新结点插入在T的左孩子的左子树上,要作单右旋处理 */ 
            (*T)->bf=L->bf=EH;
            R_Rotate(T);
            break;
         case RH: /*  新结点插入在T的左孩子的右子树上。要作双旋处理 */ 
            Lr=L->rchild; /*  Lr指向T的左孩子的右子树根 */ 
            switch(Lr->bf)
            {
    
     /*  改动T及其左孩子的平衡因子 */ 
                case LH: (*T)->bf=RH;
                         L->bf=EH;
                         break;
                case EH: (*T)->bf=L->bf=EH;
                         break;
                case RH: (*T)->bf=EH;
                         L->bf=LH;
                         break;
            }
            Lr->bf=EH;
            L_Rotate(&(*T)->lchild); /*  对T的左子树作左旋平衡处理 */ 
            R_Rotate(T); /*  对T作右旋平衡处理 */ 
    }
}

/*  对以指针T所指结点为根的二叉树作右平衡旋转处理, */ 
/*  本算法结束时,指针T指向新的根结点 */ 
void RightBalance(BiTree *T)
{
    
     
    BiTree R,Rl;
    R=(*T)->rchild; /*  R指向T的右子树根结点 */ 
    switch(R->bf)
    {
    
     /*  检查T的右子树的平衡度。并作对应平衡处理 */ 
     case RH: /*  新结点插入在T的右孩子的右子树上。要作单左旋处理 */ 
              (*T)->bf=R->bf=EH;
              L_Rotate(T);
              break;
     case LH: /*  新结点插入在T的右孩子的左子树上,要作双旋处理 */ 
              Rl=R->lchild; /*  Rl指向T的右孩子的左子树根 */ 
              switch(Rl->bf)
              {
    
     /*  改动T及其右孩子的平衡因子 */ 
                case RH: (*T)->bf=LH;
                         R->bf=EH;
                         break;
                case EH: (*T)->bf=R->bf=EH;
                         break;
                case LH: (*T)->bf=EH;
                         R->bf=RH;
                         break;
              }
              Rl->bf=EH;
              R_Rotate(&(*T)->rchild); /*  对T的右子树作右旋平衡处理 */ 
              L_Rotate(T); /*  对T作左旋平衡处理 */ 
    }
}

/*  若在平衡的二叉排序树T中不存在和e有同样关键字的结点,则插入一个 */ 
/*  数据元素为e的新结点。并返回1,否则返回0。若因插入而使二叉排序树 */ 
/*  失去平衡,则作平衡旋转处理。布尔变量taller反映T长高与否。 */
Status InsertAVL(BiTree *T,int e,Status *taller)
{
    
      
    if(!*T)
    {
    
     /*  插入新结点。树“长高”,置taller为TRUE */ 
         *T=(BiTree)malloc(sizeof(BiTNode));
         (*T)->data=e; (*T)->lchild=(*T)->rchild=NULL; (*T)->bf=EH;
         *taller=TRUE;
    }
    else
    {
    
    
        if (e==(*T)->data)
        {
    
     /*  树中已存在和e有同样关键字的结点则不再插入 */ 
            *taller=FALSE; return FALSE;
        }
        if (e<(*T)->data)
        {
    
     /*  应继续在T的左子树中进行搜索 */ 
            if(!InsertAVL(&(*T)->lchild,e,taller)) /*  未插入 */ 
                return FALSE;
            if(taller) /*   已插入到T的左子树中且左子树“长高” */ 
                switch((*T)->bf) /*  检查T的平衡度 */ 
                {
    
    
                    case LH: /*  原本左子树比右子树高。须要作左平衡处理 */ 
                            LeftBalance(T); *taller=FALSE; break;
                    case EH: /*  原本左、右子树等高,现因左子树增高而使树增高 */ 
                            (*T)->bf=LH; *taller=TRUE; break;
                    case RH: /*  原本右子树比左子树高,现左、右子树等高 */  
                            (*T)->bf=EH; *taller=FALSE; break;
                }
        }
        else
        {
    
     /*  应继续在T的右子树中进行搜索 */ 
            if(!InsertAVL(&(*T)->rchild,e,taller)) /*  未插入 */ 
                return FALSE;
            if(*taller) /*  已插入到T的右子树且右子树“长高” */ 
                switch((*T)->bf) /*  检查T的平衡度 */ 
                {
    
    
                    case LH: /*  原本左子树比右子树高。现左、右子树等高 */ 
                            (*T)->bf=EH; *taller=FALSE; break;
                    case EH: /*  原本左、右子树等高,现因右子树增高而使树增高  */
                            (*T)->bf=RH; *taller=TRUE; break;
                    case RH: /*  原本右子树比左子树高,须要作右平衡处理 */ 
                            RightBalance(T); *taller=FALSE; break;
                }
        }
    }
    return TRUE;
}

多路查找树

每个结点的孩子数可多于2个,且每个结点处可存储多个元素。
2-3树、2-3-4树、B树、B+树

散列表查找(哈希表)

散列函数构造:直接定址法(线性函数)、数字分析法、平方取中法、折叠法、除留余数法、随机数法。

处理散列冲突:开放定址法、再散列函数法、链地址法、公共溢出区法。

猜你喜欢

转载自blog.csdn.net/Ethan_997/article/details/108630021