C语言实现七大查找算法(一) |
您所在的位置:网站首页 › c语言查找指定字符 › C语言实现七大查找算法(一) |
本文主要介绍数据结构中的查找算法,主要介绍顺序查找、折半查找(二分查找)、树表查找、分块查找、哈希查找(散列)。 其他的一些查找算法也会有所介绍。 查找(Searching)就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素。 查找表(Search Table):由同一类型的数据元素构成的集合关键字(Key):数据元素中某个数据项的值,又称为键值主键(Primary Key):可唯一的标识某个数据元素或记录的关键字查找表按照操作方式可分为: **静态查找表(Static Search Table):**只做查找操作的查找表。它的主要操作是: 查询某个“特定的”数据元素是否在表中检索某个“特定的”数据元素和各种属性 **动态查找表(Dynamic Search Table):**在查找中同时进行插入或删除等操作: 查找时插入数据查找时删除数据**平均查找长度(Average Search Length,ASL):**在所有的查找过程中进行关键字的比较次数的平均值。 对于含有n个数据元素的查找表,查找成功的平均查找长度计算公式如下: A S L = ∑ i = 1 n P i C i ASL = \sum_{i=1}^{n}P_{i}C_{i} ASL=i=1∑nPiCi P i P_i Pi:查找表中第i个数据元素的概率,一般等概率为 1 n \frac{1}{n} n1 C i C_i Ci:找到第i个数据元素时已经比较过的次数。 顺序查找算法简介 顺序查找又称为线性查找,是一种最简单的查找方法。适用于线性表的顺序存储结构和链式存储结构。时间复杂度为 O ( n ) O(n) O(n)。 基本思路 从第一个元素m开始逐个与需要查找的元素x进行比较,当比较到元素值相同(即m=x)时返回元素m的下标,如果比较到最后都没有找到,则返回-1。 优缺点 缺点:当n 很大时,平均查找长度较大,效率低;优点:对表中数据元素的存储没有要求。另外,对于线性链表,只能进行顺序查找。算法实现 //顺序查找,n为数组a的长度 int SequenceSearch(int a[], int value, int n) { int i; for(i=0; iR,则搜索以失败告终 ; 3、 令 m (中间值元素)为 ⌊(L+R)/2⌋ (向下取整); 4、 如果 A m ; T A_m;T AmT,令 R为 m - 1 并回到步骤二;注意: 折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用。 算法实现 //二分查找(折半查找),一般方法,n为数组a的长度 int BinarySearch1(int a[], int value, int n) { int low, high, mid; low = 0; high = n-1; while(lowvalue) high = mid-1; //从前半部分查找 else low = mid+1; //从后半部分查找 } return -1; } //二分查找,递归方法 int BinarySearch2(int a[], int value, int low, int high) { if(low>high) return -1; int mid = (low+high)/2; if(a[mid]==value) return mid; else if(a[mid]>value) return BinarySearch2(a, value, low, mid-1); else return BinarySearch2(a, value, mid+1, high); } 插值查找算法简介 在介绍插值查找之前,首先考虑一个新问题,为什么上述算法一定要是折半,而不是折四分之一或者折更多呢? 打个比方,在英文字典里面查“apple”,你下意识翻开字典是翻前面的书页还是后面的书页呢?如果再让你查“zoo”,你又怎么查?很显然,这里你绝对不会是从中间开始查起,而是有一定目的的往前或往后翻。 经过以上分析,折半查找这种查找方式,不是自适应的(也就是说是傻瓜式的)。二分查找中查找点计算如下: m i d = ( l o w + h i g h ) / 2 , 即 m i d = l o w + 1 / 2 ( h i g h − l o w ) mid=(low+high)/2, 即mid=low+1/2(high-low) mid=(low+high)/2,即mid=low+1/2(high−low) 通过类比,我们可以将查找的点改进为如下: mid=low+(key-a[low])/(a[high]-a[low])*(high-low), 也就是将上述的比例参数1/2改进为自适应的,根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。 查找成功或者失败的时间复杂度均为 O ( l o g 2 ( l o g 2 n ) ) O(log_2(log_2^n)) O(log2(log2n)),最坏情况可能需要 O ( n ) O(n) O(n)。 算法思想 基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,插值查找也属于有序查找。 注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。 算法实现 //插值查找,类似二分查找,只是mid计算方式不同,此处给出递归方法,一般方法也和上述二分查找类似 int InsertionSearch(int a[], int value, int low, int high) { if(low>high) return -1; int mid = low+(value-a[low])/(a[high]-a[low])*(high-low); if(a[mid]==value) return mid; else if(a[mid]>value) return InsertionSearch(a, value, low, mid-1); else return InsertionSearch(a, value, mid+1, high); } 分块查找算法简介 要求是顺序表,分块查找又称索引顺序查找,它是顺序查找的一种改进方法。 时间复杂度: O ( l o g ( m ) + n / m ) O(log(m)+n/m) O(log(m)+n/m) 算法思想 将n个数据元素"按块有序"划分为m块(m ≤ n)。每一块中的结点不必有序,但块与块之间必须"按块有序";即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;而第2块中任一元素又都必须小于第3块中的任一元素,……算法流程 先选取各块中的最大关键字构成一个索引表;查找分两个部分:先对索引表进行二分查找或顺序查找,以确定待查记录在哪一块中;在已确定的块中用顺序法进行查找。平均查找长度 将长度为n的查找表均匀分为m块,每块有s个记录,在等概率的情况,平均查找长度计算如下: 索引和块内均用顺序查找 A S L = m + 1 2 + s + 1 2 = s 2 + 2 s + n 2 s , ( m s = n ) ASL= \frac{m+1}{2} + \frac{s+1}{2} = \frac{s^2+2s+n}{2s},(ms=n) ASL=2m+1+2s+1=2ss2+2s+n,(ms=n) 特殊的 当 s = n 时 , A S L m i n = n + 1 当 s =\sqrt{n}时,ASL_{min} = \sqrt{n}+1 当s=n 时,ASLmin=n +1 索引折半查找,块内顺序查找 A S L = ⌈ l o g 2 ( m + 1 ) ⌉ + s + 1 2 ASL = \left \lceil log_2^{(m+1)} \right \rceil + \frac{s+1}{2} ASL=⌈log2(m+1)⌉+2s+1 斐波那契查找这部分大致知道过程,先记录下来,以后有时间在认真研究一下=-=,主要参考百度百科 算法简介 斐波那契数列,又称黄金分割数列,指的是这样一个数列: 1 、 1 、 2 、 3 、 5 、 8 、 13 、 21 、 ⋅ ⋅ ⋅ ⋅ 1、1、2、3、5、8、13、21、···· 1、1、2、3、5、8、13、21、⋅⋅⋅⋅,在数学上,斐波那契被递归方法如下定义: F ( 1 ) = 1 , F ( 2 ) = 1 , F ( n ) = f ( n − 1 ) + F ( n − 2 ) ( n ; = 2 ) F(1)=1,F(2)=1,F(n)=f(n-1)+F(n-2) (n;=2) F(1)=1,F(2)=1,F(n)=f(n−1)+F(n−2)(n>=2)。该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)。 黄金比例又称黄金分割,是指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1。 斐波那契查找的时间复杂度还是 O ( l o g 2 n ) O(log_2^n ) O(log2n),但是与折半查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,而除法比加减法要占用更多的时间,因此,斐波那契查找的运行时间理论上比折半查找小,但是还是得视具体情况而定。 算法思想 也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。 相对于折半查找,一般将待比较的key值与第 m i d = ( l o w + h i g h / 2 mid=(low+high/2 mid=(low+high/2位置的元素比较,比较结果分三种情况: 相等,mid位置的元素即为所求;大于, l o w = m i d + 1 low=mid+1 low=mid+1;小于, h i g h = m i d − 1 high=mid-1 high=mid−1。斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。要求开始表中记录的个数为某个斐波那契数小1,及 n = F ( k ) − 1 n=F(k)-1 n=F(k)−1; 开始将k值与第 F ( k − 1 ) F(k-1) F(k−1)位置的记录进行比较(及 m i d = l o w + F ( k − 1 ) − 1 mid=low+F(k-1)-1 mid=low+F(k−1)−1),比较结果也分为三种 相等,mid位置的元素即为所求大于, l o w = m i d + 1 , k − = 2 low=mid+1,k-=2 low=mid+1,k−=2;说明: l o w = m i d + 1 low=mid+1 low=mid+1说明待查找的元素在 [ m i d + 1 , h i g h ] [mid+1,high] [mid+1,high]范围内, k − = 2 k-=2 k−=2 说明范围 [ m i d + 1 , h i g h ] [mid+1,high] [mid+1,high]内的元素个数为 n − ( F ( k − 1 ) ) = F ( k ) − 1 − F ( k − 1 ) = F ( k ) − F ( k − 1 ) − 1 = F ( k − 2 ) − 1 n-(F(k-1))= F(k)-1-F(k-1)=F(k)-F(k-1)-1=F(k-2)-1 n−(F(k−1))=F(k)−1−F(k−1)=F(k)−F(k−1)−1=F(k−2)−1个,所以可以递归的应用斐波那契查找。 小于, h i g h = m i d − 1 , k − = 1 high=mid-1,k-=1 high=mid−1,k−=1。说明: l o w = m i d + 1 low=mid+1 low=mid+1说明待查找的元素在 [ l o w , m i d − 1 ] [low,mid-1] [low,mid−1]范围内, k − = 1 k-=1 k−=1 说明范围 [ l o w , m i d − 1 ] [low,mid-1] [low,mid−1]内的元素个数为 F ( k − 1 ) − 1 F(k-1)-1 F(k−1)−1个,所以可以递归 的应用斐波那契查找。 n = F ( k ) − 1 n=F(k)-1 n=F(k)−1, 表中记录的个数为某个斐波那契数小1。这是为什么呢? 是为了格式上的统一,以方便递归或者循环程序的编写。表中的数据是 F ( k ) − 1 F(k)-1 F(k)−1个,使用 m i d mid mid值进行分割又用掉一个,那么剩下 F ( k ) − 2 F(k)-2 F(k)−2个。正好分给两个子序列,每个子序列的个数分别是 F ( k − 1 ) − 1 F(k-1)-1 F(k−1)−1与 F ( k − 2 ) − 1 F(k-2)-1 F(k−2)−1个,格式上与之前是统一的。不然的话,每个子序列的元素个数有可能是 F ( k − 1 ) , F ( k − 1 ) − 1 , F ( k − 2 ) , F ( k − 2 ) − 1 F(k-1),F(k-1)-1,F(k-2),F(k-2)-1 F(k−1),F(k−1)−1,F(k−2),F(k−2)−1个,写程序会非常麻烦。 算法举例 对于斐波那契数列: 1 、 1 、 2 、 3 、 5 、 8 、 13 、 21 、 34 、 55 、 89 … … 1、1、2、3、5、8、13、21、34、55、89…… 1、1、2、3、5、8、13、21、34、55、89……(也可以从0开始),前后两个数字的比值随着数列的增加,越来越接近黄金比值0.618。比如这里的89,把它想象成整个有序表的元素个数,而89是由前面的两个斐波那契数34和55相加之后的和,也就是说把元素个数为89的有序表分成由前55个数据元素组成的前半段和由后34个数据元素组成的后半段,那么前半段元素个数和整个有序表长度的比值就接近黄金比值0.618,假如要查找的元素在前半段,那么继续按照斐波那契数列来看,55 = 34 + 21,所以继续把前半段分成前34个数据元素的前半段和后21个元素的后半段,继续查找,如此反复,直到查找成功或失败,这样就把斐波那契数列应用到查找算法中了。 从图中可以看出,当有序表的元素个数不是斐波那契数列中的某个数字时,需要把有序表的元素个数长度补齐,让它成为斐波那契数列中的一个数值,当然把原有序表截断肯定是不可能的,不然还怎么查找。然后图中标识每次取斐波那契数列中的某个值时(F[k]),都会进行-1操作,这是因为有序表数组位序从0开始的,纯粹是为了迎合位序从0开始。 算法实现 // 斐波那契查找.cpp #include "stdafx.h" #include #include using namespace std; const int max_size=20;//斐波那契数组的长度 /*构造一个斐波那契数组*/ void Fibonacci(int * F) { F[0]=0; F[1]=1; for(int i=2;iF[k]-1)//计算n位于斐波那契数列的位置 ++k; int * temp;//将数组a扩展到F[k]-1的长度 temp=new int [F[k]-1]; memcpy(temp,a,n*sizeof(int)); for(int i=n;i |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |