二分查找
当使用二分查找时,就体现出有序数组的好处。这种查找比线程查找快很多,尤其是对于大的数组来说更为显著。
猜数游戏
二分查找使用的方法与我们在小时候常玩的猜数游戏中所用的方法一样。在这个游戏里,一个朋友会让你猜她整想的一个1至100之间的数。当你猜了一个数字后,她会告诉你三种选择中的一个:你猜的比她想的大、或小、或猜中了。
![f0339e72aa5c023924911e7f3060d097.png](https://i-blog.csdnimg.cn/blog_migrate/fc664dcfdb7b3f43581e8d200d2fc542.jpeg)
猜数游戏
为了能使用最少的次数猜中,必须从50开始猜。如果她说你猜的太小,则推出那个数在51至100之间,所以下一次猜75(51至100的一半)、但如果她说有些大,则推出那个数在1至29之间,所以下一次猜25。
每猜一次就会将可能的值划分成两部分。最后范围会缩小到一个数字那么大,那就是最终答案。
请注意运用这种方法只需要很少的猜测次数。如果用线性查找,要从1开始,然后2,3...需要O(n)的时间复杂度。在二分查找中每猜一次就把可能值划分成两部分,因此猜测的次数就大大减少。下图为一次答案为33的游戏过程。
![95fc202b403ca7738580cfd97a422680.png](https://i-blog.csdnimg.cn/blog_migrate/a76a27335691e7005550c59a8ab5a928.jpeg)
猜数
只需要七次就可以猜出正确的答案。这是最大值。如果幸运的话,不需要到最后就能猜出那个数来。例如答案为50或34时。
代码实现
/** * Created by Bruce on 2020/8/6 **/public class OrdArray_04 { long [] a; int nElem; public OrdArray_04(int maxSize) { this.a = new long[maxSize]; nElem = 0; } public int find(long searchValue){ int lowerBound = 0;//初始化为数组起始下标 int upperBound = nElem - 1;//初始化为数组结尾下标 int curIn;//当前下标位置 while (true){ curIn = (lowerBound + upperBound) / 2;//初始化数组中中间位置的下标 if(a[curIn] == searchValue){//判断中间位置结果是否是要查找的值,如果是则直接返回下标 return curIn; }else if(lowerBound > upperBound){//如果起始下标大于结尾下标,代表未找到 return -1; }else { /** * 如果要查找的值小于数组中中间位置保存的值,代表要查找的值在数组起始位置到中间位置之间, * 则把接下来要查找的数组减少一半,由于之前已经判断过中间下标不是要查找的值,则把数组结尾下标定义为中间位置下标减1。 * 反之则代表要查找的值在中间位置到数据结尾位置之间, * 则把接下来要查找的数据减少一半,由于之前已经判断过中间下标不是要查找的值,则把数组起始下标定义为中间位置下标加1。 */ if(searchValue < a[curIn]){ upperBound = curIn - 1; }else { lowerBound = curIn + 1; } } } }}
![f22fe582879695877eb5f352f1b34ac4.png](https://i-blog.csdnimg.cn/blog_migrate/e45f559dbba4e81a8c69b92a3109a12a.jpeg)
代码解释一
![de0f80ebe70820d49699b699db0ecef2.png](https://i-blog.csdnimg.cn/blog_migrate/f55b8696761918146561d6a574e2c5f5.jpeg)
代码解释二
![113c8b9fb6a054ce7f168340a97efc0b.png](https://i-blog.csdnimg.cn/blog_migrate/5407f8af7b2d3b7419a4d5432ae44062.jpeg)
示例图三
有序数组的优点
有序数据的优点是查找快,但是插入和删除慢。查找使用二分查找,所以速度很快。有序数据的插入操作比较慢,因为每次插入都需要向后移动数组中的数据,无序数组不需要向后移动,只需要在数据尾部添加即可。有序数组和无序数组的删除都是非常慢的。因为删除任意一个元素,后续的元素都需要向前移动。