二分的实用性自然不必多说。最近做一个短信编码的项目,刚好涉及一个转编码算法;有一些转码尚有规律可循,如果没有规律可循也可以开一个Hash,但如果连Hash也很难编码,那还不如先排序再搜索来的简单方便。二分的基础是有序,无论是单调上升还是单调不下降;归根到底,最基本的数学模型就是寻找已排序好的元素 :有自变量x与因变量y,函数为y=F(x),且当x1>x2时y1>y2(或者y1>=y2),现已知y,求x(或求最小/最大的x)。
最简单的模型
以UNICODE转GBK编码为例:(当然这有直接的规律,百度一下就知道,这里拿来只是演示所用)
我们开两个数组,GBK数组GBK_Code[7000],UNC数组Unicode_Code[7000],并将其整合到同一个数组GB_TO_UNI[7000][2]中
以Unicode数组进行排序,现在我们就有了一个有序数列:GB_TO_UNI[0][0], GB_TO_UNI[1][0], GB_TO_UNI[2][0], GB_TO_UNI[3][0], GB_TO_UNI[4][0]....在这个数列里面,自变量是下标0,1,2,....,应变量是Unicode编码,现在的任务就是倒过来已知编码求下标。(这个项目里原来是GB转到Unicode,所以数组名字未曾更改,特注明)
int low = 0, high = m_iDictLength; // 长度
int mid;
while(low < high)
{
mid = (low + high) / 2;
UINT16 midCode = GB_TO_UNI[mid][1];
if(midCode == unCode)
{
return GB_TO_UNI[mid][0];
}if(unCode < midCode)
high = mid - 1;
else
low = mid + 1;
}
return GB_TO_UNI[low][0];
当midcode==code,自然不用多说;
当midcode<code,也就是说正解落在(mid,high],左开右闭。
当midcode>code,请读者自行理解。
这是一种比较激进的写法,因为Code两两各不相同,而且我可以确定每个Unicode编码都能在字典中寻找到。当条件允许的时候,可以适当放宽二分在边界处的写法;但是如果不能确定每个编码都能被找到,那就要小心从事了。说到底,每种算法都需要视输入条件所定,做一定的调整以达到最高的效率,而预处理也是很重要的一个步骤。
唯一的问题就是,为什么可以这样写,确实不会出现死循环之类的问题么,确认边缘数据不会被遗漏么?分析一下一个小数据:lo = 1, hi = 4时的情况。
假设正解是1:
mid = 2; code<midcode(想想看为什么); high =1; low = 1; 跳出return 1
正解是2:
mid = 2; code = midcode; return 2;
正解是3:
mid = 2; code > midcode; low = 3;
mid = 3; code = midcode; return 3;
正解是4:
mid = 2; code > midcode; low = 3;
mid = 3; code > midcode; low = 4; 跳出 return 4
因为当return low的时候,必然发生low>=high,换言之,在前一步发生low = mid + 1或者high = mid - 1,这两种情况下均有low=high=answer。