数组 —— 二分查找

二分查找

参考链接

1. 理论基础

  • 前提条件:数组有序,且无重复元素(如果包含重复元素的话,那么二分查找法返回的下标元素可能不是唯一的)
  • 对区间的定义:即左闭右闭[left, right],或者左闭右开[left, right)

2. 实践

704 二分查找

力扣题目链接
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例1:

    输入: nums = [-1,0,3,5,9,12], target = 9
	输出: 4
	解释: 9 出现在 nums 中并且下标为 4

示例2:

   输入: nums = [-1,0,3,5,9,12], target = 2
   输出: -1
   解释: 2 不存在 nums 中因此返回 -1

提示:

  • 你可以假设 nums 中的所有元素是不重复的。
  • n 将在 [1, 10000]之间。
  • nums 的每个元素都将在 [-9999, 9999]之间。

代码如下:

Java版本如下:

class Solution {
   public int search(int[] nums, int target) {
       if (target < nums[0] || target > nums[nums.length - 1]) {
           return - 1;
       }
/* 
        // 第一种,区间左闭右闭
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int middle = (left + right) / 2;
            if (nums[middle] > target)
                // 已知nums[middle] > target 且 区间右闭,故如下:
                right = middle - 1;
            else if (nums[middle] < target)
                left = middle + 1;
            else 
                return middle;
        }

 */     
        // 第二种,区间左闭右开
        int left = 0, right = nums.length;
        while (left < right) {
            int middle = (left + right) / 2;
            if (nums[middle] > target)
                // 已知nums[middle] > target 且 区间右开,故如下:
                right = middle;
            else if (nums[middle] < target)
                left = middle + 1;
            else 
                return middle;
        }
        return -1;
    }
}

JavaScript版本如下:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    if (target < nums[0] || target > nums[nums.length - 1]) {
        return -1;
    }
/* 
    //第一种写法:左闭右闭
    let left = 0, 
        right = nums.length - 1;
    while (left <= right) {
        let middle = Math.floor((left + right) / 2);
        if (nums[middle] > target) 
            right = middle - 1;
        else if (nums[middle] < target)
            left = middle + 1;
        else
            return middle;
    }
    return -1;
 */
    //第二种写法:左闭右开
    let left = 0, 
        right = nums.length;
    while (left < right) {
        let middle = Math.floor((left + right) / 2);
        if (nums[middle] > target) 
            right = middle;
        else if (nums[middle] < target)
            left = middle + 1;
        else
            return middle;
    }
    return -1;
};

Python3版本如下:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1
        while left <= right:
            middle = (left + right) // 2
            if nums[middle] < target:
                left = middle + 1
            elif nums[middle] > target:
                right = middle - 1
            else:
                return middle
        return -1
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)

35. 搜索插入位置

力扣题目链接
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

示例1:

	输入: nums = [1,3,5,6], target = 5
	输出: 2

示例2:

	输入: nums = [1,3,5,6], target = 2
	输出: 1

示例3:

	输入: nums = [1,3,5,6], target = 7
	输出: 4

提示:

  • 1 <= nums.length <= 1 0 4 10^4 104
  • - 1 0 4 10^4 104 <= nums[i] <= 1 0 4 10^4 104
  • nums 为 无重复元素 的 升序 排列数组
  • - 1 0 4 10^4 104<= target <= 1 0 4 10^4 104

代码如下:

Java版本如下:

class Solution {
    public int searchInsert(int[] nums, int target) {

    // 第一种,区间左闭右闭
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int middle = (left + right) / 2;
        if (nums[middle] > target)
            // 已知nums[middle] > target 且 区间右闭,故如下:
            right = middle - 1;
        else if (nums[middle] < target)
            left = middle + 1;
        else 
            return middle;
    }
    return right + 1;
    
    //分别处理以下四种情况(针对左闭右闭)
    //目标值小于数组所有元素,return right + 1
    //目标值被找到,return middle
    //目标值大于数组所有元素,return right + 1
    //目标值插入数组中的位置[left,right], return right + 1

/* 
        // 第二种,区间左闭右开
        int left = 0, right = nums.length;
        while (left < right) {
            int middle = (left + right) / 2;
            if (nums[middle] > target)
                // 已知nums[middle] > target 且 区间右开,故如下:
                right = middle;
            else if (nums[middle] < target)
                left = middle + 1;
            else 
                return middle;
        }
        return right;
 */
    }
}

JavaScript版本如下:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var searchInsert = function(nums, target) {

    //第一种写法:左闭右闭
    let left = 0, 
        right = nums.length - 1;
    while (left <= right) {
        let middle = Math.floor((left + right) / 2);
        if (nums[middle] > target) 
            right = middle - 1;
        else if (nums[middle] < target)
            left = middle + 1;
        else
            return middle;
    }
    return right + 1;

    //分别处理以下四种情况(针对左闭右闭)
    //目标值小于数组所有元素,return right + 1
    //目标值被找到,return middle
    //目标值大于数组所有元素,return right + 1
    //目标值插入数组中的位置[left,right], return right + 1

/*  
    //第二种写法:左闭右开
    let left = 0, 
        right = nums.length;
    while (left < right) {
        let middle = Math.floor((left + right) / 2);
        if (nums[middle] > target) 
            right = middle;
        else if (nums[middle] < target)
            left = middle + 1;
        else
            return middle;
    }
    return right;

 */
};

Python3代码如下:

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1
        while left <= right:
            middle = (left + right) // 2
            if nums[middle] < target:
                left = middle + 1
            elif nums[middle] > target:
                right = middle - 1
            else:
                return middle
        return right + 1

36. 搜索插入位置

力扣题目链接
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例1:

	输入:nums = [5,7,7,8,8,10], target = 8
	输出:[3,4]

示例2:

	输入:nums = [5,7,7,8,8,10], target = 6
	输出:[-1,-1]

示例3:

	输入:nums = [], target = 0
	输出:[-1,-1]

提示:

  • 0 <= nums.length <= 1 0 5 10^5 105
  • - 1 0 9 10^9 109 <= nums[i] <= 1 0 9 10^9 109
  • nums 是一个非递减数组
  • - 1 0 9 10^9 109<= target <= 1 0 9 10^9 109

代码如下:

Java版本如下:
方法一:

public class LeetCode34 {
    /**
     *  方法一:先判断目标值是否存在,如果存在,左右滑动指针,确定边界值
     */
    public int[] searchRange(int[] nums, int target) {
        int index = binarySearch(nums, target);
        if (index == -1) {
            return new int[] {-1, -1};
        }
        int left = index, right = index;
//        滑动左右指针,找到first and last
        while (left - 1 >= 0 && nums[left - 1] == target) {
            left--;
        }
        while (right + 1 < nums.length && nums[right + 1] == target) {
            right++;
        }
        return new int[] {left, right};
    }

    /**
     * @param nums
     * @param target
     * @Method binarySearch
     * @Author Ami
     * @Version 1.0
     * @Description 二分查找,判断target是否存在
     * @Return int
     * @Exception
     * @Date 2023/5/5 20:44
     */
    public int binarySearch(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int middle = (left + right) / 2;
            if (nums[middle] > target) {
                right = middle - 1;
            } else if (nums[middle] < target) {
                left = middle + 1;
            } else {
                return middle;
            }
        }
        return -1;
    }
}

方法二:

/**
 * 方法二:寻找target的左右边界,根据题意,则会存在三种情况
 * 情况一:target在数组范围的左边或者右边,则返回【-1,-1】
 * 情况二:target在数组范围内,但是在数组中不存在,也返回【-1,-1】  如nums = [5,7,7,8,8,10], target = 6
 * 情况三:target存在于数组中,
 */
public int[] searchRange(int[] nums, int target) {
    int rightBorder = getRightBorder(nums, target);
    int leftBorder = getLeftBorder(nums, target);
    // 情况一
    if (rightBorder == -2 || leftBorder == -2) {
        return new int[]{-1, -1};
    }
    // 情况三
    if (rightBorder - leftBorder > 1) {
        return new int[]{leftBorder + 1, rightBorder - 1};
    }
    // 情况二: 如果target不存在于数组nums中,那么其左右边界之差一定为1
    return new int[]{-1, -1};
}

/**
 * @param nums
 * @param target
 * @Method getRightBorder
 * @Author Ami
 * @Version 1.0
 * @Description 得到target在数组中的右边界,需要注意的是,如果其值不等于-2,那么这个右边界的值比target在数组中的最右边索引值大1
 * @Return int
 * @Exception
 * @Date 2023/5/6 9:16
 */
public int getRightBorder(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    /**
     * 初始化右边界为-2,意味着开始不存在
     * 示例:nums = [5,7,7,8,8,10], target = 8
     */
    int rightBorder = -2;
    while (left <= right) {
        int middle = (left + right) / 2;
        if (nums[middle] > target) {
            right = middle - 1;
        } else {
            left = middle + 1;
            rightBorder = left;
        }
    }
    return rightBorder;
}

/**
 * @param nums
 * @param target
 * @Method getLeftBorder
 * @Author Ami
 * @Version 1.0
 * @Description 得到target在数组中的左边界,需要注意的是,如果其值不等于-2,那么这个左边界的值比target在数组中的最左边索引值小1
 * @Return int
 * @Exception
 * @Date 2023/5/6 9:25
 */
public int getLeftBorder(int[] nums, int target) {
    int leftBorder = -2;
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int middle = (left + right) / 2;
        if (nums[middle] < target) {
            left = middle + 1;
        } else {
            right = middle - 1;
            leftBorder = right;
        }
    }
    return leftBorder;
}

JavaScript版本如下:
方法一:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
    let index = binarySearch(nums, target);
    if (index == -1)
        return [-1, -1];
    let left = right = index;
    while (left - 1 >= 0 && nums[left - 1] == target) {
        left--;
    }
    while (right + 1 < nums.length && nums[right + 1] == target) {
        right++;
    }
    return [left, right];
};

function binarySearch(nums, target) {

    let left = 0, right = nums.length - 1;
    while (left <= right) {
        let middle = Math.floor((left + right) / 2);
        if (nums[middle] > target) 
            right = middle - 1;
        else if (nums[middle] < target)
            left = middle + 1;
        else
            return middle;
    }
    return -1;
}

方法二:

let getRightBorder = function(nums, target) {
    let rightBorder = -2;
    let left = 0, right = nums.lenght - 1;
    while (left <= right) {
        let middle = Math.floor((left + right) / 2);
        if (nums[middle] > target)
            right = middle - 1;
        else {
            left = middle + 1;
            rightBorder = left
        }
    }
    return rightBorder;
}

let getLeftBorder = function(nums, target) {
    let leftBorder = -2;
    let left = 0, right = nums.lenght - 1;
    while (left <= right) {
        let middle = Math.floor((left + right) / 2);
        if (nums[middle] < target)
            left = middle + 1;
        else {
            right = middle - 1;
            leftBorder = right;
        }
    }
    return leftBorder;
}

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
    let leftBorder = getLeftBorder(nums, target),
        rightBorder = getRightBorder(nums, target);
    if (leftBorder == -2 || rightBorder == -2) 
        return [-1, -1];
    if (rightBorder - leftBorder > 1)
        return [leftBorder + 1, rightBorder - 1];
    return [-1, -1];
};

Python3代码如下:

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        indexOfTarget = self.getIndexOfTarget(nums, target)
        if indexOfTarget == -1:
            return [-1, -1]
        else:
            left = right = indexOfTarget
            while left >= 0 and nums[left] == target:
                left -= 1
            while right < len(nums) and nums[right] == target:
                right += 1
            return [left + 1, right - 1]

    def getIndexOfTarget(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1
        while left <= right:
            middle = (left + right) // 2
            if nums[middle] < target:
                left = middle + 1
            elif nums[middle] > target:
                right = middle - 1
            else:
                return middle
        return -1

69.x的平方根

力扣题目链接
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

示例1:

	输入:x = 4
	输出:2

示例2:

	输入:x = 8
	输出:2
	解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

提示:

  • 0 <= x <= 2 32 − 1 2^{32}-1 2321

代码如下:

Java版本如下:

public class LeetCode69 {
    public int mySqrt(int x) {
        int left = 0,
                right = x,
                ans = -1;
        while (left <= right) {
            int middle = (left + right) / 2;
            if ((long) middle * middle <= x) {
                left = middle + 1;
                ans = middle;
            } else {
                right = middle - 1;
            }
        }
        return ans;
    }
}

JavaScript版本如下:

	/**
 * @param {number} x
 * @return {number}
 */
let mySqrt = function(x) {
    let left = 0,
        right = x,
        ans = -1;

    while (left <= right) {
        let middle = Math.floor((left + right) / 2);
        if (middle * middle <= x) {
            ans = middle;
            left = middle + 1;
        } else {
            right = middle - 1;
        }
    }
    return ans;
};

Python版本代码如下:

class Solution:
    def mySqrt(self, x: int) -> int:
        left, right, ans = 0, x, -1
        while left <= right:
            middle = (left + right) // 2
            if middle ** 2 <= x:
                left = middle + 1
                ans = middle
            else:
                right = middle - 1
        return ans
367.有效的完全平方数

力扣题目连接
给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。

不能使用任何内置的库函数,如 sqrt 。

示例1:

	输入:num = 16
	输出:true
	解释:返回 true ,因为 4 * 4 = 16 且 4 是一个整数。

示例2:

	输入:num = 14
	输出:false
	解释:返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。

提示:

  • 1 <= num <= 2 31 − 1 2^{31} - 1 2311

Java代码如下:

public class LeetCode367 {
    public boolean isPerfectSquare(int num) {
        int left = 1,
                right = num;

        while (left <= right) {
            int middle = (left + right) / 2;
            if ((long) middle * middle < num) {
                left = middle + 1;
            } else if ((long) middle * middle > num) {
                right = middle - 1;
            } else {
                return true;
            }
        }
        return false;
    }
}

Python版本代码如下:

class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        left, right = 0, num
        while left <= right:
            middle = (left + right) // 2
            if middle ** 2 < num:
                left = middle + 1
            elif middle ** 2 > num:
                right = middle - 1
            else:
                return True
        return False
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个关于排序和二分查找的问题。 首先,我们需要对待查找的数据进行排序,这里就采用一种简单的排序算法——冒泡排序。冒泡排序的基本思想是:从第一个元素开始,比较相邻的两个元素大小,如果前一个元素大于后一个元素,则交换它们的位置,依次比较下去,直到最后一个元素。 接下来,我们可以用二分查找查找指定的元素。二分查找的基本思想是:将待查找的区间分成两个部分,取中间位置的元素与目标元素进行比较,如果相等,则找到了目标元素;如果目标元素大于中间元素,则在后半部分继续查找;如果目标元素小于中间元素,则在前半部分继续查找。 下面是示例代码: ```python def bubble_sort(arr): n = len(arr) for i in range(n): for j in range(0, n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] def binary_search(arr, left, right, target): if left > right: return -1 mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: return binary_search(arr, mid+1, right, target) else: return binary_search(arr, left, mid-1, target) # 测试代码 arr = [5, 2, 8, 3, 9, 1] bubble_sort(arr) print(arr) print(binary_search(arr, 0, len(arr)-1, 3)) ``` 输出结果: ``` [1, 2, 3, 5, 8, 9] 2 ``` 在这个示例代码中,我们先用冒泡排序将待查找数组进行了排序,然后调用二分查找函数查找目标元素 3,最终返回了它的索引位置 2。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值