查找 学习笔记

查找结构:线性表,树表,散列表
性能:
设计到重要的相关概念:平均查找长度ASL
在这里插入图片描述
一、线性表的查找技术
1.顺序查找
从线性表的一段到另一端比较关键码,从而查找出相应的数据
可通过设置哨兵,来提高查找速度,不需要每趟都判断是否发生数组越界。
一般将哨兵放置于数组最开头的元素,将关键码即需查找的值赋给哨兵。
代码实现:

int LineSearch :: SeqSearch(int k)
{ 
    int i = length;        //从数组高端开始比较
    data[0] = k;           //设置哨兵
    while (data[i] != k) //不用判断下标i是否越界
        i--;
    return i; 
}

ASL(SU)=(n+1)/2
ASL(UNSU)=n+1;
若在单链表中实现顺序查找:
代码实现为:

int LinkSearch::SeqSearch2(Node *first, int k){  
	Node *p;
	int count=0;//记录比较的次数
	p=first->next; 
	int j=1;//记录数据在表中的位置
      while (p &&  p->data != k)
	{p=p->next;	j++;    count++;}
	if (!p){
             cout<<“查找失败,比较的次数为:"<<count<<endl; 	
             return 0;
     } else{
	    cout<<“\n”<<“查找成功,比较的次数为:"<<count<<endl; 	 
          return j;
	}

对于顺序查找,对于存储结构没有要求,且对一组数据的有序性也没有要求
但当数组中元素较多时,查找效率则会较低
因此对于有序的顺序表,可通过折半查找的方法来实现元素查找。
2.折半查找
在有序表中(low, high,low<=high),
取中间记录作为比较对象,
若给定值与中间记录的关键码相等,则查找成功;
若给定值小于中间记录的关键码,则在中间记录的左半区继续查找;
若给定值大于中间记录的关键码,则在中间记录的右半区继续查找。
不断重复上述过程,直到查找成功,或所查找的区域无记录,查找失败。
算法实现:
非递归算法:

int LineSearch :: BinSearch1(int k){
     int mid, low = 1, high = length; //初始查找区间是[1, n]
     while (low <= high) {//当区间存在时
          mid = (low + high) / 2; 
          if (k < data[mid]) 
              high = mid - 1;
          else if (k > data[mid]) 
               low = mid + 1; 
          else
               return mid; //查找成功,返回元素序号
      }
      return 0; //查找失败,返回0
}

递归算法:

int LineSearch :: BinSearch2(int low, int high, int k){
      if (low > high) 
          return 0; //递归的边界条件
      else {
         int mid = (low + high) / 2;
      if (k < data[mid]) 
           return BinSearch2(low, mid-1, k);
      else if (k > data[mid]) 
           return BinSearch2(mid+1, high, k); 
      else 
           return mid; //查找成功,返回序号
     }
}

折半查找判定树:
判定树:折半查找的过程可以用二叉树来描述,
树中的每个结点对应有序表中的一个记录,
结点的值为该记录在表中的位置。
通常称这个描述折半查找过程的二叉树为折半查找判定树,简称判定树。
在这里插入图片描述
构造方法:
⑴ 当n=0时,折半查找判定树为空;
⑵ 当n>0时,
折半查找判定树的根结点为mid=(n+1)/2,
根结点的左子树是与有序表r[1] ~ r[mid-1]相对应的折半查找判定树,
根结点的右子树是与r[mid+1] ~ r[n]相对应的折半查找判定树。
关于ASL:
以上图为例
ASL(SU)=(1+22+34+44)/11=33/11=3
ASL(UNSU)=( 3
4+4*8) /12
二、树表的查找技术:
1.二叉排序树
二叉排序树的性质:
⑴若它的左子树不空,则左子树上所有结点的值均小于根结点的值;
⑵若它的右子树不空,则右子树上所有结点的值均大于根结点的值;
⑶ 它的左右子树也都是二叉排序树。
二叉排序树的插入算法:
若二叉排序树为空树,则新插入的结点为新的根结点;
否则,如果插入的值比根节点值大,则在右子树中进行插入;否则,在左子树中进行插入。
递归。

BiNode *BiSortTree::InsertBST(BiNode *bt, int x)
{
if (bt == nullptr) { //找到插入位置
BiNode *s = new BiNode; 
s->data = x;
s->lchild = nullptr; s->rchild = nullptr;
bt = s;
return bt;
}
else if (bt->data > x) bt->lchild = InsertBST(bt->lchild, x);
else bt->rchild = InsertBST(bt->rchild, x);
}

通过插入方法,从而写出构造方法:

BiSortTree::BiSortTree(int a[ ], int n)
{
root = nullptr;
for (int i = 0; i < n; i++)
root = InsertBST(root, a[i]);
}

二叉排序树的删除:
由于二叉排序树的特殊性质,执行删除操作时,可分为以下三种情况:
被删除的结点是叶子;
被删除的结点只有左子树或者只有右子树;
被删除的结点既有左子树,也有右子树。
当删除叶子节点时,可直接将该节点赋空
当被删除的节点只有左子树或右子树时:
将双亲结点的相应指针域的值指向被删除结点的左子树或右子树。
当被删除的节点既有左子树又有右子树时:
以其后继(右子树中的最小值)替代之,然后再删除该前驱结点。
具体实现方法:

  1. 若结点p是叶子,则直接删除结点p;
  2. 若结点p只有左子树,则只需重接p的左子树;
    若结点p只有右子树,则只需重接p的右子树;
  3. 若结点p的左右子树均不空,则
    3.1 查找结点p的右子树上的最左下结点s及s双亲结点par;
    3.2 将结点s数据域替换到被删结点p的数据域;
    3.3 若结点p的右孩子无左子树,
    则将s的右子树接到par的右子树上;
    否则,将s的右子树接到结点par的左子树上;
    3.4 删除结点s;
    代码实现:
void BiSortTree::DeleteBST(BiNode<int> *p, BiNode<int> *f ) {
	 if (!p->lchild && !p->rchild) 	{   
              if(f->child==p)        f->lchild= NULL;  
              else  f->lchild= NULL; 
              delete p;
	  }
	 else if (!p->rchild) {     //p只有左子树
             if(f->child==p)   f->lchild=p->lchild;
             else f->rchild=p->lchild;
	               delete p;
	 }
	 else if (!p->lchild) {   //p只有右子树
		 if(f->child==p)  f->lchild=p->rchild;
		 else f->rchild=p->rchild;
            delete p;
        	}
        	else {   //左右子树均不空
             par=p;  s=p->rchild;  
             while (s->lchild!=NULL)   //查找最左下结点
             {
               par=s;
               s=s->lchild;
             }
             p->data=s->data;
             if (par==p) p->rchild=s->rchild;  //处理特殊情况
                 else par->lchild=s->rchild;    //一般情况
             delete s;
           } //左右子树均不空的情况处理完毕
 }


二叉排序树的查找:
采用递归方法
⑴ 若root是空树,则查找失败;
⑵ 若k=root->data,则查找成功;否则
⑶ 若k<root->data,则在root的左子树上查找;否则
⑷ 在root的右子树上查找。
上述过程一直持续到k被找到或者待查找的子树为空,如果待查找的子树为空,则查找失败。

BiNode *BiSortTree::SearchBST(BiNode<int> *root, int k)
{
    if (root==NULL)
    return NULLelse if (root->data==k) 
              return root;
    else if (k<root->data) 
              return SearchBST(root->lchild, k);
    else 
	         return SearchBST(root->rchild, k);
}

2.平衡二叉树(AVL树)
平衡二叉树:或者是一棵空的二叉排序树,或者是具有下列性质的二叉排序树:
⑴ 根结点的左子树和右子树的深度最多相差1;
⑵ 根结点的左子树和右子树也都是平衡二叉树。
平衡因子:结点的平衡因子是该结点的左子树的深度与右子树的深度之差。
结点的平衡因子=HL-HR
三、散列表的查找技术
散列函数的构造:
直接定址法
除留余数法
数字分析法
平方取中法
折叠法(分段叠加法)
冲突处理方法:
开放定址法
链地址法
建立公共溢出区
散列的基本思想:在记录的存储地址和它的关键码之间建立一个确定的对应关系。这样,不经过比较,一次读取就能得到所查元素的查找方法。
散列函数
设计散列函数一般应遵循以下原则:
⑴ 计算简单。散列函数不应该有很大的计算量,否则会降低查找效率。
⑵ 函数值即散列地址分布均匀。函数值要尽量均匀散布在地址空间,这样才能保证存储空间的有效利用并减少冲突。
例如:
H(key) = a * key + b (a,b为常数)
除留余数法:H(key)=key mod p
数字分析法:根据关键码在各个位上的分布情况,选取分布比较均匀的若干位组成散列地址。
平方取中法:对关键码平方后,按散列表大小,取中间的若干位作为散列地址(平方后截取)。
折叠法:将关键码从左到右分割成位数相等的几部分,将这几部分叠加求和,取后几位作为散列地址。
冲突的处理:
1.开放定址法
用开放定址法处理冲突得到的散列表叫闭散列表。
线性探测法
当发生冲突时,从冲突位置的下一个位置起,依次寻找空的散列地址。
Hi=(H(key)+di) % m (di=1,2,…,m-1)
2.拉链法(链地址法)
用拉链法处理冲突构造的散列表叫做开散列表。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值