排序算法学习整理拓展(二分查找)
说到查找算法,最简单最暴力的算法就是一个个比较,确定是否为所需要的值。
例如,
0,1,2,3,4,5,6,7,8,9 十个数中查找是否存在数 x ,
那么暴力的方式就是
1. 取出第一个数 0 与 x 比较,若相等则返回索引
2. 取出第二个数 1 与 x 比较,若相等则返回索引
3. 取出第三个数 2 与 x 比较,若相等则返回索引
4. 取出第四个数 3 与 x 比较,若相等则返回索引
5. 取出第五个数 4 与 x 比较,若相等则返回索引
……
10.取出第十个数 9 与 x 比较,若相等则返回索引
这样如果我需要查找的 x 为 9 则需要查找10次,时间复杂度为(O)n 。事实上呢,我们人正常找 x 位于数列的什么位置会找一个参照数,
看x大于它还是小于它,然后再在参照数到 x 之间再找一个参照数,再进行比较,直到找到 x 。
这种思想是在计算机中叫分治,而且当分治的分割方法为对半时,称这种方法为二分查找。
二分查找的优势在小数列中不太明显,当存在一个长度为100 的有序数列,你想找到 x为 50 时,正常的查找方法就得查找51次(从0开始),而二分查找只需要一次。
下面我们来思考一下如何构建二分查找的算法:
设:存在一个 数列 { 1,2,3,4,5,6,7,8,9,10 } ,设需查找的数x为7,则有:
1. 找到 5 ;1,2,3,4,5,6,7,8,9,10 。5 != 7 所以,继续查找;
由于数列较小,++的速度会比二分的方法快所以先介绍一个伪二分,
2. 找到 6 ;1,2,3,4,5,6,7,8,9,10。6 != 7,继续查找
3. 找到 7 ;1,2,3,4,5,6,7,8,9,10。7 == 7,返回索引
这种二分只适用于较短的数组,长数组伪二分的意义不大。但是我们可以像快速排序一样设定一个阈值到一定值的时候进行正常比较。
毕竟没最好的算法只有最合适的算法。
接着我们来构建真正的二分算法。
1. 0+9 = 9, 9/2 = 4 数列的第四个数为5, 所以第一次找到5
2. 7 > 5, 所以取数组后半节,(5+1+8)/ 2 = 7,数组的第7个数时8, 所以第二次找到8
3. 8 > 7, 所以取前半截, (7-1+5+1) = 6,数组的第6个为7, 7 == 7 返回索引。
现在观察一下思路,发现几个规律
1.每一次都会有对半操作, (前索引+后索引)/ 2,
2.当中间值与索引上的值不等时会有两种情况,
1)x比索引上的值大,中间值的索引加1;
2)索引上的值比x小,中间值的索引减1;
从 1 中我们可以写出一行代码 (left+right)/ 2
从 2 中我们知道会有两个判断语句
1. if (arr[inedx] > x)
{
index++;
}
2.if (arr[index] < x)
{
index--;
}
至此核心代码就出来了。
下面是完整的二分查找代码:
int Binary_Search(int nums[], int numsSize) int left = 0, right = numsSize-1; int index = (left + left) / 2; while (left < right) { if (nums[index] > target) { right = index-1; index = (left + right) / 2;//放在外面会更简洁 continue; } else if (nums[index] < target) { left = index + 1; index = (left + right) / 2; continue; } return index; }
化简后的二分查找
int binary_search(int *arr, int n, int pivot) { int left = 0; int right = n-1; int mid; while (left <= right) { mid = (left+right) / 2; if (pivot < arr[mid]) { right = mid - 1; continue; } if (pivot > arr[mid]) { left = mid + 1; continue; } return mid; } return -1; }