书籍简介
第一章 字符串
1.0 本章导读
1.1 旋转字符串
1.2 字符串包含
1.3 字符串转换成整数
1.4 回文判断
1.5 最长回文子串
1.6 字符串的全排列
1.10 本章习题
第二章 数组
2.0 本章导读
2.1 寻找最小的 k 个数
2.2 寻找和为定值的两个数
2.3 寻找和为定值的多个数
2.4 最大连续子数组和
2.5 跳台阶
2.6 奇偶排序
2.7 荷兰国旗
2.8 矩阵相乘
2.9 完美洗牌
2.10 K个最小和 (UVA 11997 K Smallest Sums)
2.15 本章习题
第三章 树
3.0 本章导读
3.1 红黑树
3.2 B树
3.3 最近公共祖先LCA
3.5 R树:处理空间存储问题
3.10 本章习题
第四章 查找匹配
4.1 有序数组的查找
4.2 行列递增矩阵的查找
4.3 出现次数超过一半的数字
第五章 动态规划
5.0 本章导读
5.1 最大连续乘积子串
5.2 字符串编辑距离
5.3 格子取数
5.4 交替字符串
5.6 最长递增子序列
5.10 本章习题
第六章 海量数据处理
6.0 本章导读
6.1 关联式容器
6.2 分而治之
6.3 simhash算法
6.4 外排序
6.5 MapReduce
6.6 多层划分
6.7 Bitmap
6.8 Bloom filter
6.9 Trie树
6.10 数据库
6.11 倒排索引
6.15 本章习题
第七章 机器学习
7.1 K 近邻算法
7.2 支持向量机
附录 更多题型
附录A 语言基础
附录B 概率统计
附录C 智力逻辑
附录D 系统设计
附录E 操作系统
附录F 网络协议
4.1 有序数组的查找 - 《程序员编程艺术:面试和算法心得》 - 光年文档管理系统(Light Year Doc)
网站首页
4.1 有序数组的查找
### 题目描述 给定一个有序的数组,查找某个数是否在数组中,请编程实现。 ### 分析与解法 一看到数组本身已经有序,我想你可能反应出了要用二分查找,毕竟二分查找的适用条件就是有序的。那什么是二分查找呢? 二分查找可以解决(预排序数组的查找)问题:只要数组中包含T(即要查找的值),那么通过不断缩小包含T的范围,最终就可以找到它。其算法流程如下: - 一开始,范围覆盖整个数组。 - 将数组的中间项与T进行比较,如果T比数组的中间项要小,则到数组的前半部分继续查找,反之,则到数组的后半部分继续查找。 - 如此,每次查找可以排除一半元素,范围缩小一半。就这样反复比较,反复缩小范围,最终就会在数组中找到T,或者确定原以为T所在的范围实际为空。 对于包含N个元素的表,整个查找过程大约要经过log(2)N次比较。 此时,可能有不少读者心里嘀咕,不就二分查找么,太简单了。 然《编程珠玑》的作者Jon Bentley曾在贝尔实验室做过一个实验,即给一些专业的程序员几个小时的时间,用任何一种语言编写二分查找程序(写出高级伪代码也可以),结果参与编写的一百多人中:90%的程序员写的程序中有bug(我并不认为没有bug的代码就正确)。 也就是说:在足够的时间内,只有大约10%的专业程序员可以把这个小程序写对。但写不对这个小程序的还不止这些人:而且高德纳在《计算机程序设计的艺术 第3卷 排序和查找》第6.2.1节的“历史与参考文献”部分指出,虽然早在1946年就有人将二分查找的方法公诸于世,但直到1962年才有人写出没有bug的二分查找程序。 你能正确无误的写出二分查找代码么?不妨一试,关闭所有网页,窗口,打开记事本,或者编辑器,或者直接在本文评论下,不参考上面我写的或其他任何人的程序,给自己十分钟到N个小时不等的时间,立即编写一个二分查找程序。 要准确实现二分查找,首先要把握下面几个要点: - 关于right的赋值 - right = n-1 => while(left <= right) => right = middle-1; - right = n => while(left < right) => right = middle; - middle的计算不能写在while循环外,否则无法得到更新。 以下是一份参考实现: ```cpp int BinarySearch(int array[], int n, int value) { int left = 0; int right = n - 1; //如果这里是int right = n 的话,那么下面有两处地方需要修改,以保证一一对应: //1、下面循环的条件则是while(left < right) //2、循环内当 array[middle] > value 的时候,right = mid while (left <= right) //循环条件,适时而变 { int middle = left + ((right - left) >> 1); //防止溢出,移位也更高效。同时,每次循环都需要更新。 if (array[middle] > value) { right = middle - 1; //right赋值,适时而变 } else if(array[middle] < value) { left = middle + 1; } else return middle; //可能会有读者认为刚开始时就要判断相等,但毕竟数组中不相等的情况更多 //如果每次循环都判断一下是否相等,将耗费时间 } return -1; } ``` ## 总结 编写二分查找的程序时 - 如果令 `left <= right,则right = middle - 1; - 如果令left < right,则 right = middle;` 换言之,算法所操作的区间,是左闭右开区间,还是左闭右闭区间,这个区间,需要在循环初始化。且在循环体是否终止的判断中,以及每次修改left, right区间值这三个地方保持一致,否则就可能出错。
上一篇:
第四章 查找匹配
下一篇:
4.2 行列递增矩阵的查找