二分搜索——一维数组&二维数组进行二分搜索

320 阅读4分钟

二分搜索概述

二分搜索,也叫折半查找,是一种采用分治思想的搜索算法。采用二分搜索的前提是所查找的线性表必须采用顺序存储结构,且表中元素按关键字有序排列。二分搜索充分利用了元素间的次序关系,每次搜索过程不断的对序列折半查找,根据中间位置的值缩小搜索的区域,把搜索过程的时间复杂度降低到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
 */

在这里插入图片描述