970 likes | 1.1k Views
搜索初探. Cs 2.6 1040310615, simple, Swai. 算法的时间复杂性. 百鸡问题 公鸡每只五元,母鸡每只三元,小鸡三只一元用 n 元买 n 只鸡,求公鸡,母鸡,小鸡的只数 约束方程: a+b+c=n 5a+3b+c/3=n. 输入:所购买三种鸡的总数目 n 输出:满足问题的解的数目 k, 公鸡 , 母鸡 , 小鸡的只数 g[], m[], s[] 1.void chicken_question(int n, int &k, int g[], int m[], int s[]) 2.{ int a, b, c;
E N D
搜索初探 Cs 2.6 1040310615, simple, Swai
算法的时间复杂性 • 百鸡问题 公鸡每只五元,母鸡每只三元,小鸡三只一元用n元买n只鸡,求公鸡,母鸡,小鸡的只数 约束方程: a+b+c=n 5a+3b+c/3=n
输入:所购买三种鸡的总数目n • 输出:满足问题的解的数目k,公鸡,母鸡,小鸡的只数g[], m[], s[] 1.void chicken_question(int n, int &k, int g[], int m[], int s[]) 2.{ • int a, b, c; • k=0; • for(a=0;a<=n;a++){ • for(b=0;b<=n;b++){ • for(c=0;c<=n;c++){ 8. if((a+b+c==n)&&(5*a+3*b+c/3==n)&&(c%3==0)){ • g[k]=a; • m[k]=b; • s[k]=c; • k++; • } • } • } • } 17. }
下面考虑这个算法花费的时间T1(n) 第4行需执行1个操作;第5行需执行1+2(n+1)个操作;第6行需执行n+1+2(n+1)2个操作;第7行需执行(n+1)2+2(n+1)3;第8行需执行14(n+1)3个操作;第9,10,11,12行循环各执行一个操作,执行与否取决于第8行的条件语句,所以这四行的执行时间不会超过4(n+1)3因此,T1(n)<= 1+2(n+1)+n+1+2(n+1)2+(n+1)2+16(n+1)3+4(n+1)3=20n3+63n2+69n+27 当n很大时,T1(n) ≈c1n3 c1>0 当n=10000时,约需11天零13小时,显然这个算法不能满足我们的要求!
改进的百鸡问题 • 输入:所购买三种鸡的总数目n • 输出:满足问题的解的数目k,公鸡,母鸡,小鸡的只数g[], m[], s[] 1.void chicken_question(int n, int &k, int g[], int m[], int s[]) 2.{ • int i, j, a, b, c; • k=0; • i=n/5; • j=n/3; • for(a=0;a<=n;a++){ • for(b=0;b<=n;b++){ • c=n-a-b; • if((a+b+c==n)&&(5*a+3*b+c/3==n)&&(c%3==0)){ • g[k]=a; • m[k]=b; • s[k]=c; • k++; • } • } • } 16.}
分析第二个算法的时间花费: 第4行执行1个操作;第5,6行各执行2个操作;第7行执行1+2(n/5+1)个操作;第8行执行n/5+1+2(n/5+1)(n/3+1)个操作;第9行执行3(n/5+1)(n/3+1);第10行执行10(n/5+1)(n/3+1)个操作;第11,12,13,14行循环一次各执行一个操作,执行与否取决于第10行的条件语句,所以这四行的执行时间不会超过4(n/5+1)(n/3+1)个操作。上述/均表示整除操作,因此 T2(n)<=1+2+2+1+2(n/5+1)+n/5+1+2(n/5+1)(n/3+1)+(3+10+4)(n/5+1)(n/3+1)=19n2/15+161n/15+28≈c2n2 , c2 >0, (n很大时) 当n=10000时,约花费6.7s
运行时间的上界,Ο记号 定义:令N为自然数集合,R+为正实数集合。函数f:N→ R+,函数g:N→ R+,若存在自然数n0和正常数c,使得对所有的n>= n0,都有f(n)<=cg(n),则称函数f(n)的阶至多是Ο(g(n)). 因此,如果存在lim f(n)/g(n),则: n→∞ lim f(n)/g(n) ≠ ∞,即:f(n)= Ο(g(n)) n→∞这个定义表明:f(n)的增长至多像g(n)的增长那么快。此时称Ο(g(n))是f(n)的上界
运行时间的下界,s记号 定义:令N为自然数集合,R+为正实数集合。函数f:N→ R+,函数g:N→ R+,若存在自然数n0和正常数c,使得对所有的n>= n0,都有f(n)>=cg(n),则称函数f(n)的阶至少是s(g(n)). 因此,如果存在lim f(n)/g(n),则: n→∞ lim f(n)/g(n) ≠ 0,即:f(n)= s(g(n)) n→∞这个定义表明:f(n)的增长至少像g(n)的增长那么快。此时称s(g(n))是f(n)的下界
运行时间的准确界,⊙记号 定义:令N为自然数集合,R+为正实数集合。函数f:N→ R+,函数g:N→ R+,若存在自然数n0和两个正常数c1c2,使得对所有的n>= n0,都有c1g(n)<=f(n)<=c2g(n),则称函数f(n)的阶是⊙(g(n)). 因此,如果存在lim f(n)/g(n),则: n→∞ lim f(n)/g(n) = c,即:f(n)= ⊙(g(n)) n→∞
e.g.指数函数f(n)=5*2n+n2 令n0=0,当n>=n0时,有c1=5,g(n)=2n,使得: f(n)>=5*2n=c1g(n) 所以,f(n)=s(g(n))=s(2n) 令n0=4,当n>=n0时,有:c2=6,g(n)=2n f(n)<=5*2n+2n<=6*2n=c2g(n) 所以,f(n)= ⊙(2n) 结论:复杂性关系 1﹤ ㏒ ㏒ n ﹤ ㏒ n ﹤(n)1/2 ﹤n3/4 ﹤n ﹤n㏒n ﹤n2 ﹤2n ﹤n! ﹤2n2
算法的时间复杂性分析 • 循环次数的统计 • 基本操作频率的统计 • 计算步的统计 • 最坏情况分析 • 平均情况分析
最坏情况分析 线性检索算法 输入:给定n个已排序过的元素的数组A[]及元素x 输出:若x=A[j],0<=j<=n-1,输出j,否则输出-1 1. int linear_search(int A[], int n, int x) 2. { 3. int j=0; • while(j<n && x!=A[j]) • j++; • if(x==A[j]) • return j; • else • return -1; 10. }
二叉检索算法 输入:给定n个已排序过的元素的数组A[]及元素x 输出:若x=A[j],0<=j<=n-1,输出j,否则输出-1 1.int binary_search(int A[], int n, int x) 2.{ • int mid, low=0, high=n-1, j=-1; • while(low<=high && j<0){ • mid=(low+high)/2; • if(x==A[mid]) j=mid; • else if(x<A[mid]) high=mid-1; • else low=mid+1; • } • return j; 11.}
数组中不存在元素x,或者元素x是数组的最后一个元素,就是线性检索算法的最坏情况。如果采用第4行的数组元素的比较操作作为算法的基本操作,这时算法必须对数组元素进行n次比较,因此,在最坏情况下,线性检索的时间复杂性是⊙(n)。数组中不存在元素x,或者元素x是数组的最后一个元素,就是线性检索算法的最坏情况。如果采用第4行的数组元素的比较操作作为算法的基本操作,这时算法必须对数组元素进行n次比较,因此,在最坏情况下,线性检索的时间复杂性是⊙(n)。 • 在二叉检索算法中,当数组中不存在元素x,或者x是数组的第一个元素与最后一个元素,这是二叉检索算法的最坏情况。假定x是数组的最后一个元素,第一次比较后,数组后半部分的元素个数是[n/2],这是第二次要继续进行检索的元素个数。类似的,第三次进行检索的元素个数是[n/4],…第i次检索时元素的个数是[n/2j-1].这种情况一直延续到被检索的元素的个数是1。假设检索x次所需要检索的最大比较次数是j次,则j满足: [n/2j-1]=1,即j-1≤㏒n≤j,得到j= [㏒n]+1,这表明在最坏情况下,二叉检索算法元素比较次数最多为[㏒n]+1次,因此它的时间复杂性是Ο(㏒n),它至少执行[㏒n]+1次,因此,它的时间复杂性是⊙(㏒n)。
平均情况分析 e.g.用插入法按递增顺序排序数组A 输入:n个元素的整数数组A[],数组元素个数n 输出:按递增顺序排序的数组A[] 1. void insert_sort(int A[], int n) 2. { • int a, i, j; • for(i=1; i<n; i++){ • a=A[i]; • j=i-1; • while(j>=0 && A[j] > a){ • A[j+1] = A[j]; • j--; • } • A[j+1] = a; • } 13. }
假定A中的元素为{x1,x2,…xn},且xi≠xj. n个元素共有n!种排列,假定每种的概率相同,均为1/i。如果前面i-1个元素已经按递增顺序排序了,现在要把元素xi插入到一个合适的位置,以构成一个i个元素的递增数列。 ii-1 Ti=(i-1)/i+∑(i-j+1)/i=(i-1)/i +∑j/i = ½+i/2-1/i j=2j=1 分别把x2,x3,…xn插入到合适位置,所需的平均比较总次数T为: nnn n T= ∑Ti=∑(1/2+i/2-1/i)=(n-1)/2+1/2 ∑i-∑1/i +1 i=2 i=2 n i=2 i=1 =(n2+3n)/4 - ∑1/i i=1 n 因为㏑(n+1) ≤ ∑1/i i=1 所以:T=(n2+3n)/4 - ㏑n 即插入排序法insert_sort在平均情况下的时间复杂性是 ⊙(n2)
查找(Search) 1、静态查找表2、动态查找表3、哈希查找表
基本概念 • 查找是确定在数据元素集合中是否存在一个数据元素关键字等于给定关键字的过程 • 操作过程:比较 成功 :在记录集合中能找到与给定值相等 的关键字 失败 :查遍整个记录集合也未能找到与给定值的关键字
静态查找:只检查某个特定的记录是否存在 于给定的记录 集合 (采用顺序表结构) • 动态查找:不但检查是否存在,而且当不存在时要将记录插 入到记录集合中;或者当记录存在时,要将其 修改或删除 (采用树表结构) • 衡量查找算法效率的标准: 平均查找长度——为确定某一记录在记录集合中的位置,给定值关键字与集合中的记录关键字所需要进行的比较次数的期望值 查找成功时的平均查找长度(n个记录) ASL = ∑Pi * Ci Pi 是查找第i个记录的概率 Ci是查找第i个记录所需要比较的次数
顺序表的静态查找 • 顺序查找 方法:从顺序表的一端开始,用给定值 的关键字Key逐个顺序地与表中 各记录的关键字相比较 适用于表中数据元素无序的情况
顺序查找的C语言算法 int SequelSeach(elemtype s[], keytype Key, int n) /*在s[0]~s[n-1]中顺序查找关键字为Key的记录*/ /*查找成功返回该记录的下标序号;失败时返回-1*/ { int i=0; while(i<n && s[i].Key!= Key) i++; if(s[i].Key == Key) return i; else return -1; }
e.g. 定义如下主函数: typedef int keytype; typedef struct{ keytype Key; }elemtype; int SequelSearch(elemtype s[], keytype Key, int n); int main(void) { elemtype Test[10]={710,342,45,686,6, 841,429,134,68,264}; int n=10, Key=686, i; i = SequelSeach(Test, Key, n); if(i!=-1) printf(“\n查找成功!该数据元素为第%d个记录”,i ); else printf(“\n查找失败!该数据元素在记录集合中不存在”); }
在这个算法中,while循环语句包含两个条件检测,若要提高查找速度,应尽量减少检测条件。在这个算法中,while循环语句包含两个条件检测,若要提高查找速度,应尽量减少检测条件。 小技巧:设置监视哨 即在表的尾端设置一个虚拟的记录s[n],在查找前将给定值的关键字Key赋给s[n].Key,这样就无需在循环时检查整个标是否检查完毕。
修改后的算法 int SequelSeach(elemtype s[], keytype Key, int n) /*在s[0]~s[n-1]中顺序查找关键字为Key的记录*/ /*查找成功返回该记录的下标序号;失败时返回-1*/ { int i=0; s[n].Key = Key; /*s[n]为监视哨*/ while( s[i].Key!= Key) i++; if(i >= n) return -1; else return i; }
缺点:查找时间长分析: 假设顺序表中每个记录的查找概率均为 Pi=1/n,(i = 1,2,3,…,n), Ci = i, 则查找成功时 ASLsq = (n+1)/2; 查找失败时 ASLsq = n。 假设被查找的记录在顺序表中概率为p, 不在为q,则 ASLsq = (n+1)(1- p/2) 因此,从上面可以看出,顺序查找方法虽然简单,但查找效率较低。当已知各记录查找概率不等时,可以改变记录的存储次序,把查找频率高的记录放到序列前面,低的放后面,可以提高查找效率。
二分查找 • 使用条件:顺序表,表内元素之间有序 s[0].Key <= s[1].Key <= …<= s[n-1].Key 不可直接用于线性链表 设置三个变量low, high, mid,它们分别指向表的当前待查范围的下界,上界和中间位置。初始时,令low=0,high=n-1,设待查数据元素的关键字为Key (1)令mid = [(low+high)/2] (2)比较Key与s[mid].Key 1. s[mid].Key=Key, 查找成功 2. s[mid].Key<Key,查找s[mid]后半部, low=mid+1 3. s[mid].Key>Key,查找s[mid]前半部, high=mid-1 (3)比较low和high,若low<=high,重复(1)(2);否则,表中不存在关键字为Key的记录,查找失败
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[i+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 7 (初始时为最大下标 n ); • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =4 mid=4 但 key=9 < 10, 向左 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=7 low=1
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; mid=4 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3(mid-1)
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =2 mid=2 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3(mid-1)
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =2 mid=2; 但 key=9 > 8, 向右 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3(mid-1)
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 3; high(高下标)= 3 ; mid=2 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 low=3(mid+1)
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 3; high(高下标)= 3 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 = 3 mid=3; key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 low=3
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 9 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 = 3 mid=3; 但 key=9 中点值也为 9 ,找到 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=3 low=3
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 7 (初始时为最大下标 n ); • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 mid=4 但 key=5 < 10, 向左 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 high=7 low=1
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; mid=4 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3(mid-1)
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找不成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =2 mid=2 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 3 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 =2 mid=2; 但 key=5 < 8, 向左 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=3
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 1 ; mid=2 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=1(mid-1)
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 1 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 = 1 mid=1 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=1
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、应用范围:顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 1 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 = 1 mid=1; 但 key=5 > 4, 向右 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=1 high=1
1、静态查找表 • 2、有序表的查找 • 折半查找(或二分查找法) • 1、顺序表,表内元素之间有序。不可直接用于线性链表。 e.g: 查找 key = 5 的结点所在的数组元素的下标地址。 • 查找成功的情况:数组 ST.elem 如下图所示有序 • 数组 ST.elem :递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1 • 查找范围 :low(低下标)= 1; high(高下标)= 1 ; • 比较对象:中点元素,其下标地址为 mid = (low+high)/ 2 = 1 失败条件:low > high; 处于间隙中的键值导致这种情况! mid=1; 但 key=5 > 4, 向右 key 4 8 9 10 11 13 19 0 1 2 3 4 5 6 7 low=2 (mid+1) high=1
二分查找的C语言算法 int BinarySeach(elemtype s[], keytype key,int n) /*在有序表s[0]~s[n-1]中二分查找关键字为Key的记录*/ /*查找成功时返回记录的下标序号,失败时返回-1*/ { int low=0, high=n-1, mid; while(low<=high){ mid=(low+high)/2; if(s[mid].key == key) return mid; else if(s[mid].key<key) low = mid+1; else high = mid-1; } return -1; }
3、性能分析: 2、平均情况分析(在成功查找的情况下): ∴ASL = ( 20×1+ 21×2+ 22×3+ … + 2t-1 ×t) / n t = ∑ (i × 2i-1 ) / n i=1 = [(n + 1) ×( log2(n + 1) - 1 ) + 1 ] / n = (n + 1) ×log2(n + 1) / n - 1 = (n + 1) ×log2(n + 1) / n - 1 结论:在成功查找的情况下,平均查找的代价约为 ASL = log2(n + 1) - 1 或者简单地记为:ASL = log2n - 1
图的搜索算法 ——simple
1.宽度优先搜索(Breadth-First-Search,BFS) 考察下图,判断从1出发可到达的所有顶点 2 5 10 3 6 8 4 7 9
方法 1.确定邻接于1的顶点集合. 是{ 2,3,4 } 2.确定邻接于{ 2,3,4 }的新的顶点集合. 是{ 5,6,7 } 3.邻接{ 5,6,7 }的顶点集合是{ 8,9 },不存在邻接于 { 8.9 }的顶点. 2 5 10 • 3 6 8 4 7 9 因此,从顶点1出发可到达的顶点集为{1,2,3,4,5,6,7,8,9}
这种从一个顶点开始,识别所有可到达的顶点的方法叫做宽度优先搜索(Breadth-First Search,BFS)。 这种搜索可用队列来实现。
//从顶点v开始的宽度优先搜索 把顶点v标记为已到达顶点; 初始化队列Q,其中仅包含一个元素v; While(Q不为空) { 从队列中删除定点w; 令u为邻接于w的点; while(u) { if(u尚未被标记) { 把u加入队列; 把u标记为一到达顶点; } u = 邻接于w的下一个顶点; } }
将该段伪代码用于前面的图中,v = 1.因此在第一个循环中,顶点2,3,4都将被加入到队列中(假设是按此次序加入的).再接下来的循环中,2被从队列中去掉,加入顶点5;然后删除3,之后再删除4, 加入6和7;删除5并加入8;删除6后不增加;删除7后加入9,最后将8和9删除,队列成为空队列.过程终止时,顶点1到9被加上以到达标记。 下图给出了访问过程中所经历的顶点和边构成的子图。
2 5 1 3 6 8 4 7 9