二分查找
二分法的定义
给定N个从小到大排好序的整数序列Data[ ]以及某待查找整数X,目标是找到X在Data[ ]中的下标。即若有Data[i]=X,则返回i;否则返回失败标记NotFound表示没有找到。二分法是先找到序列的中点Data[Mid],与X进行比较,若相等则返回中点下标Mid;否则,若Data[Mid]>X,则在左边的子系列中查找X;若Data[Mid]<X,则在右边的子系列中查找X。
函数接口和List结构定义
Position BinarySearch(List L, ElementType X);
typedef struct LNode* List;
struct LNode
{
ElementType Data[MAXSIZE];
Position Last; /*保存Data数组中最后一个元素的位置*/
};
实现方法
(1)递归方法:
在二分查找算法中,递归的整体思路是:先找到序列的中点的值zData[Mid],然后将其与X进行比较,若中点处的值比X大,则返回在左边序列中查找的结果;否则如果中点处的值比X小,则返回在右边序列查找的结果;否则中点处的值与X相等,直接返回序列的中点下标Mid。这样,我们就可以将大问题分解成小问题,简化问题的求解了。
递归一定要思考递归出口,即什么时候结束递归。在二分查找问题中,到子序列中所含元素个数为零时,我们就可以结束递归了。那么怎么判断子序列中所含元素个数为零呢,显然只需要序列左边界的下标大于右边界的下标,就可以断定了。于是递归出口为:
if (Left > Right)
{
return NotFound;
}
(2)非递归方法(while循环实现):
在二分查找问题中,非递归方法的思路和递归方法有些类似,也是找到序列中点,然后将中点处的值与待查找元素X进行比较,但是它将递归方法中缩小问题求解规模的思路转换为了调整序列的左右边界。非递归实现根据条件不断调整查找序列的左右边界,知道找到元素X,返回相应的下标值;或者当查找范围内一个元素都没有时,直接返回NotFound。
代码实现
#include <iostream>
using namespace std;
#define MAXSIZE 10
#define NotFound 0
typedef int Position;
typedef int ElementType;
typedef struct LNode* List;
/*函数接口,若待查找元素X在Data数组中能够找到,且Data[i]=X,则函数返回值是i,i是数组下标,从1开始;
否则,返回失败标记NotFound表示没有找到*/
Position BinarySearch1(List L, ElementType X);
void CreateList(List& l, int last);
Position DivideAndSearch(List l, ElementType X, Position Left, Position Right);
Position BinarySearch2(List& l, ElementType X);
struct LNode
{
ElementType Data[MAXSIZE];
Position Last; /*保存Data数组中最后一个元素的位置*/
};
int main()
{
List L=NULL;
cout << "请输入数组Data中最后一个元素的位置:";
Position lp;
cin >> lp;
/*调用创建线性表函数,为问题准备原始数据*/
CreateList(L, lp);
ElementType X;
cout << "请输入待查找的元素:";
cin >> X;
/*调用二分查找算法在给出序列中查找元素*/
//Position position=BinarySearch1(L, X);
Position position = BinarySearch2(L, X);
if (position != NotFound)
{
cout << "元素 " << X << " 在Data数组中的下标是: " << position << endl;
}
else
{
cout << "数组Data中未能找到元素" << X << endl;
}
return 0;
}
void CreateList(List &l, int last)
{
l = (List)malloc(sizeof(struct LNode));
cout << "请输入" << last << "个元素,保证递增有序,中间用空格分隔:";
for (int i = 1; i <= last; ++i)
{
cin >> l->Data[i];
}
l->Last = last;
}
/*非递归查找算法*/
Position BinarySearch1(List L, ElementType X)
{
Position Left, Right, Mid;
Left = 1, Right = L->Last;
while (Left <= Right)
{
Mid = (Left + Right) / 2;
if (L->Data[Mid] > X)
{
Right = Mid - 1;
}
else if (L->Data[Mid] < X)
{
Left = Mid + 1;
}
else
{
return Mid;
}
}
return NotFound;
}
/*由于递归函数需要传入当前搜索范围的左右边界的下标值,所以非递归二分查找算法的函数接口不能直接用于递归;
为了让相同功能的函数保持相同风格的函数接口,需要另外实现一个递归函数,并在原始函数中正确调用递归函数*/
Position BinarySearch2(List& l, ElementType X)
{
return DivideAndSearch(l, X, 1, l->Last);
}
Position DivideAndSearch(List l, ElementType X, Position Left, Position Right)
{
/*递归函数总是需要有一个递归出口*/
if (Left > Right)
{
return NotFound;
}
Position Mid = (Left + Right) / 2;
if (l->Data[Mid] > X)
{
DivideAndSearch(l, X, Left, Mid - 1);
}
else if (l->Data[Mid] < X)
{
DivideAndSearch(l, X, Mid + 1, Right);
}
else
{
return Mid;
}
}