首页 > 编程语言 >数据结构与算法——二分查找(自学笔记)

数据结构与算法——二分查找(自学笔记)

时间:2024-11-21 14:44:31浏览次数:3  
标签:二分 right target nums int mid 查找 数据结构 left

本文参考 二分查找 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台

基本概念

  1. 前提条件:
    • 数组必须是有序的(升序或降序均可)。
  2. 核心思想:
    • 每次比较中间元素与目标元素的关系,将查找区间一分为二。根据目标元素与中间元素的大小关系,决定接下来查找的区间是左半部分还是右半部分。
    • 通过不断折半,缩小查找范围,最终可以在对数时间复杂度内找到目标元素(如果存在的话)。

模板I

int binarySearch(int[] nums, int target){
  if(nums == null || nums.length == 0)
    return -1;

  int left = 0, right = nums.length - 1;
  while(left <= right){
    // Prevent (left + right) overflow
    int mid = left + (right - left) / 2;
    if(nums[mid] == target){ return mid; }
    else if(nums[mid] < target) { left = mid + 1; }
    else { right = mid - 1; }
  }

  // End Condition: left > right
  return -1;
}

题目1:x的平方根

给你一个非负整数 x ,计算并返回 x算术平方根

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

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

示例 1:

输入:x = 4
输出:2

方法1:二分查找

class Solution {
    public int mySqrt(int x) {
        int left = 0, right = x, ans = -1;
        while(left <= right){
            int mid = left + ( right - left) / 2;
            if((long) mid * mid <= x ){
                ans = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return ans;
    }
    
    public static void main(String[] args) {
        Solution obj = new Solution();
        int x = 2147395599;
        int result = obj.mySqrt(x);
        System.out.println(result);
    }
}

注意:

  • int 通常占用 4 字节(32 位),表示的范围是从 -2,147,483,648 到 2,147,483,647。
  • long 通常占用 8 字节(64 位),表示的范围是从 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。

时间复杂度:O(logx),即为二分查找需要的次数。

空间复杂度:O(1)。

题目2:搜索旋转排序字母

整数数组 nums 按升序排列,数组中的值 互不相同

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1

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

方法1:二分查找

核心思路:

定理一:只有在顺序区间内才可以通过区间两端的数值判断target是否在其中。

定理二:判断顺序区间还是乱序区间,只需要对比 left 和 right 是否是顺序对即可,left <= right,顺序区间,否则乱序区间。

定理三:每次二分都会至少存在一个顺序区间。

class Solution {
    

    // search 函数:在旋转排序数组中查找目标元素
    public int search(int[] nums, int target) {
        int n = nums.length;  // 获取数组的长度
        if (n == 0) {  // 如果数组为空,直接返回 -1
            return -1;
        }
        if (n == 1) {  // 如果数组只有一个元素,直接判断是否等于目标值
            return nums[0] == target ? 0 : -1;
        }
        
        int l = 0, r = n - 1;  // 初始化左右指针
        
        // 使用二分查找算法
        while (l <= r) {
            int mid = l + (r - l) / 2;  // 计算中间位置,避免溢出
            if (nums[mid] == target) {  // 如果中间元素是目标值,返回其索引
                return mid;
            }
    
            // 判断左半部分是否是有序的
            if (nums[0] <= nums[mid]) {
                // 如果目标值在左半部分有序的范围内
                if (nums[0] <= target && target < nums[mid]) {
                    r = mid - 1;  // 目标值在左边,调整右指针
                } else {
                    l = mid + 1;  // 目标值不在左半部分,调整左指针
                }
            } else {
                // 如果右半部分是有序的
                if (nums[mid] < target && target <= nums[n - 1]) {
                    l = mid + 1;  // 目标值在右半部分,调整左指针
                } else {
                    r = mid - 1;  // 目标值不在右半部分,调整右指针
                }
            }
        }
        return -1;  // 如果循环结束仍未找到目标值,返回 -1
    }
    
    // main 函数:用于测试
    public static void main(String[] args) {
        Solution obj = new Solution();  // 创建 Solution 类的对象
        int[] nums = {4, 5, 6, 7, 0, 1, 2};  // 旋转排序数组
        int target = 0;  // 要查找的目标值
        int result = obj.search(nums, target);  // 调用 search 函数查找目标值
        System.out.println(result);  // 输出目标值的索引,如果未找到则输出 -1
    }

}

时间复杂度:O(logx),即为二分查找需要的次数。

空间复杂度:O(1)。

模板II

int binarySearch(int[] nums, int target){
  if(nums == null || nums.length == 0)
    return -1;

  int left = 0, right = nums.length;
  while(left < right){
    // Prevent (left + right) overflow
    int mid = left + (right - left) / 2;
    if(nums[mid] == target){ return mid; }
    else if(nums[mid] < target) { left = mid + 1; }
    else { right = mid; }
  }

  // Post-processing:
  // End Condition: left == right
  if(left != nums.length && nums[left] == target) return left;
  return -1;
}

题目1:寻找旋转排序数组的最小值

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

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

方法1:二分查找

fig1

考虑数组中的最后一个元素 x:在最小值右侧的元素(不包括最后一个元素本身),它们的值一定都严格小于 x;而在最小值左侧的元素,它们的值一定都严格大于 x。因此,我们可以根据这一条性质,通过二分查找的方法找出最小值。

class Solution {

    // findMin 函数:用于在旋转排序数组中找到最小值
    public int findMin(int[] nums) {
        int low = 0;  // 初始化左指针为数组的起始位置
        int high = nums.length - 1;  // 初始化右指针为数组的结束位置
        
        // 使用二分查找的方式,查找旋转数组中的最小值
        while (low < high) {
            int point = low + (high - low) / 2;  // 计算中间索引,避免溢出
            
            // 如果中间值小于右边界值,说明最小值在左半部分(包括中间值)
            if (nums[point] < nums[high]) {
                high = point;  // 右指针向左移动
            } else {
                // 如果中间值大于右边界值,说明最小值在右半部分
                low = point + 1;  // 左指针向右移动
            }
        }
        
        // 最终,low 指向的就是最小值的位置
        return nums[low];
    }
    
    // main 函数:用于测试
    public static void main(String[] args) {
        Solution obj = new Solution();  // 创建 Solution 类的对象
        int[] nums = { 5, 1, 2, 3, 4 };  // 旋转排序数组
        int result = obj.findMin(nums);  // 调用 findMin 函数查找最小值
        System.out.println(result);  // 输出最小值
    }

}

模板III

int binarySearch(int[] nums, int target) {
    if (nums == null || nums.length == 0)
        return -1;

    int left = 0, right = nums.length - 1;
    while (left + 1 < right){
        // Prevent (left + right) overflow
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid;
        } else {
            right = mid;
        }
    }
    
    // Post-processing:
    // End Condition: left + 1 == right
    if(nums[left] == target) return left;
    if(nums[right] == target) return right;
    return -1;

}

题目1:在排序数组中找到起始位置

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

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

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

示例 1:

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

方法1:二分查找

采用的是模板I,其实什么模板都不重要。

class Solution {

    public int[] searchRange2(int[] nums, int target){
        int left = 0, right = nums.length - 1;
        int first = -1, last = -1;
        // 找第一个target位置
        while (left <= right){
            int middle = left + (right - left) / 2;
            if(nums[middle] == target){
                first = middle;
                right = middle - 1; // 重点
            } else if (nums[middle] > target) {
                right = middle - 1;
            }else {
                left = middle + 1;
            }
        }

        // 找最后一个target位置
        left = 0;
        right = nums.length - 1;
        while (left <= right){
            int middle = left + (right - left) / 2;
            if(nums[middle] == target){
                last = middle;
                left = middle + 1; // 重点
            } else if (nums[middle] > target) {
                right = middle - 1;
            }else {
                left = middle + 1;
            }
        }
        return new int[]{first, last};
    }

    public static void main(String[] args) {
        Solution obj = new Solution();
        int[] nums = { 1, 1, 2, 2, 3, 3, 3, 4, 5, 9};
        int target = 3;
        int[] result = obj.searchRange2(nums,target);
        System.out.println(result[0]+" "+result[1]);
    }
}

标签:二分,right,target,nums,int,mid,查找,数据结构,left
From: https://blog.csdn.net/weixin_45691264/article/details/143945841

相关文章

  • 【python图解】Python 数据结构之列表和元组
    【python图解】Python数据结构之列表和元组顾名思义,数据结构是能够将数据组合在一起的一种结构。在数学中,很多情况下需要将数据进行有序排列。例如我们统计了某班50人的数据成绩,那么一共50个人。例如Alice=99,Bob=86....无疑是非常繁琐的。这时我们可以通过数......
  • 数据结构实验3
    实验.cpp1//编写程序,实现顺序栈的基本运算:初始化ok、入栈ok、出栈ok;2//应用顺序栈实现数制转换:把任意非负十进制正整数转换为n(n可以为2、8、16等等)进制数输出ok,给出至少5组测试数据及结果。;3//应用顺序栈,编程实现表达式(只包含+、-、*、/四种运算符及左右圆......
  • 数据结构——哈希
    目录一.哈希的相关概念二.哈希函数三.哈希冲突解决1.闭散列1.线性探测2.二次探测2.开散列1.开散列的增容2.开散列的插入3.开散列的查找4.开散列的删除四.整体代码1.HashTable.h2.Hash.cpp一.哈希的相关概念顺序结构以及平衡树中,元素关键码与其存储位置之间......
  • NFLS贪心与数据结构题单笔记(未完结)
    A.奶牛飞车贪心,把最慢的放前面#include<bits/stdc++.h>usingnamespacestd;constexprintmaxn=1e6+10;intn,m,d,L;ints[maxn];intans=0;inlineboolcmp(intx,inty){returnx>y;}intmain(){cin>>n>>m>>d>......
  • 数据结构在二叉树中用子问题思路来解决问题
    二叉树Oj题获取二叉树的节点数获取二叉树的终端节点个数获取k层节点的个数获取二叉树的高度检测为value的元素是否存在判断两颗树是否相同判断是否是另一棵的子树反转二叉树判断一颗二叉树是否是平衡二叉树时间复杂度O(n*n)复杂度O(N)二叉树的遍历判断是否是对称的二叉......
  • Java二分查找算法Collections.binarySearch
    Java二分查找算法Collections.binarySearchpackagecom.example.core.mydemo.javaDemo;importjava.util.ArrayList;importjava.util.Collections;/***二分查找算法是一种高效的查找方法。*该方法要求待查找的集合必须是有序的。索引从0开始*/publicclassBinar......
  • 【数据结构】栈和队列的定义与实现
    主页:HABUO......
  • 【数据结构OJ】【图论】货币套汇(图路径)
    题目描述套汇是指利用货币汇兑率的差异将一个单位的某种货币转换为大于一个单位的同种货币。例如,假定1美元可以买0.7英镑,1英镑可以买9.5法郎,1法郎可以买到0.16美元。通过货币兑换,一个商人可以从1美元开始买入,得到0.7×9.5×0.16=1.064美元,从而获得6.4%的利润。给定n种......
  • 数据结构/第五章 树与二叉树/数据结构习题/树与二叉树的习题/考研/期末复习
    一、选择题1.一棵树中,所有结点的度数之和为n,则该树共有(    )个结点。A.n-1   B.n   C.n+1   D.无法确定2.高度为4的3叉树至多有(    )个结点。A.6   B.27   C.40   D.803.度为m的树中第6层至多有(    ......
  • uniapp项目清理工具:自动查找未使用的组件和资源文件
    uniapp项目清理工具:自动查找未使用的组件和资源文件前言在开发uniapp项目的过程中,随着项目规模的增长,经常会遇到一些组件和资源文件(图片、音频等)不再使用但仍然保留在项目中的情况。这些无用文件不仅占用存储空间,还会影响项目的维护性。为了解决这个问题,我开发了两个No......