二分查找细则讨论
二分查找细则讨论
二分查找有两种实现方式:非递归和递归。我们首先给出非递归的实现,然后对其中的细则进行讨论。之后,我们再讨论递归实现的细则。
一、非递归实现
这里我们假设待查找序列是有序且互异的。
#include <iostream> #include <vector> using namespace std; void nonrec_binary(const vector<int>& arr, int n, int& pos) { pos = -1; assert(arr.size() > 0); int left = 0, right = arr.size() - 1, middle = 0; while (left <= right) // 细则1 { middle = (left + right) / 2; // 细则2 if (n == arr[middle]) { pos = middle; break; } else if (n > arr[middle]) { left = middle + 1; // 细则3 } else { right = middle - 1; // 细则4 } } return; } int main() { int a[] = {2, 4, 5, 8, 9, 10, 13, 16, 18, 19}; vector<int> arr(a, a+sizeof (a) / sizeof (*a)); for (vector<int>::size_type i = 0; i != arr.size(); ++i) { cout << arr[i] << ' '; } cout << endl; int n = 9, pos = -1; nonrec_binary(arr, n, pos); cout << n << ':' << pos << endl; n = 12, pos = -1; nonrec_binary(arr, n, pos); cout << n << ':' << pos << endl; system("PAUSE"); return 0; }
细则讨论:
1) 细则1
while (left <= right)
这里是 left <= right,而不是 left < right。举个例子,如果序列中只有一个元素,则left=0,right=0,如果n=arr[0],二分查找的结果pos应为0,但是如果是while(left < right)则无法进入循环,程序运行结果pos为-1,导致bug。
2) 细则2
middle = (left + right) / 2;
left、right、middle均为int型,所以除法/操作得到的数只是取整,如果left=0,right=9,则middle为4,而非4.5。
3) 细则3
left = middle + 1;
if (n > arr[middle])则left = middle + 1; 而不是 left = middle。因为,这是根据细则2讨论的整形除法,如果arr为{3, 5},待查找元素为5,正常来说结果pos应为为1。但是如果left=middle;,在每一次循环中,left一直为0,middle一直为0,所以导致了死循环,最终程序异常终止。
4) 细则4
right = middle – 1
if (n < arr[middle])则right = middle – 1, 而不是 right = middle。细则4与细则3并不一样,如果写成right=middle并不程序的运行效果,因为整数整除对细则4的影响并不像细则3那么严重。但是由于 n < arr[middle] 了,虽然 right = middle; 可以照常工作,arr[middle] 已可以不在考虑范围内,所以最好写为 right = middle – 1,这样既可以提高效率,也可以与细则3表现一致。
5) 细则5
程序开头的部分并没有检测待查找元素n与arr[0]、arr[arr.size()-1]的大小关系,如果进行检查,如果n < arr[0] || n > arr[arr.size()-1] 则返回 pos = -1,如果 n == arr[0],则 pos = 0,如果 n == arr[arr.size() - 1],则 pos = arr.size() – 1。如果都不符合上面的条件,则进入 while 循环。
其实没必要进行前面的检查,因为不管前面检查与否,while 循环都会考虑到前面的所有因素,细则1和 if (n == arr[middle]) break; 会涵盖n < arr[0] || n > arr[arr.size()-1]的情况,细则2会涵盖n == arr[arr.size() - 1],则 pos = arr.size() – 1的情况。
在有先验知识的情况下,可以决定是否要加上前面的判断情况:如果进行大量查找的情况下,且n不经常出现于序列中或n主要为最大或最小元素时,则进行前面的判断可以提高一定的效率,否则没有必要加上这些判断条件。
二、递归实现
#include <iostream> #include <vector> using namespace std; void rec_binary(const vector<int>& arr, int left, int right, int middle, int n, int& pos) // 细则1 { if (left > right) // 细则2 { pos = -1; return; } else if (n == arr[middle]) { pos = middle; return; } else if (n > arr[middle]) { rec_binary(arr, middle+1, right, (middle+1+right)/2, n, pos); // 细则3 } else { rec_binary(arr, left, middle-1, (left+middle-1)/2, n, pos); // 细则4 } } int main() { int a[] = {2, 4, 5, 8, 9, 10, 13, 16, 18, 19}; vector<int> arr(a, a+sizeof (a) / sizeof (*a)); for (vector<int>::size_type i = 0; i != arr.size(); ++i) { cout << arr[i] << ' '; } cout << endl; int n = 9, pos = -1; rec_binary(arr, 0, arr.size()-1, (0+arr.size()-1)/2, n, pos); // 细则5 cout << n << ':' << pos << endl; n = 12, pos = -1; rec_binary(arr, 0, arr.size()-1, (0+arr.size()-1)/2, n, pos); cout << n << ':' << pos << endl; system("PAUSE"); return 0; }
递归的实现,关键在于递归结构的定义,以及终止条件以及递归公式。
1) 细则1
void rec_binary(const vector<int>& arr, int left, int right, int middle, int n, int& pos)
这里需要有 left 和 right 作为查找序列的边界,递归的意义也就在于对 left 和 right 上下边界的操作。
middle 参数可有可无,这里在函数定义的时候作为了参数,也可以在函数内部有 left 和 right 得到。
2) 细则2
细则2和下面的 n == arr[middle] 作为递归函数的终止条件。
3) 细则3、4
递归函数的内部调用——递归公式。
4) 细则5
递归函数的外部调用。
rec_binary(arr, 0, arr.size()-1, (0+arr.size()-1)/2, n, pos);
附:递归的另一个实现
void rec_binary_2(const vector<int>& arr, int left, int right, int n, int& pos)
#include <iostream> #include <vector> using namespace std; void rec_binary_2(const vector<int>& arr, int left, int right, int n, int& pos) { int middle = (left + right) / 2; if (left > right) { pos = -1; return; } else if (n == arr[middle]) { pos = middle; return; } else if (n > arr[middle]) { rec_binary_2(arr, middle+1, right, n, pos); } else { rec_binary_2(arr, left, middle-1, n, pos); } } int main() { int a[] = {2, 4, 5, 8, 9, 10, 13, 16, 18, 19}; vector<int> arr(a, a+sizeof (a) / sizeof (*a)); for (vector<int>::size_type i = 0; i != arr.size(); ++i) { cout << arr[i] << ' '; } cout << endl; int n = 9, pos = -1; rec_binary_2(arr, 0, arr.size()-1, n, pos); cout << n << ':' << pos << endl; n = 12, pos = -1; rec_binary_2(arr, 0, arr.size()-1, n, pos); cout << n << ':' << pos << endl; system("PAUSE"); return 0; }
(完)
文档信息
·版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
·博客地址:http://www.cnblogs.com/unixfy
·博客作者:unixfy
·作者邮箱:goonyangxiaofang(AT)163.com
·如果你觉得本博文的内容对你有价值,欢迎对博主 小额赞助支持