二分搜索概述
二分搜索,也叫折半查找,是一种采用分治思想的搜索算法。采用二分搜索的前提是所查找的线性表必须采用顺序存储结构,且表中元素按关键字有序排列。二分搜索充分利用了元素间的次序关系,每次搜索过程不断的对序列折半查找,根据中间位置的值缩小搜索的区域,把搜索过程的时间复杂度降低到O(logn)。二分搜索的实际运用千变万化,接下来总结两种经典的二分搜索算法,一种是对一维数组进行二分查找,另外一种是对二维数组进行查找。
文末有完整代码。
一维数组二分查找
在最简单的形式中,二分查找对具有指定左索引和右索引的连续序列进行操作。这就是所谓的查找空间。二分查找维护查找空间的左、右和中间指示符,并比较查找目标或将查找条件应用于集合的中间值;如果条件不满足或值不相等,则清除目标不可能存在的那一半,并在剩下的一半上继续查找,直到成功为止。如果查以空的一半结束,则无法满足条件,并且无法找到目标。
方法体(找到返回下标,没找到返回-1):
public static int simple(int[] array, int target) {
int left = 0;
int right = array.length - 1;
int middle;
while (left <= right) {
//这个二分查找在一些类型的语言中可能会越界,下面的不会
//middle=(left+right)/2;
middle = left + (right - left) / 2;
if (array[middle] == target) {
return middle;
} else if (array[middle] < target) {
left = middle + 1;
} else {
right = middle - 1;
}
}
System.out.println("没找到!");
return -1;
}
主函数
//一维数组进行二分查找
//int [] arraysimple={1,5,9,13,25,29,48};
int [] arraysimple={1,5,9,13,25,29,48,99};
int subscriptsimple = simple(arraysimple,25);
System.out.println(subscriptsimple);
二维数组二分查找
二重二分查找
时间复杂度: O(log(M) + log(N))
思路
由于每行的第一个元素大于前一行的最后一个元素,且每行元素是升序的,所以每行的第一个元素大于前一行的第一个元素,因此矩阵第一列的元素是升序的。我们可以对矩阵的第一列的元素二分查找,找到最后一个不大于目标值的元素,然后在该元素所在行中二分查找目标值是否存在。
public static void complex(int[][] array, int target) {
int result = -1;
int left = 0;
int right = array[0].length - 1;
int middle;
int mid;
int row = 0;
int column = 0;
int low = 0;
int high = array.length - 1;
while (low < high) {
middle = low + (high - low) / 2;
if (array[middle][0] == target) {
row = middle;
column = 0;
result = 1;
break;
} else if (array[middle][0] < target) {
low = middle + 1;
if (low == array.length - 1) {
row = low;
}
if (target < array[middle + 1][0]) {
row = middle;
}
} else {
high = middle;
}
}
while (left <= right) {
mid = left + (right - left) / 2;
if (array[row][mid] == target) {
column = mid;
result = 1;
break;
} else if (array[row][mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
if (result > 0) {
System.out.println(target+" "+row+" "+column);
} else {
System.out.println("没有找到!");
}
}
一重二分查找
思路
若将矩阵每一行拼接在上一行的末尾,则会得到一个升序数组,我们可以在该数组上二分找到目标元素。代码实现时,可以二分升序数组的下标,将其映射到原矩阵的行和列上。
public static void searchMatrix(int[][] matrix, int target) {
int row = 0;
int column = 0;
int m = matrix.length, n = matrix[0].length;
int low = 0, high = m * n - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
int x = matrix[mid / n][mid % n];
if (x < target) {
low = mid + 1;
} else if (x > target) {
high = mid - 1;
} else {
row = mid / n;
column = mid % n;
break;
}
}
System.out.println(target+" "+row+" "+column);
}
//和上一种方法是一样的,这个判断存不存在,上面的方法是返回下标
public static boolean searchMatrixbool(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int low = 0, high = m * n - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
int x = matrix[mid / n][mid % n];
if (x < target) {
low = mid + 1;
} else if (x > target) {
high = mid - 1;
} else {
return true;
}
}
int row = 0;
int column = 0;
row = low;
column = high;
System.out.println(row);
System.out.println(column);
return false;
}
中心放射法
leetcode-cn.com/problems/se…
从负雪明烛的方法二:从左下角或者右上角开始查找 中受到启发
思路:
找到矩阵正中心,首先对目标数和中心矩阵数进行比较,如果目标数小于中心数搜索上部分,反之搜索下部分,找到目标数行数后,先找到行中心,再二分查找具体数
public static void CentralSpiral(int[][] array, int target) {
int volumer = array.length;//行
int volumec = array[0].length;//列
int row = -1;
int column = -1;
int low = 0;
int high = volumer - 1;
int rowmid;// int rowmid=low+(high-low)/2;
int left = 0;
int right = volumec - 1;
int colmid = left + (right - left) / 2;
int mid;
while (low <= high) {
rowmid = low + (high - low) / 2;
mid = array[rowmid][colmid];
if (mid == target) {
row = rowmid;
column = colmid;
break;
} else if (mid < target) {
if (target > array[low][volumec - 1]) {
low = rowmid + 1;
} else {
row = rowmid;
break;
}
} else {
high = rowmid - 1;
}
row = rowmid;
}
if (target <= array[(volumer - 1) / 2][right] && target >= array[(volumer - 1) / 2][0]) {
row = (volumer - 1) / 2;
}
while (left<=right){
colmid=left+(right-left)/2;
int sumcolmid=array[row][colmid];
if (target==sumcolmid){
column=colmid;
break;
}else if (target<sumcolmid){
right=colmid-1;
}else {
left=colmid+1;
}
}
System.out.println(target+" "+row+" "+column);
}
本博客是平时学习笔记没有很多技术含量,小白可以来学习一下,也欢迎各位大佬指点!
完整代码
package practise.Algorithm;
public class BinarySearch {
public static void main(String[] args) {
//一维数组进行二分查找
//int [] arraysimple={1,5,9,13,25,29,48};
int [] arraysimple={1,5,9,13,25,29,48,99};
int subscriptsimple = simple(arraysimple,25);
System.out.println("一维数组进行二分查找:"+subscriptsimple);
//二维数组进行查找
int[][] arraycomplex = new int[][]{{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 60},
{73, 80, 84, 90}, {173, 180, 184, 190}};
//int[][] arraycomplex= new int[][]{{1, 3, 5, 7,9}, {10, 11, 16, 20,21}, {23, 30, 34, 60,67}};
System.out.println("二重二分法:");
complex(arraycomplex,184);//二重二分法
//一重二分法
System.out.println("一重二分法:");
searchMatrix(arraycomplex,190);
boolean result=searchMatrixbool(arraycomplex,11);
System.out.println(result);
//中心放射法
System.out.println("中心放射法:");
CentralSpiral(arraycomplex, 190);
}
public static int simple(int[] array, int target) {
int left = 0;
int right = array.length - 1;
int middle;
while (left <= right) {
//这个二分查找在一些类型的语言中可能会越界,下面的不会
//middle=(left+right)/2;
middle = left + (right - left) / 2;
if (array[middle] == target) {
return middle;
} else if (array[middle] < target) {
left = middle + 1;
} else {
right = middle - 1;
}
}
System.out.println("没找到!");
return -1;
}
//二维数组二分查找
/*
两次二分查找
时间复杂度: O(log(M) + log(N))
思路
由于每行的第一个元素大于前一行的最后一个元素,且每行元素是升序的,所以每行的第一个元素大于前一行的第一个元素,因此矩阵第一列的元素是升序的。
我们可以对矩阵的第一列的元素二分查找,找到最后一个不大于目标值的元素,然后在该元素所在行中二分查找目标值是否存在。
*/
public static void complex(int[][] array, int target) {
int result = -1;
int left = 0;
int right = array[0].length - 1;
int middle;
int mid;
int row = 0;
int column = 0;
int low = 0;
int high = array.length - 1;
while (low < high) {
middle = low + (high - low) / 2;
if (array[middle][0] == target) {
row = middle;
column = 0;
result = 1;
break;
} else if (array[middle][0] < target) {
low = middle + 1;
if (low == array.length - 1) {
row = low;
}
if (target < array[middle + 1][0]) {
row = middle;
}
} else {
high = middle;
}
}
while (left <= right) {
mid = left + (right - left) / 2;
if (array[row][mid] == target) {
column = mid;
result = 1;
break;
} else if (array[row][mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
if (result > 0) {
System.out.println(target+" "+row+" "+column);
} else {
System.out.println("没有找到!");
}
}
/*
一次二分查找
思路
若将矩阵每一行拼接在上一行的末尾,则会得到一个升序数组,我们可以在该数组上二分找到目标元素。
代码实现时,可以二分升序数组的下标,将其映射到原矩阵的行和列上。
*/
public static void searchMatrix(int[][] matrix, int target) {
int row = 0;
int column = 0;
int m = matrix.length, n = matrix[0].length;
int low = 0, high = m * n - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
int x = matrix[mid / n][mid % n];
if (x < target) {
low = mid + 1;
} else if (x > target) {
high = mid - 1;
} else {
row = mid / n;
column = mid % n;
break;
}
}
System.out.println(target+" "+row+" "+column);
}
//和上一种方法是一样的,这个判断存不存在,上面的方法是返回下标
public static boolean searchMatrixbool(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int low = 0, high = m * n - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
int x = matrix[mid / n][mid % n];
if (x < target) {
low = mid + 1;
} else if (x > target) {
high = mid - 1;
} else {
return true;
}
}
int row = 0;
int column = 0;
row = low;
column = high;
System.out.println(row);
System.out.println(column);
return false;
}
/*
中心放射法 CentralSpiral
https://leetcode-cn.com/problems/search-a-2d-matrix/solution/fu-xue-ming-zhu-liu-chong-fang-fa-bang-n-e20z/
负雪明烛:方法二:从左下角或者右上角开始查找 启发
思路:
找到矩阵正中心,首先对目标数和中心矩阵数进行比较,如果目标数小于中心数搜索上部分,反之搜索下部分,找到
目标数行数后,先找到行中心,再二分查找具体数
*/
public static void CentralSpiral(int[][] array, int target) {
int volumer = array.length;//行
int volumec = array[0].length;//列
int row = -1;
int column = -1;
int low = 0;
int high = volumer - 1;
int rowmid;// int rowmid=low+(high-low)/2;
int left = 0;
int right = volumec - 1;
int colmid = left + (right - left) / 2;
int mid;
while (low <= high) {
rowmid = low + (high - low) / 2;
mid = array[rowmid][colmid];
if (mid == target) {
row = rowmid;
column = colmid;
break;
} else if (mid < target) {
if (target > array[low][volumec - 1]) {
low = rowmid + 1;
} else {
row = rowmid;
break;
}
} else {
high = rowmid - 1;
}
row = rowmid;
}
if (target <= array[(volumer - 1) / 2][right] && target >= array[(volumer - 1) / 2][0]) {
row = (volumer - 1) / 2;
}
while (left<=right){
colmid=left+(right-left)/2;
int sumcolmid=array[row][colmid];
if (target==sumcolmid){
column=colmid;
break;
}else if (target<sumcolmid){
right=colmid-1;
}else {
left=colmid+1;
}
}
System.out.println(target+" "+row+" "+column);
}
}
/*
1 3 5 7 1 3 5 7 9
10 11 16 20 10 11 16 20 21
23 30 34 60 23 30 34 60 67
73 80 84 90
173 180 184 190
*/
