您的位置:首页 > 理论基础 > 数据结构算法

《数据结构与算法分析-C语言描述》详解-Sec2(二)

2017-03-18 14:28 246 查看
第二章中给出了几个具体的例子并分别给出了几种不同复杂度的算法,我编写了书中例子中某些没有给出的算法代码并编译调试通过,同时对于大部分课后习题也做了解答和编译,下面是按照问题分别进行代码分析

有序数组查找元素

问题描述 A[]是一个数组,其中元素按照顺序排列,要求在A中查找元素X是否存在并返回其下标

算法1 顺序查找

这是最直接的方法,即从0到N-1遍历

int SequenSearch(const int A[], int X, int N)
{
for (int i = 0; i < N; i++)
if (A[i] == X)
return i;
return Notfound;
}


复杂度分析

最差情况下需要从0遍历到N-1一共N次,平均情况下为N/2次,根据算法复杂度的计算和化简,其复杂度是线性的,即O(N),然而因为数组A本身是个有序数组,算法1没有充分利用这一点。

算法2 对分查找的迭代写法

每次将要查找的元素X与数组中的中间元素
A[center]
进行对比,若
X>A[center]
则在
A[center]
A[N-1]
之间继续查找,若
X<A[center]
则在
A[0]
A[center]
之间继续查找,直到X等于
A[center]
或者未找到X

//算法2 迭代法对分查找
//input:  A[]:待查找的有序数组,要求已排序;X:需要找到的数据; N 数组大小
//return:X在数组中的下标(找到)或者-1(未找到)
int BinarySearch(const int A[], int X, int N)
{
int Low, High, center;
Low = 0;
High = N - 1;

while (Low <= High)
{
center = (Low + High) / 2;
if (X < A[center])
High = center - 1;
else if (X > A[center])
Low = center + 1;
else return center;

}
return Notfound;
}


复杂度分析

最差情况下即X不存在,则将一直查找到下标low>上标High

,由于在while循环中每次迭代均将查找范围减少到原来的一半,假设N为64,则每次迭代时查找范围分别是
64 32 16 8 4 2 1
一共7次,即log64+1,对任意数N而言,则一共需要迭代[logN]+1次,其中[logN]为不超过logN的最大整数,因此显然算法2 的复杂度为对数级,即O(logN)

这里要注意迭代的跳出条件即
while(Low <= High)
,以及每次迭代向下一次迭代的“推进过程”,可以尝试将
Low = center +1
改为
Low = center
会导致无线循环无法跳出,因为当Low=High-1时,此时center与low相等,这时会导致迭代无法继续推进,使用
Low = center +1
可以保证每次迭代间都会产生一个强制的推进,这样可以有效通过while中的条件进行迭代控制

算法3 对分查找的递归写法1

基本思路与算法2相同,只是在算法2中是通过一个while()来控制迭代次数,而这里使用递归的方式,通过最后设置返回条件来控制递归次数

//算法3 对分查找的递归写法
//input: A 待查找的有序数组,X待查找的元素,Low 查找范围下限下标,High 查找范围上限下标
//return: X在A中下标(找到)或者-1(未找到)
int BinarySearch_rec(const int A[], int X, int Low, int High)
{
if (Low == High)
return (A[Low] == X) ? Low : Notfound;
int center;
center = (Low + High) / 2;
if (X < A[center])
{
High = center - 1;
return BinarySearch_rec(A, X,Low, High);
}
else if (X > A[center])
{
Low = center + 1;
return BinarySearch_rec(A, X, Low, High);
}
else return center;
}


复杂度分析

可以设数组为N时查找X需要T(N)时间,显然T(1)=1,同时观察每次迭代过程,以最差情况考虑,即X每次迭代要么小于A[center]要么大于A[center],则在这两种情况下都至少需要T(N/2)时间,同时观察如
center = (Low + High)
以及while判断等语句都只消耗O(1)时间,因此T(N)可以由这两部分组成,即

T(N)=T(N/2)+1

即得到了T(N)的递推公式,再由T(1)=1,我们可以得到

T(2)=T(1)+1=1+1

T(22)=T(2)+1=2+1

T(23)=T(22)+1=3+1

...

从中可以不严格的归纳出T(N) = logN +1,所以复杂度为O(logN)。

或者换个角度而言,显然这里与算法2类似,每次递归后都会将查找数组的范围减少一半,因此复杂度为O(logN),

递归中尤其需要小心的是最后的返回条件和每次递归的推进方式,同样类似算法2,如果将
Low = center + 1
改为
Low = center
.也会发生无法产生递归返回条件,无穷递归导致栈溢出

算法4 对分查找的递归写法2

在算法3中是通过判断
Low == High
作为最后一次递归的返回条件,这里可以类似算法2将递归返回条件改为判断
Low <= High


//算法2 对分查找的递归写法
//input: A 待查找的有序数组,X待查找的元素,Low 查找范围下限下标,High 查找范围上限下标
//return: X在A中下标(找到)或者-1(未找到)
int BinarySearch_rec2(const int A[], int X, int Low, int High)
{
if (Low <= High)
{
int center;
center = (Low + High) / 2;
if (X < A[center])
{
High = center - 1;
return BinarySearch_rec(A, X, Low, High);
}
else if (X > A[center])
{
Low = center + 1;
return BinarySearch_rec(A, X, Low, High);
}
else return center;
}
return Notfound;
}


复杂度分析

显然是与算法3中相同,为O(logN)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息