牛客经典101题解题集--二分查找/排序

目录

二分查找/排序

二分查找-I

题目

分析

代码

java

python

二维数组中的查找

题目

分析

代码

java

python

寻找峰值

示例1

示例2

题目

分析

代码

java

python

数组中的逆序对

题目

输入描述:

分析

代码

java

python

旋转数组的最小数字

题目

分析

代码

java

python

比较版本号

题目

分析

代码

java

python

总结


二分查找/排序

二分查找-I

题目

请实现无重复数字的升序数组的二分查找

给定一个 元素升序的、无重复数字的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标(下标从 0 开始),否则返回 -1

数据范围:0≤𝑙𝑒𝑛(𝑛𝑢𝑚𝑠)≤2×105, 数组中任意值满足 ∣𝑣𝑎𝑙∣≤109

进阶:时间复杂度 𝑂(log⁡𝑛) ,空间复杂度 𝑂(1)

示例1

输入:[-1,0,3,4,6,10,13,14],13

返回值:6

说明:13 出现在nums中并且下标为 6

示例2

输入:[],3

返回值:-1

说明:nums为空,返回-1

示例3

输入:[-1,0,3,4,6,10,13,14],2

返回值:-1

说明:2 不存在nums中因此返回 -1

备注:数组元素长度在[0,10000]之间

数组每个元素都在 [-9999, 9999]之间。

分析

简单的二分查找法,分为左闭右开和左闭右闭

代码

java

左闭右闭

java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @param target int整型 
     * @return int整型
     */
    public int search (int[] nums, int target) {
        // write code here
        int i=0;
        int j=nums.length-1;
        while(i<=j){
            int mid=(i+j)>>>1;
            if(nums[mid]>target){
                j=mid-1;
            }else if(nums[mid]<target){
                i=mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }
}

左闭右开

java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @param target int整型 
     * @return int整型
     */
    public int search (int[] nums, int target) {
        // write code here
        int i=0;
        int j=nums.length;
        while(i<j){
            int mid=(i+j)>>>1;
            if(nums[mid]>target){
                j=mid;
            }else if(nums[mid]<target){
                i=mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }
}
python

左闭右闭

python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param nums int整型一维数组 
# @param target int整型 
# @return int整型
#
class Solution:
    def search(self , nums: List[int], target: int) -> int:
        # write code here
        i=0
        j=len(nums)-1
        while i<=j:
            mid=(i+j)//2
            if nums[mid]>target:
                j=mid-1
            elif nums[mid]<target:
                i=mid+1
            else:
                return mid
        return -1

左闭右开

python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param nums int整型一维数组 
# @param target int整型 
# @return int整型
#
class Solution:
    def search(self , nums: List[int], target: int) -> int:
        # write code here
        i=0
        j=len(nums)
        while i<j:
            mid=(i+j)//2
            if nums[mid]>target:
                j=mid
            elif nums[mid]<target:
                i=mid+1
            else:
                return mid
        return -1

二维数组中的查找

题目

在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

\[1,2,8,9\], \[2,4,9,12\], \[4,7,10,13\], \[6,8,11,15

]

给定 target = 7,返回 true。

给定 target = 3,返回 false。

数据范围:矩阵的长宽满足 0≤𝑛,𝑚≤500 , 矩阵中的值满足 −109≤𝑣𝑎𝑙≤109

进阶:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑛+𝑚)

示例1

输入:7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]

返回值:true

说明:存在7,返回true

示例2

输入:1,[[2]]

返回值:false

示例3

输入:3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]

返回值:false

说明:不存在3,返回false

分析

这道题如果刚开始建立一个一维数组,把数字都放进去,那么其实空间复杂度是不符合的,所以我们可以直接在原来的二维数组上操作,我们从右上角出发,利用二分法。

那么为什么是右上角呢?

右上角的那个数字是当前行的最大值和当前列的最小值

所以可以根据这个移动。

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param target int整型 
     * @param array int整型二维数组 
     * @return bool布尔型
     */
    public boolean Find (int target, int[][] array) {
        // write code here
        if (array == null || array.length == 0 || array[0].length == 0) {
            return false;
        }
        
        // 从 右上角 开始
        int row = 0;
        int col = array[0].length - 1;
        
        while (row < array.length && col >= 0) {
            if (array[row][col] == target) {
                return true; // 找到了
            } else if (array[row][col] > target) {
                col--; // 太大了,往左走
            } else {
                row++; // 太小了,往下走
            }
        }
        return false; // 遍历完都没找到
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param target int整型 
# @param array int整型二维数组 
# @return bool布尔型
#
class Solution:
    def Find(self , target: int, array: List[List[int]]) -> bool:
        # write code here
        if(len(array)==0):
            return False
        i=0
        j=len(array[0])-1
        while i<len(array) and j>=0:
            if array[i][j]==target:
                return True
            elif array[i][j]<target:
                i+=1
            elif array[i][j]>target:
                j-=1
        return False

寻找峰值

给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。

1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于

2.假设 nums[-1] = nums[n] = −∞−∞

3.对于所有有效的 i 都有 nums[i] != nums[i + 1]

4.你可以使用O(logN)的时间复杂度实现此问题吗?

数据范围:

1≤𝑛𝑢𝑚𝑠.𝑙𝑒𝑛𝑔𝑡ℎ≤2×105

−231<=𝑛𝑢𝑚𝑠[𝑖]<=231−1

如输入[2,4,1,2,7,8,4]时,会形成两个山峰,一个是索引为1,峰值为4的山峰,另一个是索引为5,峰值为8的山峰,如下图所示:

示例1

输入:[2,4,1,2,7,8,4]

返回值:1

说明:4和8都是峰值元素,返回4的索引1或者8的索引5都可以

示例2

输入:[1,2,3,1]

返回值:2

说明:3 是峰值元素,返回其索引 2

题目

分析

为什么右边更大,右边一定有峰值?

  • 右边一直在上升 → 一直走,直到不再上升,那个点就是峰值
  • 如果一直上升到最后一个元素,因为 nums[n] = -∞最后一个元素天然就是峰值

为什么中间更大,左边一定有峰值?

  • 中间比右边大
  • 我们往左边搜索
  • 如果一直下降到第一个元素,因为 nums[-1] = -∞第一个元素天然就是峰值

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int findPeakElement (int[] nums) {
        // write code here
         if(nums.length==1){
            return 0;
        }
        int i=0;
        int j=nums.length-1;
        while(i<j){
            int mid=(i+j)>>>1;
            if(nums[mid]<nums[mid+1]){
                i=mid+1;
            }else{
               j=mid; 
            }
        }
        return j;
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param nums int整型一维数组 
# @return int整型
#
class Solution:
    def findPeakElement(self, nums):
        left = 0
        right = len(nums) - 1
        
        while left < right:
            mid = (left + right) // 2
            if nums[mid] < nums[mid + 1]:
                # 右边更高 → 峰值一定在右边
                left = mid + 1
            else:
                # 左边更高 → 峰值一定在左边
                right = mid
        
        return right

数组中的逆序对

题目

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007

数据范围: 对于 50%的数据, 𝑠𝑖𝑧𝑒≤104

对于 100% 的数据, 𝑠𝑖𝑧𝑒≤105

数组中所有数字的值满足 0≤𝑣𝑎𝑙≤109

要求:空间复杂度 𝑂(𝑛),时间复杂度 𝑂(𝑛𝑙𝑜𝑔𝑛)

输入描述:

题目保证输入的数组中没有的相同的数字

示例1

输入:[1,2,3,4,5,6,7,0]

返回值:7

示例2

输入:[1,2,3]

返回值:0

分析

一直这样划分,得到一个数字一组,然后把数组恢复成有序的,如果当前数字大于右半部分的,那么当前数字的后面也会大于右半部分,也能组成逆序对

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
     int MOD=1000000007;
     int count=0;
    public int InversePairs (int[] nums) {
        // write code here
         if (nums == null || nums.length <= 1) {
            return 0;
        }
        count = 0;
        merge(nums, 0, nums.length - 1);
        return count % MOD;
    }
    public void merge(int [] nums,int left,int right){
        if(right<=left){
            return;
        }
        int mid=(left+right)>>>1;
        merge(nums,left,mid);
        merge(nums,mid+1,right);
        merge2(nums,left,mid,right);
    }
    public void merge2(int [] nums,int left,int mid,int right){
        int [] temp=new int[right-left+1];
        int i=left;
        int j=mid+1;
        int index=0;
        while(i<=mid && j<=right){
            if(nums[i]<=nums[j]){
                temp[index++]=nums[i++];
            }else{
                 count = (count + mid - i + 1) % MOD;
                temp[index++] = nums[j++];
            }
        }

     while (i <= mid) temp[index++] = nums[i++];
        while (j <= right) temp[index++] = nums[j++];
         
        // 复制回原数组
        for (int k = 0; k < temp.length; k++) {
            nums[left + k] = temp[k];
        }
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param nums int整型一维数组 
# @return int整型
#
class Solution:
    
    MOD=1000000007
    def InversePairs(self , nums: List[int]) -> int:
        # write code here
        if len(nums)==0:
            return 0
        self.count=0
        self.merge(nums,0,len(nums)-1)
        return self.count
        
    
    def merge(self,nums,left,right):
        if left>=right:
            return
        mid=(left+right)//2
        self.merge(nums,left,mid)
        self.merge(nums,mid+1,right)
        return self.merge2(nums,left,mid,right)
    def merge2(self,nums,left,mid,right):
        temp=[0] * (right-left+1)
        i=left
        j=mid+1
        index=0
        while i<=mid and j<=right:
            if nums[i]<=nums[j]:
                temp[index]=nums[i]
                index+=1
                i+=1
            else:
                self.count=(self.count+mid-i+1)%self.MOD
                temp[index]=nums[j]
                index+=1
                j+=1
        while i<=mid:
            temp[index]=nums[i]
            index+=1
            i+=1
        while j<=right:
            temp[index]=nums[j]
            index+=1
            j+=1
        for i in range(len(temp)):
            nums[left+i]=temp[i]

旋转数组的最小数字

题目

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

数据范围:1≤𝑛≤10000,数组中任意元素的值: 0≤𝑣𝑎𝑙≤10000

要求:空间复杂度:O(1) ,时间复杂度:O(logn)

示例1

输入:[3,4,5,1,2]

返回值:1

示例2

输入:[3,100,200,3]

返回值:3

分析

这道题要找最小的,他反转以后是分阶段的,和中间的数字比,如果中间的数字大于最后一个数字,那么最小的数字一定在右边,不然一定在左边,如果相等的话那就说明是重复 的数字,没有办法判断,只能一点一点缩小范围。以至于为什么要和最后一个数字比,不和第一个数字比,很浅显易懂的是最后一个数字一定比第一个数字小。更接近于最小的数字

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // write code here
        int i=0;
        int j=nums.length-1;
        while(i<j){
            int mid=(i+j)>>>1;
            if(nums[mid]<nums[j]){
                j=mid;
            }else if(nums[mid]>nums[j]){
                i=mid+1;
            }else{
                j--;//有重复数字
            }
        }
        return nums[i];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param nums int整型一维数组 
# @return int整型
#
class Solution:
    def minNumberInRotateArray(self , nums: List[int]) -> int:
        # write code here
        i=0
        j=len(nums)-1
        while i<j:
            mid=(i+j)//2
            if nums[mid]>nums[j]:
                i=mid+1
            elif nums[mid]<nums[j]:
                j=mid
            else:
                j-=1
        return nums[i]

比较版本号

题目

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

数据范围:1≤𝑛≤10000,数组中任意元素的值: 0≤𝑣𝑎𝑙≤10000

要求:空间复杂度:𝑂(1) ,时间复杂度:𝑂(𝑙𝑜𝑔𝑛)

示例1

输入:[3,4,5,1,2]

返回值:1

示例2

输入:[3,100,200,3]

返回值:3

分析

难点在于java语言如何去掉前导0

代码

java
java 复制代码
public class Solution {
    public int comp(String num1, String num2) {
        if (num1.length() > num2.length()) return 1;
        else if (num1.length() < num2.length()) return -1;
        else
            for (int i = 0; i < num1.length(); i++)
                if (num1.charAt(i) != num2.charAt(i))
                    return num1.charAt(i) - num2.charAt(i)>0?1:-1;
        return 0;
    }
    public int compare (String version1, String version2) {
        // write code here
        String[] v1Arr = version1.split("\\.0*(?!0\\.)");
        String[] v2Arr = version2.split("\\.0*(?!0\\.)");
        for (int i = 0; i < (v1Arr.length<v2Arr.length?v1Arr.length:v2Arr.length); i++) {
            int r = comp(v1Arr[i], v2Arr[i]);
            if (r != 0) return r;
        }
        return v1Arr.length==v2Arr.length?0:(v1Arr.length-v2Arr.length)>0?1:-1;
    }
}
python
python 复制代码
class Solution:
    def compare(self, version1: str, version2: str) -> int:
        # 分割并转成整数(自动处理前导零)
        v1 = list(map(int, version1.split('.')))
        v2 = list(map(int, version2.split('.')))
        
        # 补齐末尾的0,让两个版本段数一样
        max_len = max(len(v1), len(v2))
        v1 += [0] * (max_len - len(v1))
        v2 += [0] * (max_len - len(v2))
        
        # 逐段比较
        for a, b in zip(v1, v2):
            if a > b:
                return 1
            elif a < b:
                return -1
        return 0

总结

二分法考查形式:

单纯的写二分法

找数组中最小的数字

总体来说二分法没有什么太难的,弄清楚题意即可

相关推荐
lihihi2 小时前
CF1992F Valuable Cards
算法
Omics Pro2 小时前
癌症亚型分类新型多组学整合框架
大数据·人工智能·python·算法·机器学习·分类·数据挖掘
熬夜敲代码的猫2 小时前
C++:模板精讲
c++·算法·模板
帅次2 小时前
Android 高级工程师面试参考答案:架构设计、Jetpack 与 Compose
android·面试·职场和发展·架构·composer·jetpack
MegaDataFlowers2 小时前
3.无重复字符的最长子串
算法
人道领域2 小时前
【LeetCode刷题日记】20.有效的括号
算法·leetcode·职场和发展
生信研究猿2 小时前
#P3492.第1题-基于决策树预判资源调配优先级
python·算法·决策树
贾斯汀玛尔斯2 小时前
每天学一个算法--缓存淘汰策略(LRU / LFU · 结构与复杂度)
算法·缓存
大熊背2 小时前
一套为硬件加速设计的经典边缘检测流水线(一)----边缘细化原理
人工智能·算法·计算机视觉·梯度计算