请实现有重复数字的升序数组的二分查找。
输出在数组中第一个大于等于查找值的位置(下标从1开始算起),如果数组中不存在这样的数(指不存在大于等于查找值的数),则输出数组长度加一。
5,4,[1,2,4,4,5]
3
输出位置从1开始计算
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; } }
题目要求的是找到第一个大于等于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; } }
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; }
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开始的结果?为了考查审题细心么?
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; } }
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也没问题。有大神帮看看吗
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; }
二分查找比较基础的思想
实际上手有几个需要注意的地方
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; } }
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; } }
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; }
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] 只要判断相等即可
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; } }最佳答案
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; } } }