首页 > 编程语言 >算法:查找

算法:查找

时间:2024-10-29 17:47:14浏览次数:7  
标签:折半 哈希 关键码 int 算法 查找 key

算法

查找是非数值数据处理中一种基本运算,查找运算的效率与查找表所采用的数据结构和查找方法密切相关。查找表是指由同一类型的数据元素(或记录)构成的集合。由于“集合”中的数据元素之间存在着完全松散的关系,可以将查找表构造为静态的或者动态的。
根据对表中数据的处理有:

  1. 静态查找表:不进行修改表结构的操作。如查询某个特定的数据元素是否在查找表中;检索某个特定的数据元素的属性。
  2. 动态查找表:是动态变化的,除了查询和检索,对查找表要进行的操作有:插入一个数据元素和删除一个数据元素。

表中数据的查找通过关键码进行区分。关键码是数据元素(或记录)的某个数据项的值,用它来识别(标识)这个数据元素(或记录)。主关键码是指能唯一标识一个数据元素的关键码。次关键码是指能标识多个数据元素的关键码。
查找算法的基本操作:将记录的关键码与给定值进行比较。
对于含有n个记录的表,查找成功时的平均查找长度定义为:
A S L = ∑ i = 1 n P i C i ASL=\sum_{i=1}^nP_iC_i ASL=i=1∑n​Pi​Ci​
P i P_i Pi​为表中第i个记录进行查找的概率,且 ∑ i = 1 n P i = 1 \sum_{i=1}^nP_i=1 ∑i=1n​Pi​=1; C i C_i Ci​为找到表中其关键码与给定值相等的记录时和给定值已进行过比较的关键码个数。

1. 顺序查找和折半查找

当仅在查找表中进行查找运算而不修改查找表时,可先构造一个静态查找表,然后进行顺序查找或折半查找等操作。

1.1 顺序查找

顺序查找的方法对于顺序查找和链式查找存储方式的线性表都适用。
在等概率情况下,顺序查找成功的平均查找长度是:
A S L s s = ∑ i = 1 n P i C i = 1 n ∑ i = 1 n ( n − i + 1 ) = n + 1 2 ASL_{ss}=\sum_{i=1}^nP_iC_i=\frac{1}{n} \sum_{i=1}^n(n-i+1)=\frac{n+1}{2} ASLss​=i=1∑n​Pi​Ci​=n1​i=1∑n​(n−i+1)=2n+1​

成功查找的平均比较次数约为表长的一半。若所查记录不在表中,则至少进行n次比较才能确定查找失败。与

  • 缺点:顺序査找方法在"值较大时,其平均查找长度较大,查找效率较低。
  • 优点:算法简单且适应面广,对查找表的存储结构没有要求,无论记录是否按关键码有序排列均可应用。

1.2 折半查找

折半查找也称为二分查找,该方法是将给定值与中间位置记录的关键码比较,若相等,则查找成功:若不等,则缩小范围,直至新的査找区间中间位置记录的关键码等于给定值或者查找区间没有元素时(表明查找不成功)为止。

  • 使用范围:表中的元素已经按关键码排序。
  1. 整型数组折半查找
int Bsearsh_1(int r[], int low, int high, int key)
/*元素存储在r[low, high],用折半查找的方法在数组r中找值为key的元素*/
/*若找出则返回元素的下标,否则返回-1*/
{
	int mid;
	while(low<=high){
		mid=(low+high)/2;
		if(key==r[mid]) return mid;
		else if(key<r[mid]) high = mid-1;
			else low=high+1;
	}
	return -1;
}
  1. 整型数组折半查找递归算法
int Bsearsh_rec(int r[], int low, int high, int key)
{
	int mid;
	if(low<high){
		mid=(low+high)/2;
		if(key==r[mid]) return mid;
		else if(key<r[mid]) return  Bsearsh_rec(r, low, mid-1, key);
			else return  Bsearsh_rec(r, mid+1, high, key);
	}
	return -1;
}

折半查找可以理解为折半查找树。以当前查找区间的中间位置上的记录为根,左子表和右子表中的记录分贝作为根的左子树和右子树结点。
在这里插入图片描述
具有n个结点的判定树的高度为 [ l o g 2 n ] + 1 [log_2n]+1 [log2​n]+1,所以折半查找在查找成功时和给定值进行比较的关键码个数至多为 [ l o g 2 n ] + 1 [log_2n]+1 [log2​n]+1。
折半查找的平均查找长度为:
A S L b s = ∑ j = 1 n P i C i = 1 n ∑ j = 1 n j × 2 j − 1 = n + 1 n l o g 2 ( n + 1 ) − 1 ASL_{bs}=\sum_{j=1}^nP_iC_i=\frac{1}{n}\sum_{j=1}^nj\times2^{j-1}=\frac{n+1}{n}log_2(n+1)-1 ASLbs​=j=1∑n​Pi​Ci​=n1​j=1∑n​j×2j−1=nn+1​log2​(n+1)−1
当n值较大时,
A S L b s ≈ l o g 2 ( n + 1 ) − 1 ASL_{bs} \approx log_2(n+1)-1 ASLbs​≈log2​(n+1)−1

  • 折半查找的效率很高,但是查找表需要采用顺序存储并且关键码要有序排列。折半查找适用于查找不易变动,且又经常进行查找的情况。

1.3 索引顺序查找

在这里插入图片描述
在分块查找过程中,首先将查找表分成若干块,每一块中关键码不一定有序,但块之间是有序的,即后一块中所有记录的关键码均大于前一个块中最大的关键码。此外,还建立了一个’索引表”,索引表的元素按关键码有序排列。因此,查找过程分为两步:第-步在索引表中确定待查记录所在的块;第二步在块内顺序查找。
平均查找长度为:
A S L b s = 1 2 ( n s + s ) + 1 ASL_{bs} = \frac{1}{2}(\frac{n}{s}+s)+1 ASLbs​=21​(sn​+s)+1
其中,b为分的块数;s为每块内的元素数量。当s取 n \sqrt{n} n ​时, A S L b s ASL_{bs} ASLbs​取最小值 n + 1 \sqrt{n}+1 n ​+1

2. 树表查找

将查找表组织为一种树状结构时,则为树表查找。
非空的二叉查找树中左子树上所有结点的关键码均小于根结点的关键码,右子树上所有结点的关键码均大于根结点的关键码,因此,可将二叉查找树看成是一个有序表,其查找过程与折半查找过程相似。
二叉树数据结构及存储

typedef struct Tnode{
	int key;
	struct Tnode *lchild,*rchild;
}BSTnode,*Bitree;

2.1 查找

Bitree searchBST(Bitree root, int thekey, Bitree *father)
/*在root指向根的二叉查找树中查找键值为key的结点*/
/*若找到则返回所指向的节点指针,否则返回NULL,并向father带回父节点的指针*/
{	Bitree p=root; *father = NULL;
	While(p&&p->key!=thekey){
	*father = p;
	if(thekey<p->key) p=p->lchild;
	else p=p->rchild;
	}
	return p;
}

2.2 插入

构造二叉树:关键码序列 { 46 , 25 , 54 , 13 , 29 , 91 } \{46,25,54,13,29,91\} {46,25,54,13,29,91},则其构造的过程为:
在这里插入图片描述
代码实现:
二叉查找树的插入是不断向下延展的,不会在中间结点位置插入。
此代码是在查找的基础之上进行的实现。

Bitree insertBST(Bitree *root, int newkey)
/*在*root指向根的二叉查找树中插入一个键值为newkey的结点,若插入成功则返回0,否则返回-1*/
{	Bitree s,p,father;
	s = (BSTnode *)malloc(sizeof(BSTnode));
	if(!s) return -1;
	s->key = newkey;s->lchild =NULL;s->rchild = NULL;
	P = searchBST(*root,newkey,&father);  //查找插入位置
	if(p) return -1;                      //键值为newkey的结点已在树中,不再插入
	if(!father) *root=s;                  //若为空树,键值为newkey的结点为树根
	else if(newkey <father->key) father->lchild = s;
		else  father->rchild = s;
	return 0;
}

另外,由于一棵二叉查找树的形态完全由输入序列决定,所以在输入序列已经有序的情况下,所构造的二又查找树是一棵单枝树。从二又查找树的查找过程可知,这种情况下的査找效率和顺序查找的效率相当。

3. 哈希表及哈希查找

散列表结合使用。

顺序查找、折半查找和二叉查找表的存储位置和关键码之间不存在确定的关系,查找过程要通过系列对关键码的比较后才能确定记录在表中的位置。
理想情况是依据记录的关键码直接得到其对应的存储位置,即要求记录的关键码与其存储位置之间存在一一对应关系,从而快速地找到记录。

3.1 哈希造表

根据设定的哈希函数 Hash(key)和处理冲突的方法,将一组关键码映射到一个有限的连续的地址集(区间)上,并以关键码在地址集中的“像”作为记录在表中的存储位置,这种表称为哈希表,这一映射过程称为哈希造表或散列,所得的存储位置称为哈希地址或散列地址。

利用哈希函数来对应关键码和存储位置,必须做到一一对应,每次映射的结果是统一的
采用哈希法需要解决两个问题:

  1. 哈希函数的构造;
  2. 冲突的解决。
  • 冲突
    对于某个哈希函数 Hash 和两个关键码 K 1 K_1 K1​和 K 2 K_2 K2​,如果 K 1 ≠ K 2 K_1 \ne K_2 K1​=K2​,而 H a s h ( K 1 ) = H a s h ( K 2 ) Hash(K_1)=Hash(K_2) Hash(K1​)=Hash(K2​),则称出现了冲突,对该哈希函数来说, K 1 K_1 K1​和 K 2 K_2 K2​称为同义词。

3.2 处理冲突

处理冲突就是为出现冲突的关键码找到另一个空的哈希地址。
常见的处理冲突的方法有开放定址法、链地址法(拉链法)、再哈希法、建立公共溢出区法等,在处理冲突的过程中,可能得到一个地址序列,记为 H i ( i = 1 , 2 , . . . , k ) H_i(i=1,2,...,k) Hi​(i=1,2,...,k)。

开放定址法

H i = ( H a s h ( k e y ) + d i ) % m i = 1 , 2 , . . . , k ( k ≤ m − 1 ) \begin{matrix} H_i=(Hash(key)+d_i)\%m & i=1,2,...,k(k\le{m-1}) \end{matrix} Hi​=(Hash(key)+di​)%m​i=1,2,...,k(k≤m−1)​
其中,Hash(key)为哈希函数;m为哈希表的表长; d i d_i di​为增量序列。常见增量序列有:

  1. d i = 1 , 2 , . . . , m − 1 d_i=1,2,...,m-1 di​=1,2,...,m−1,称为线性探测再散列。
  2. d i = 1 2 , − 1 2 , 2 2 , − 2 2 , 3 2 , . . . , − k 2 ( k ≤ m 2 ) d_i=1^2,-1^2,2^2,-2^2,3^2,...,-k^2(k\le{\frac{m}{2}}) di​=12,−12,22,−22,32,...,−k2(k≤2m​),称为二次探测再散列。
  3. d i = 伪随机序列 d_i=伪随机序列 di​=伪随机序列,称为随机探测再散列。

最简单的就是进行线性探测,发生冲突时顺序的存储到下一个单元进行探测,直到找到空闲的地址。
在这里插入图片描述
在这里插入图片描述
线性探测法可能使第i个哈希地址的同义词存入第 +1 个哈希地址,这样本应存入第 i+1个哈希地址的元素变成了第 i+2 个哈希地址的同义词,……,因此,可能出现很多元素在相邻的哈希地址上“聚集”起来的现象,大大降低了查找效率。为此,可采用二次探测法或随机探测再散列法,以降低“聚集”现象。

链地址法

链地址法是一种经常使用且很有效的方法。它将具有相同哈希函数值的记录组织成一个链表,当链域的值为 NUIL 时,表示已没有后继记录。
例如,哈希表表长为 11、哈希函数为 H a s h ( k e y ) = k e y m o d    11 Hash(key)=key \mod 11 Hash(key)=keymod11,对于关键码序列 47,34,13,12,52,38,33,27,3,使用链地址法构造的哈希表。
在这里插入图片描述

3.3 哈希查找

在线性探测法解决冲突的哈希表中进行查找的过程如下:

  1. 先根据哈希函数计算出元素的哈希地址。
  2. 若是空单元,说明查找不成功,可结束查找:若不是空单元,则与该单元中的元素进行比较。
  3. 若相等,则查找成功并结束查找,否则,计算出下一个哈希地址,转(2)。

在用链地址法解决冲突构造的哈希表中查找元素,就是根据哈希函数得到元素所在链表的头指针,然后在链表中进行顺序查找即可。

标签:折半,哈希,关键码,int,算法,查找,key
From: https://blog.csdn.net/qingttqing/article/details/143307517

相关文章

  • 解码小红书CES算法,让你的笔记阅读量提升100%
    随着社交媒体成为日常生活的一部分,内容创作者们都在积极寻找提高作品可见性的方法。作为社交分享领域的佼佼者,小红书凭借其独特的CES算法机制,在众多平台中脱颖而出。本文将深入探讨小红书的CES算法工作原理,并提供实用技巧,帮助你显著提升笔记的阅读量。一、小红书CES算法解......
  • (算法)最⻓公共⼦序列————<动态规划>
    1.题⽬链接:1143.最⻓公共⼦序列2.题⽬描述:3.解法(动态规划):算法思路:1.状态表⽰:对于两个数组的动态规划,我们的定义状态表⽰的经验就是:        i.选取第⼀个数组[0,i]区间以及第⼆个数组[0,j]区间作为研究对象;        ii.结合题⽬要求,定义状态......
  • 点云学习笔记4——点云滤波降采样后进行4PCS粗配准【四点一致集配准算法(4-Point Congr
    #include<iostream>#include<pcl/point_cloud.h>#include<pcl/point_types.h>#include<pcl/filters/voxel_grid.h>#include<pcl/common/common_headers.h>#include<pcl/io/pcd_io.h>#include<pcl/visualization/cloud_vi......
  • 【回溯算法】(第七篇)
    目录⼦集(medium)题目解析讲解算法原理编写代码找出所有⼦集的异或总和再求和(easy)题目解析讲解算法原理编写代码⼦集(medium)题目解析1.题目链接:.-力扣(LeetCode)2.题目描述给你⼀个整数数组nums,数组中的元素互不相同。返回该数组所有可能的⼦集(幂集)。解集不能包......
  • 【综合算法练习】(第八篇)
    目录全排列Ⅱ(medium)题目解析讲解算法原理编写代码电话号码的字⺟组合(medium)题目解析讲解算法原理编写代码全排列Ⅱ(medium)题目解析1.题目链接:.-力扣(LeetCode)2.题目描述给定⼀个可包含重复数字的序列nums,按任意顺序返回所有不重复的全排列。•⽰例1:输⼊:num......
  • 百度二面算法:合法的括号字符串
    目录标题1.题目1.1示例2.利用栈求解2.1代码结构分析2.1.1代码优缺点1.题目给定一个字符串s,字符串s只包含以下三种字符:(,*,),请你判断s是不是一个合法的括号字符串。合法括号字符串有如下规则:左括号’(‘必须有对应的右括号’)’右括号’)‘必须有对应的左括号......
  • 算法与数据结构——计数排序
    计数排序计数排序(countingsort)通过统计元素数量来实现排序,通常应用于整数数组。简单实现给定一个长度为n的数组nums,其中的元素都是“非负整数”,计数排序的整体流程如下:遍历数组,找出其中最大的数组,记为m,然后创建一个长度为m+1的辅助数组counter。借助counter统计nums中各......
  • 抖音中aBogus签名算法的纯Python代码实现(2024年10月)
    目前网上的aBogus签名算法都是用python里execjs来执行js代码计算的,这种方法虽然可以达到计算签名值的结果,但是性能不高。本文直接将aBogus的js的源码改成python代码,同样的参数,计算的结果和js版本一样。附python源码importjsonfromrandomimportchoicefromrandomimport......
  • 【C++】—— priority_queue :平衡效率与秩序的算法利器
    去感受一棵草、一缕风、一场日落,去重新触摸真正的生活。——高盛元目录1、优先级队列1.1什么是优先级队列1.2 priority_queue的使用1.3仿函数2、priority_queue的模拟实现2.1整体框架接口2.2插入&&向上调整2.2删除&&向下调整2.3其他接口2.4优先级队列的应用......
  • Excel-多表数据查找匹配(VLOOKUP)
    ......