请实现有重复数字的升序数组的二分查找。
输出在数组中第一个大于等于查找值的位置(下标从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;
}
}
}