首页 > 试题广场 >

二分查找

[编程题]二分查找
  • 热度指数:155398 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
请实现有重复数字的升序数组的二分查找。
输出在数组中第一个大于等于查找值的位置(下标从1开始算起),如果数组中不存在这样的数(指不存在大于等于查找值的数),则输出数组长度加一。
示例1

输入

5,4,[1,2,4,4,5]

输出

3

说明

输出位置从1开始计算     
示例2

输入

5,4,[1,2,3,3,5]

输出

5

说明

虽然数组中没有4,但 5 ≥ 4 ,所以返回5对应的位置,刚好也是5。  
public class Solution {
    public int upper_bound_ (int n, int v, int[] a) {
        int left = 0;
        int right = a.length - 1;
        int mid = (left + right) / 2;
        
        while (left < right) {
            mid = (left + right) / 2;
            
            if (a[mid] >= v) {
                while (mid > 0 && a[mid - 1] >= v) {
                    mid--;
                }
                return mid + 1;
            } else {
                left = mid + 1;
            }
        }
        
        return n + 1;
    }
}
进一步优化:
public class Solution {
    public int upper_bound_ (int n, int v, int[] a) {
        int left = 0;
        int right = a.length - 1;
        int mid = (left + right) / 2;
        
        while (left < right) {
            mid = (left + right) / 2;
            
            if (a[mid] >= v) {
                if (mid > 0 && a[mid - 1] >= v) {
                    right = mid;
                } else {
                   return mid + 1;
                }
            } else {
                left = mid + 1;
            }
        }
        
        return n + 1;
    }
}

编辑于 2021-02-20 15:48:43 回复(0)

题目要求的是找到第一个大于等于v的

public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        // write code here
        int start = 0;
        int end = n-1;
        // 如果第一个就大于等于 V,返回 1
        if(a[0] >= v) return 1;
        // 如果最后一个都小于 V,返回 n+1
        if(a[n-1] < v) return n+1;
        while(start <= end){
            int mid = start + (end - start)/2;
            // 找到第一个大于等于V的时候就停止继续二分查找
            // 转为从当前位置向前遍历,直到遇到第一个小于等于的时候停止,返回 mid + 1
            if(a[mid] >= v){
                while(a[mid-1] >= v){
                    mid--;
                }
                return mid + 1;
            }else{
                start = mid + 1;
            }
        }
        return n+1;
    }
}
发表于 2021-02-08 11:48:25 回复(0)
public int upper_bound_ (int n, int v, int[] a) {
        // write code here
        int left=0,right=n-1;
        while(left<=right){
            int mid=left+(right-left)/2;
            if(a[mid]>=v){
                right=mid-1;
            }else{
                left=mid+1;
            }
        }
        return left+1;
    }

发表于 2021-02-04 10:18:59 回复(0)
/*先来一个非二分查找*/  
public int upper_bound_ (int n, int v, int[] a) {
        int i=0;
        for(i=0;i<n;i++){
          if(a[i]>=v){
              return i+1;
          } 
        }
        return i+1;
    }

发表于 2021-01-25 09:34:20 回复(1)
**,他这是找第一个大于目标的数  不是找第一个相等的数

2,1 ,{2,3,5}
输出  数组仲第一个比1 大的数   应该是 2  然后 从1开始计算  所以是  2在数组中的下标  0+1=1;
如果是找第一相等的数  才是   数组长度 3+1=4     那个测试用例   100,1   那个不过就是这个原因

题目实属坑,考阅读理解
发表于 2020-12-31 11:26:16 回复(0)
    public int upper_bound_ (int n, int v, int[] a) {
        int p = find(0,n-1,v,a);
        if(p == -1){ // 没找到,只需判断 a[0]>v 是则返回1,否者说明 a[n-1]<v,直接返回n+1
            if(a[0] > v) return 1;
            else return n+1;
        }else{
            while(p-1 >=0){ // 找到了结果,还要要向前判断一下是不是第一个符合条件的
                if(a[p-1] == v) p--;
                else break;
            }
            return p+1; // 因为要返回值从1开始算,+1
        }
    }
    
    // 递归二分查找 找到了返回index 否者返回-1
    public int find(int from ,int to,int v,int[] a){
        if(from >to) return -1;
        int p = (from + to) /2;
        if(a[p] == v) return p;
        else if(a[p] > v) return find(from,p-1,v,a);
        else return find(p+1,to,v,a);
    }
不是很理解为什么要求返回从1开始的结果?为了考查审题细心么?
发表于 2020-12-29 15:14:54 回复(0)
这个题目表述不清除,首先返回值必须是与v相等是数组下标+1,这也是为什么测试的数据4返回的是3,因为从1标号开始数,第一个大于等于4的在标号3的位置,在数组中如果最大的值比v小才返回数组长度+1,最小的值比v大是返回1的。并且它机器测试的数据v没有一个夹在a[i]和a[i+1]之间的(i为整数,并且i>=0),因为夹在这中间数组也会找不到与v相等的。实现代码如下:
import java.util.*;

public class Solution {
	public static void main(String[] args) {
		int n,v;
		int [] a;
		Scanner reader = new Scanner(System.in);
		System.out.println("请输入数组长度:");
		n=reader.nextInt();
		a=new int[n];
		for(int i=0;i<a.length;i++)
			a[i]=reader.nextInt();
		System.out.println("请输入要查找的值:");
		v=reader.nextInt();
		try {System.out.println(upper_bound_(n, v, a) );}catch(Exception e) {
			e.printStackTrace();
		}	
	}
	public static int upper_bound_(int n, int v, int[] a) {
		int key;
		int min=0,max=a.length-1;
		if(a.length>0) {
			while(min<=max) {
					key=(min+max)/2;
					if(v==a[key]) {
                                        //找到与v相等还得看key-1是否与v相等,因为题目说的是第一个
						int i=key;
						do {
								i--;
							
						}while(v==a[i]);
						return i+2;
					}
						
					else if(v>a[key])
						min=key+1;
					
					else max=key-1;
				}
		}
                //找不到比较是否比a[0]还小,还小返回1;
		if(v<a[0])
			return 1;
                //否则返回数组长度+1;
		else
			return a.length+1;	
	}

}


发表于 2020-12-22 01:50:43 回复(0)
import java.util.Scanner;

public class Main {
    private static final String SPLIT_SYMBOL = "\\[";
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        String[] firstPart = line.split(SPLIT_SYMBOL)[0].split(",");
        String[] secondPart = line.split(SPLIT_SYMBOL)[1].split("\\]")[0].split(",");
        int originArrLength = Integer.parseInt(firstPart[0]);
        int toFindNum = Integer.parseInt(firstPart[1]);

        int[] baseArr = new int[originArrLength];
        for (int i = 0; i < secondPart.length; i++) {
            baseArr[i] = Integer.parseInt(secondPart[i]);
        }

        System.out.print(findPosition(originArrLength, toFindNum, baseArr));
    }

    private static int findPosition(int originArrLength, int toFindNum, int[] baseArr) {
        int arrLength = baseArr.length;
        if (toFindNum > baseArr[arrLength - 1]) {
            return originArrLength + 1;
        }
        if (toFindNum <= baseArr[0]) {
            return 1;
        }
        // 正常二分逻辑
        return method(toFindNum, baseArr, arrLength);
    }
    private static int method(int toFindNum, int[] baseArr, int arrLength) {
        int tempNum = baseArr[arrLength / 2];
        int topSide = arrLength;
        int bottomSide = 0;
        int currentUseNum = arrLength / 2;
        while(toFindNum != tempNum) {
            if (toFindNum < tempNum) {
                currentUseNum = (bottomSide + currentUseNum) / 2;
                tempNum = baseArr[currentUseNum];
                topSide = currentUseNum;
                continue;
            }
            currentUseNum = (topSide + currentUseNum) / 2;
            tempNum = baseArr[currentUseNum];
            bottomSide = currentUseNum;
        }
        int findFirstTestNum = currentUseNum;
        if (toFindNum == tempNum) {
            while (findFirstTestNum > 0) {
                if (baseArr[--findFirstTestNum] == toFindNum) {
                    continue;
                }
                break;
            }
            return findFirstTestNum + 2;
        }
        return arrLength + 1;
    }
}
、、我搞不懂为什么提交的时候说我没有实际输出,只有用例输出96,但是我用他的“数据自测”功能输出结果也是96,本地idea也没问题。有大神帮看看吗

编辑于 2020-12-19 21:47:48 回复(0)
public int upper_bound_ (int n, int v, int[] a) {
        
        if(v > a[n -1]){
         return n + 1;
        }
        
        if(v < a[0]){
           return n+1;
        }
        
        int start = 0;
        int end = n -1;
        while(start <= end){
        int mid = ( end - start)/2 + start;
            if(a[mid] == v){
                return mid + 1; //从1开始算,什么JB题意    
            }else if(a[mid] > v){
                end = a[mid] -1;
            }else if(a[mid] < v){
                start = a[mid] + 1;
            }
        }
        return n + 1;
       
    }        
        
我就想知道 leetcode这样做为啥 运行超时??求指导 疯了
发表于 2020-12-17 14:18:44 回复(0)
中间有个坑,比数组最小值小居然要输出1。。。我一开始都是去输出了n+1
public int upper_bound_ (int n, int v, int[] a) {
        if (v > a[n-1]) return n + 1;
        if (v < a[0]) return 1;
        int left = 0;
        int rigth = n - 1;
        while (left <= rigth) {
            int mid = (left + rigth) / 2;
            if (a[mid] == v) {
                while(a[mid-1] == a[mid])
                    mid--;
                return mid + 1;
                
            }
            if (a[mid] < v) {
                left = mid + 1;
            } else
                rigth = mid -1;
        }
        return n + 1;
    }
编辑于 2020-12-07 11:03:40 回复(0)
import java.util.*;


public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        // write code here
        if(a[n-1]<v)
            return n+1;
        int s = 0;
        int e = n-1;
        int m =(s+e)>>1;
        while(s<=e){
            if(a[m]<v){
                s=m+1;
                m =(s+e)>>1;
            }
            if(a[m]>=v){
                e=m-1;
                m =(s+e)>>1;
            }
        }
        return m+2;
    }
}
发表于 2020-11-24 16:46:51 回复(0)

二分查找比较基础的思想
实际上手有几个需要注意的地方
1.确定循环跳出条件(可能有些情况需要特殊处理一下)
2.确定中间位置计算公式 我这边使用的
findPos=startPos+((endPos-startPos) >> 1);
使用位运算是在考虑可以加快一点速度

import java.util.*;


public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        // write code here
        //开始位置
        int startPos=0;
        //结束位置
        int endPos=n-1;
        //当前查找位置
        int findPos=(n-1) >> 1;
        //
        int firstAbove=-1;

        while(startPos<endPos){
            if(startPos>=endPos-1){
                if(a[startPos]==v){
                    firstAbove=startPos; 
                }
                if(a[endPos]==v){
                    firstAbove=endPos; 
                }
                break;
            }
            if(a[findPos]>v){
                endPos=findPos;
                findPos=startPos+((endPos-startPos) >> 1);
                //记录大于查找值节点,便于未找到相等节点时遍历返回
                firstAbove=findPos;
            }else if(a[findPos]<v){
                startPos=findPos;
                findPos=startPos+((endPos-startPos) >> 1);
            }else{
                //发现相等,跳出
                firstAbove=findPos;
                break;
            }
        }

        if(firstAbove==-1){
            return n+1;
        }
        int i=firstAbove;
        for(;i>=0;i--){
            if(a[i]<v){
               break;
            }
        }

       return i+2;



    }
}
发表于 2020-11-24 12:13:36 回复(0)
二分查找的变种,注意里面有2个坑:
1. “第一个大于等于查找值的位置”,所以按照传统二分查找找到的值不一定是结果,如[2,2,2,2,2]按传统二分查找找到的是中间那个2,而题目要求的是找到第1个2。所以二分查找到搜索值时还要继续往左边集合继续寻找
2. 下标从1开始
import java.util.*;


public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        if(n<=0 || a==null || a.length==0)
            return 1;
        
        int low=0, high=a.length-1, idx=0;
        while(low <= high) {
            int mid = low + (high-low)/2;
            if(a[mid] < v){
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        
        return low+1;
    }
}


发表于 2020-11-19 17:11:34 回复(0)
二分寻找左侧边界问题
首先确定搜索空间是[0,n),寻找第一大于等于查找值的位置,所以满足的该条件的时候就要收紧右侧边界锁定左侧边界,减小搜索空间为[left, mid)
即  a[mid] >= v  则 right = mid
当 a[mid] < v 时候提高左侧边界[mid+1, right)
    public int upper_bound_ (int n, int v, int[] a) {
        int left = 0;
        int right = n;
       while(left < right){
           int mid = (right + left) / 2;
           if(a[mid] >= v){
               right = mid;
           }else if(a[mid] < v){
               left = mid + 1;
           }
       }
       return left;
       // 牛客需要返回 return left + 1;
    }

同样引发右侧边界问题:如果是找最后一个小于等于查找值???
即 a[mid] <= v 则 left = mid + 1 左侧
当 a[mid] > v 时候降低右侧边界right = mid
右面边界返回的时候必须注意 return 的是 left -1
    public int lower_bound_ (int n, int v, int[] a) {
        int left = 0;
        int right = n;
       while(left < right){
           int mid = (right + left) / 2;
           if(a[mid] <= v){
               left = mid + 1;
           }else if(a[mid] > v){
               right = mid;
           }
       }
       return left - 1;
    }
还有一种是寻找一个数,搜索空间[0,n-1] 只要判断相等即可
编辑于 2020-11-17 22:29:02 回复(0)
import java.util.*;


public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        int result = Integer.MAX_VALUE;
        int left = 0;
        int right = n - 1;
        while(left <= right) {
            int mid = left + (right - left) / 2;
            if(a[mid] >= v) {
                result = Math.min(result, mid);
                //尝试找更小的
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return result == Integer.MAX_VALUE ? n + 1 : result + 1;
    }
}
最佳答案

发表于 2020-11-02 19:51:50 回复(0)
我只能说,题目怪怪的。
import java.util.*;


public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        int i = 0, j = n-1;
        int mid;
        while(i <= j) {
            mid = (i + j) >>> 1;
            if (a[mid] == v) {
                //怕前面会有重复的数字出现,所以进行一次while,找出第一个该数的位置
                while(a[mid - 1] == v) {
                    mid--;
                }
                // 输出的位置从1开始,但是实际从0开始,所以要 + 1
                return mid + 1;
            }else if(a[mid] < v) {
                i =mid + 1;
            }else {
                j = mid -1;
            }
        }
        if(j < 0) {
            return 1;
        }else if(i >= n) {
            return n + 1;
        }else{
            // 此时 i < j
            if(a[j] > v){
                return j + 1;
            }else if(a[i] > v) {
                return i + 1;
            }
            return n + 1;
        }
    }
}


发表于 2020-10-28 11:33:15 回复(0)
import java.util.*;


public class Solution {
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型一维数组 有序数组
     * @return int整型
     */
    public int upper_bound_ (int n, int v, int[] a) {
        // write code here
        int max = n-1;
        int min = 0;
//        if(v>a[max]){
//            return n+1;
//        }
        while (max>=min){
            int index=(max+min)/2;

            if(v>a[index]){
                min=index+1;
            }else{
                for(int i=min;i<=index;i++){
                    if(v<=a[i]){
                        return i+1;
                    }
                }
            }

        }
        return  n+1;
    }
}
发表于 2020-10-24 22:56:51 回复(0)

问题信息

上传者:牛客332641号
难度:
31条回答 14654浏览

热门推荐

通过挑战的用户

二分查找