第二章中给出了几个具体的例子并分别给出了几种不同复杂度的算法,我编写了书中例子中某些没有给出的算法代码并编译调试通过,同时对于大部分课后习题也做了解答和编译,下面是按照问题分别进行代码分析
有序数组查找元素
问题描述 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