zoukankan      html  css  js  c++  java
  • Java实现的二分查找算法

    二分查找过程

    二分查找,也称折半查找,是对有序序列的查找算法,时间复杂度为O(log n).本文的重点是某元素二分查找的比较次数。特别要注意的是查找的上下边界问题(下面有解释)

    例:22 34 55 77 89 93 99 102 120 140,查找77需要查找的次数是多少?

    答:4次。
    

    序列: 22 34 55 77 89 93 99 102 120 140

         下标:0   1   2    3    4   5   6    7     8     9
    

    用low表示低位元素下标,用high表示高位下标

    查找77

    第一次查找:(low+high)/2 = (0+9)/2 = 4 ,查找下标为4的元素,即89。由于89>77,在89的左侧继续查找。此时调整low=0,high=3

    请注意,由于知道下标为4的元素89比要查找的元素77大,为了提高效率,会跳过下标为4的元素,使得high=3

    第二次查找:(low+high)/2=(0+3)/2=1,查找下标为1的元素,即34。由于34<77,因此,在34的右侧继续查找。此时调整low=2,high=3,由于已知道1号元素34与77不相等,所以low不取1。

    第三次查找:(low+high)/2=(2+3)/2=2,查找下标为2的元素,即55。由于55<77,在55的右侧继续查找。此时调整low=3,high=3.low取3而不取2的原因同上。

    第四次查找:(low+high)/2=(3+3)/2=3,查找下标为3的元素,即77. 77=77,找到目标元素,查找结束。

    同理查找34和99,需要比较的次数分别是2和4次

    /*
    * BinarySearch.java
    * Version 1.0.0
    * Created on 2017年12月17日
    * Copyright ReYo.Cn
    */
    package reyo.sdk.utils.test.search;
    
    /**
    * <B>创  建 人:</B>AdministratorReyoAut <BR>
    * <B>创建时间:</B>2017年12月17日 上午10:06:08<BR>
    *
    * @author ReYo
    * @version 1.0
    */
    /**
     * 二分查找又称折半查找,它是一种效率较高的查找方法。
      【二分查找要求】:1.必须采用顺序存储结构 2.必须按关键字大小有序排列。
     *
     */
    public class BinarySearch {
        public static void main(String[] args) {
            int[] src = new int[] { 1, 3, 5, 7, 8, 9 };
            System.out.println(binarySearch(src, 3));
            System.out.println(binarySearch(src, 3, 0, src.length - 1));
        }
    
        /**
         * * 二分查找算法 * *
         *
         * @param srcArray
         *            有序数组 *
         * @param des
         *            查找元素 *
         * @return des的数组下标,没找到返回-1
         */
        public static int binarySearch(int[] srcArray, int des) {
    
            int low = 0;
            int high = srcArray.length - 1;
            while (low <= high) {
                int middle = (low + high) / 2;
                if (des == srcArray[middle]) {
                    return middle;
                } else if (des < srcArray[middle]) {
                    high = middle - 1;
                } else {
                    low = middle + 1;
                }
            }
            return -1;
        }
    
        /**
        *二分查找特定整数在整型数组中的位置(递归)
        *@paramdataset
        *@paramdata
        *@parambeginIndex
        *@paramendIndex
        *@returnindex
        */
        public static int binarySearch(int[] dataset, int data, int beginIndex, int endIndex) {
            int midIndex = (beginIndex + endIndex) / 2;
            if (data < dataset[beginIndex] || data > dataset[endIndex] || beginIndex > endIndex) {
                return -1;
            }
            if (data < dataset[midIndex]) {
                return binarySearch(dataset, data, beginIndex, midIndex - 1);
            } else if (data > dataset[midIndex]) {
                return binarySearch(dataset, data, midIndex + 1, endIndex);
            } else {
                return midIndex;
            }
        }
    
    }

    其它方式的思考:

    问题:   某数组A[1..n]含有所有从0..n的所有整数,但其中有一个整数不在数组中,通过利用一个辅助数组B[0..n]来记录A中出现的整数,很容易在 O(n)时间内找出所缺的整数。但在这个问题中,我们却不能由一个单一操作来访问A中的一个完整整数,因为A中元素是以二进制表示的。我们所能用的唯一操 作就是“取A[i]的第j位”这个操作所花时间为常数。      

    证明:如果访问数组A中信息的唯一方式是这种单一位操作,仍能在O(n)时间内找出所缺的整数。A之外的任一完整整数仍然可以由一个单一操作来访问。

    基本方法就是用二分法:
    1, 遍历整数0到n的第一位,分成两个数组:P1[1] 和P0[1],分别代表第一位是1,0的数,并记录第一位是1的个数CountN,代价为O(n)
    2, 遍历数组A[1...n]的第一位, 分成两个组:Q1[1]和Q0[1],分别代表第一位是1,0的数,并记录1的个数CountA,代价为O(n)
    3, 比较CountN和CountA的值,结果可能有两种情况CountN = CountA,或者CountN = CountA + 1, 前者表明所缺数的第一位为0, 后者为1,代价为O(1)
    4, 通过3的结果,随后我们可以在P1[1]和Q1[1](CountN>CountA,即缺少第一位为1的数)  或者 P0[1]和Q0[1](CountN=CountA,即缺少第一位为0的数)中的第2位中重复步骤1,2中的操作,记录数组P1[2]、P0[2]和 CountN'及Q1[2]、Q0[2]和CountA'。代价为O(n/2)和O(n/2), 经过比较后可得到所缺数第二位是0还是1,决定接下来比较P1[2]和Q1[2]  或者 P0[2]和Q0[2],代价O(1)
    5, 不断重复Ceiling(lg(n))次,最后即可找到所缺数总代价为2* (O(n) + O(n/2) + ... +O(n/pow(2, k))) + ... + O(1)) = 2* O(2n) = 4*O(n) = O(n)

    当然这里忽略了一个问题,如果A中缺了一个,这应该是n-1个数,则多出来的那个数是什么呢,如果和其他数有重复,上面的方法就无效了,情况变得相当复杂。因此上面的只适用于多出的一个数为0,或者干脆就只有n-1个数。

    【另】

    比较弱的方法:1到n所有数字加起来的和 减去 A[1]到A[n]的和

    比较犀利的方法:凑m,m=4k-1,并且是4k-1>=n的最大整数(比如n=7,则m=7;n=16,则m=19;n=21,m=23以此类推)
    那么只需要A[1]^A[2]^……^A[n-1]^A[n]^{A[m-2]^A[m-1]^A[m]},即可

    原因:从4的整数倍开始,连续4个数字异或,结果为(例如4^5^6^7的结果是0;204^205^206 = 207;8^10^11=9)
    所以0^1^2^……^m的结果为0,却哪个数字,则A[1]^A[2]^……^A[n-1]^A[n]^{A[m-2]^A[m-1]^A[m]}的结果就是所缺的数字

  • 相关阅读:
    新概念第二册(1)--英语口语听力课1
    外企面试课程(一)---熟悉常见的缩略词
    公司 邮件 翻译 培训 长难句 结课
    workflow
    公司 邮件 翻译 培训 长难句 20
    公司 邮件 翻译 培训 长难句 19
    Engineering Management
    公司 邮件 翻译 培训 长难句 18
    公司 邮件 翻译 培训 长难句 17
    第14.5节 利用浏览器获取的http信息构造Python网页访问的http请求头
  • 原文地址:https://www.cnblogs.com/interdrp/p/8051517.html
Copyright © 2011-2022 走看看