请实现有重复数字的升序数组的二分查找。
输出在数组中第一个大于等于查找值的位置(下标从1开始算起),如果数组中不存在这样的数(指不存在大于等于查找值的数),则输出数组长度加一。
5,4,[1,2,4,4,5]
3
输出位置从1开始计算
5,4,[1,2,3,3,5]
5
虽然数组中没有4,但 5 ≥ 4 ,所以返回5对应的位置,刚好也是5。
class Solution {
public:
/**
* 二分查找
* @param n int整型 数组长度
* @param v int整型 查找值
* @param a int整型vector 有序数组
* @return int整型
*/
int upper_bound_(int n, int v, vector<int>& a) {
// write code here
if(a[n-1]<v) return n+1;
int left=0,right = n-1;
int temp;
while(left<right){
int mid = (right+left)/2;
if(a[mid]<v) {
left = mid+1;
}
else if(a[mid]>=v){
temp = mid;
right = mid;
}
}
return temp+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){
left=mid+1;
}else if(a[mid]>=v){
//如果数组中的第一个元素就大于目标值,那就直接加1返回
if(mid==0) return mid+1;
//判断一下是否为第一个大于等于的位置,利用了数组是有序的条件
if(a[mid-1]>=v) {right=mid-1;}
else return 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.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也没问题。有大神帮看看吗class Solution {
public:
/**
* 二分查找
* @param n int整型 数组长度
* @param v int整型 查找值
* @param a int整型vector 有序数组
* @return int整型
*/
int upper_bound_(int n, int v, vector<int>& a) {
// write code here
int l = 0, r = n - 1, mid;
while (l <= r) {
mid = (l + r) / 2;
if (a[mid] == v) {
while (a[mid] == a[mid-1]) {//找到v输出第一个等于v的值的下标+1 因为题目说明从1开始
mid--;
}
return mid + 1;
}
if (a[mid] > v) r = mid - 1;
if (a[mid] < v) l = mid + 1;
}
for (int i = 0; i < n; i++) {//程序到此 说明a里面没找到等于v的值 输出第一个大于v的值的下标+1
if (a[i] >= v) {
return i+1;
}
}
return n + 1;//a里面没有比v大的数 输出a的长度加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;
}
}
} //二分查找上边界查找改版
class Solution {
public:
int upper_bound_(int n, int v, vector<int>& a) {
int left=0,right=n-1;
while(left<=right){
int middle=left+(right-left)/2;
if(a[middle]<v) left=middle+1;
else if(a[middle]>=v) right=middle-1;
}
if(left==n) return n+1;
return left+1;
}
}; class Solution: def upper_bound_(self , n , v , a ): # write code here lo = 0 hi = len(a) while lo < hi: mi = (lo + hi) // 2 if a[mi] < v: lo = lo+1 # 这里写错了, 应该是 lo = mi + 1 else: hi = mi return lo+1
class Solution {
public:
/**
* 二分查找
* @param n int整型 数组长度
* @param v int整型 查找值
* @param a int整型vector 有序数组
* @return int整型
*/
int upper_bound_(int n, int v, vector<int>& a) {
// write code here
if(n==0)
return 1;
else if(a[n-1]<v)
return n+1;
else if(v<a[0])
return 1;
else
{
int min=0;
int max=n-1;
while(min<=max)
{
int mid=(min+max)/2;
if(a[mid]==v)
{
while(a[mid]==a[mid-1])
mid=mid-1;
return mid+1;
}
else if(a[mid]<v && a[mid+1]>v)
return mid+2;
else if(a[mid]>v)
{
max=mid-1;
}
else
min=mid+1;
}
return n+1;
}
}
}; /**
* 二分查找
* @param n int整型 数组长度
* @param v int整型 查找值
* @param a int整型一维数组 有序数组
* @param aLen int a数组长度
* @return int整型
*/
int upper_bound_(int n, int v, int* a, int aLen ) {
// write code here
//给了2个数组长度应该都一样吧
if(n <= 0)
{
return n+1;
}
//有序数组,判断一下降序还是升序,降序的话只需要比较第一个
if(a[0] >= a[n-1])
{
if(a[0] < v)
{
return n+1;
}
else
{
return 1;
}
}
//升序的话先比较一下最后一个
if(a[n-1] < v)
{
return n+1;
}
//从左右分别开始查找,左起查找大于等于目标值的,右起查找小于目标值的
int left = 0;
int right = n-1;
while(left < right)
{
if(a[left] >= v)
{
right = n;
break;
}
left++;
if(a[right] >= v)
{
right--;
}
else
{
right++;
left = n;
break;
}
}
if(left < n )
{
return left + 1;
}
if(right < n)
{
return right + 1;
}
return n+1;
} 5,4,[1,2,4,4,5]
3
int upper_bound_(int n, int v, vector<int> &a) {
if (a.back() < v) return n+1;
int l = 0, r = n, m = 0;
while (l < r) {
m = (l + r) >> 1;
if (a[m] >= v) r = mid;
else l = mid + 1;
}
return l + 1;
} 上面的语句 if (a[m] >= v) 改成 大于, 就是c++库里的 upper_bound 函数功能。class Solution {
public:
/**
* 二分查找
* @param n int整型 数组长度
* @param v int整型 查找值
* @param a int整型vector 有序数组
* @return int整型
*/
int upper_bound_(int n, int v, vector<int>& a) {
// write code here
int i = 0, j = n-1;
while(i <= j) {
int m_p = (i + j) / 2;
int m_v = a[m_p];
if(m_v == v) {
if(m_p == 0 || a[m_p - 1] != v) {
return m_p + 1;
} else {
j = m_p - 1;
}
} else if(m_v > v) {
// 1 2 3 4 5
if(m_p == 0 || a[m_p - 1] < v) {
return m_p + 1;
}
j = m_p - 1;
} else if(m_v < v) {
i = m_p + 1;
}
}
return 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) {
// write code here
if(v>a[n-1]){
return n+1;
}
int start = 0;
int end = n-1;
int mid=start+(end-start)/2;
while(start < end){
if(a[mid]>=v){
end = mid;
}else{
start = mid+1;
}
mid = start+(end-start)/2;
}
return mid+1;
}
}
注意:
1.本题数组索引从1开始而非从零开始
2.本题目可转换为:查找元素v在有重复元素的有序数组array中的索引,若数组中不存在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) {
int right = n - 1;
int left = 0;
int middle = 0;
// 循环不变量:
// 若v能被找到,则保证v在[left, right]这个左闭右闭区间中
while (left <= right) {
int mid = left + (right - left) / 2;
// mid处的值和v相等,但是它可能并不是数组中第一个v
// 因此继续缩小查找范围,排除掉重复的元素
if (a[mid] >= v) {
right = mid - 1;
} else {
left = mid + 1;
}
}
// 循环结束时left > right
// 分为2种情况:
// 情况1:找到v,此时left指向v
// 情况2:未找到v,此时left指向v应该被插入的位置
// 因为该题索引从1开始,因此返回值要加1
return left + 1;
}
}
# 二分查找 # @param n int整型 数组长度 # @param v int整型 查找值 # @param a int整型一维数组 有序数组 # @return int整型 # class Solution: def upper_bound_(self , n , v , a ): # write code here l = 0 r = n - 1 while l <= r: m = (l + r) >> 1 if a[m] >= v: r = m - 1 else: l = m + 1 return r + 2
用例输入:100,1,[2,3,4,4,4,7,7,8,10,10,11,12,13,14,15,15,17,18,19,23,24,24,24,24,25,26,26,26,27,27,28,29,29,30,33,36,38,38,40,40,41,43,43,43,44,46,46,47,51,52,52,53,54,56,57,57,57,58,58,61,61,61,62,64,64,66,66,67,67,67,70,72,74,74,74,75,75,78,78,78,79,79,80,83,83,83,83,84,84,86,88,89,89,90,91,91,92,93,93,96] 用例输出:1 实际输出:101