二分查找由浅入深--算法--java

news2025/1/16 19:10:25

二分查找

  • 写在开头
  • 算法前提:
  • 算法逻辑
  • 算法实现
    • 简单实现
    • left+right可能超过int表示的最大限度
    • 代码分析和变换
    • 更多需求:求索引最小的值
    • java二分API
  • 应用
    • 基础题
    • 思考难度
    • 方法

写在开头

二分查找应该是算比较简单的这种算法了,我本以为还可以。但有时候也没想到过能这么用,最震惊的就是对答案进行二分了。而且有时候不够熟练,还有我遇到的问题都总结一下。

算法前提:

数据需要有序,可以重复元素。

算法逻辑

以升序数列为例,
比较一个元素与数列中的中间位置的元素的大小
1.如果比中间位置的元素大,则继续在后半部分的数列中进行二分查找;
2.如果比中间位置的元素小,则在数列的前半部分进行比较;
3.如果相等,则找到了元素的位置。
每次比较的数列长度都会是之前数列的一半,直到找到相等元素的位置或者最终没有找到要找的元素。

算法实现

简单实现

// 二分查找
public static int binarySearch(int[] a, int target) {
    int left = 0;
    int right = a.length - 1;
     while (left <= right) {
        int mid = (left + right) / 2;
        if (a[mid] == target) {
            return mid;
        } else if (a[mid] < target) {
            left = mid + 1;
        } else {
           right = mid - 1;
       }
   }
   return -1; 
}

left+right可能超过int表示的最大限度

出现结果:mid算出来为负数。
java中int类型占4个字节的最大值是 2 31 − 1 2^{31}-1 2311, 即 2147483647
如果超过了表示范围,则会发送越界,则会加到符号位。
那么我们可以进行无符号右移就可以完成除法运算,和解决越界问题了。
(有没有可能超过32位,2个int数相加最大不会大于 2 32 − 1 2^{32}-1 2321,所以一定不会超过int存储)
所以代码修改如下

int mid =(left+right)>>>2;

代码分析和变换

简单实现里面:还是建议把等值放到最后面,因为等值的可能性是最低的,这样也就可以降低判断的次数。
把谁放第一判断,则那一边效率会高一点,如果下代码,如果数据偏右,则效率可能高些。

if (a[mid] < target) {
     left = mid + 1;
} else if (a[mid] > target) {
    right = mid - 1;
} else {
   return mid;
}

最坏情况 O ( log ⁡ n ) O(\log n) O(logn)
最好情况 O ( 1 ) O(1) O(1)
空间复杂度 O ( 1 ) O(1) O(1)

均衡性:每次循环必定需要一次判断

public static int binarySearch(int[] a, int target) {
    int left = 0;
    int right = a.length;
     while (right - left <= 1) {
        int mid = (left + right) / 2;
        if (target < a[mid]) {
            right = mid;
        } else{
            left = mid;
        }
       }
       return a[i]==target?i:-1;
   }
   return -1; 
}

时间复杂度 Ω ( log ⁡ n ) \Omega(\log n) Ω(logn)

更多需求:求索引最小的值

public static int binarySearchMin(int[] a, int target){
	int l=0,r=a.length;
	while(l < r){
		int m = (l + r) >>> 1;
		if (a[m] < target)
			l = m + 1;
		else
			r = m;
	}
	return l;
}

注意:如果数据不存在则返回的是切入点
不想这样的话可以

return a[l]==target?l:-(l+1);

java二分API

在Arrays类中有一个binarySearch()的方法可以返回数组中二分查找的结果
使用:

binarySearch(数组,key)

在这里插入图片描述
源代码:和我们实现的差不多。
在这里插入图片描述

格外的api

public static int binarySearch(int[] a, int fromIndex, int toIndex,int key)
fromIndex – 要搜索的第一个元素(包括)的索引 
toIndex – 要搜索的最后一个元素(独占)的索引

应用

基础题

力扣278. 第一个错误的版本
解决:二分求索引最小


public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int l=1;int r=n;
        while(l < r){
            int mid=(l+r)>>>1;
            if(isBadVersion(mid))
                r=mid;
            else
                l=mid+1;
        }
        return l;
    }
}

leetcode34. 在排序数组中查找元素的第一个和最后一个位置

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。

public int[] searchRange(int[] nums, int target) {
        int x = -1, y = -1;
        int l = 0, r = nums.length - 1;
        while (l < r) {
            int m = (l + r) >>> 1;
            if (nums[m] >= target)
                r = m;
            else
                l = m + 1;
        }
        if (l < 0 || l > nums.length-1||nums[l] != target)
            return new int[]{x, y};
        x = l;
        r = nums.length - 1;
        while (l < r) {
            int m = (l + r) >>> 1;
            if (nums[m] > target)
                r = m - 1;
            else
                l = m + 1;
        }
        y = l - 1;
        return new int[]{x, y};
    }

思考难度

leetcode 4. 寻找两个正序数组的中位数
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数.
算法的时间复杂度应该为 O(log(m+n)) 。

分析:
l=n+m为总数
求中位数:

  1. 如果l为奇数,则中位数为(l+1)/2的位置
  2. 如果为偶数,则中位数为(l+1)/2和(l+2)/2的平均数

即求指定位置k的数。

在每次比较k/2位置的2个数组上的数如果n1[k/2]<n2[k/2]则n1上k/2以前的数都在合并k位置之前。
如:
n1=[1,2,4,5,6]
n2=[2,3,5,7,9,11]
l=11 k=6 k/2=3
n1[3]=4<n2[3]=5
则1,2,4都会在合并数组k位置的前面

n1=[5,6]
n2[2,3,5,7,9,11]
此时k=3 k/2=1
比较5和2

所以
n1=[5,6]
n2[3.5,7,9,11]
此时k=2,k/2=1
去掉一个3

k=1
返回2个最开始的小值就行。

问:
1.能不能在k=2的时候返回大值呢?
不行的,如果一个数组1,2另一个3,4就不可用。
2.如果k/2大于一个数组长度呢?
那就取这个数组最后一个就行了,这样要么不用考虑这个数组了,要么长数组会变短。

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int n = nums1.length;
    int m = nums2.length;
    int left = (n + m + 1) / 2;
    int right = (n + m + 2) / 2;
    //将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
    return (search(nums1, 0, nums2, 0, left) + search(nums1, 0, nums2, 0, right)) * 0.5; 
}
// a为一个数组,i为这个数组剩下的.b同理
// k 为需要求的数的位置
public int search(int[] a,int i,int[] b,int j,int k){
	//求剩余长度
	int l1 = a.length-i;
	int l2 = b.length-j;
	//调整l1永远为短的
	if(l1>l2) return search(b,j,a,i,k);
	
	if(l1 == 0) return b[j+k-1];
	if (k == 1) return Math.min(a[i], b[j]);
	int q = k/2;
	//x,y 为索引
	int x = i + Math.min(q,l1) - 1;
	int y = j + Math.min(q,l2) - 1;

	if(a[x] > b[y])
		return search(a,i,b,y+1,k-(y-j+1));
	else
		return search(a,x+1,b,j,k-(x-i+1));
}

方法

leetcode2439. 最小化数组中的最大值
给你一个下标从 0 开始的数组 nums ,它含有 n 个非负整数。
每一步操作中,你需要:
选择一个满足 1 <= i < n 的整数 i ,且 nums[i] > 0 。
将 nums[i] 减 1 。
将 nums[i - 1] 加 1 。
你可以对数组执行任意次上述操作,请你返回可以得到的 nums 数组中最大值最小为多少。

示例 1:

输入:nums = [3,7,1,6]
输出:5
解释:
一串最优操作是:
1. 选择 i = 1 ,nums 变为 [4,6,1,6] 。
2. 选择 i = 3 ,nums 变为 [4,6,2,5] 。
3. 选择 i = 1 ,nums 变为 [5,5,2,5] 。
nums 中最大值为 5 。无法得到比 5 更小的最大值。
所以我们返回 5 。

示例 2:

输入:nums = [10,1]
输出:10
解释:
最优解是不改动 nums ,10 是最大值,所以返回 10 。

提示:
n = = n u m s . l e n g t h n == nums.length n==nums.length
2 < = n < = 1 0 5 2 <= n <= 10^5 2<=n<=105
0 < = n u m s [ i ] < = 1 0 9 0 <= nums[i] <= 10^9 0<=nums[i]<=109

分析:
这个题目是我第一次遇到二分答案的题目,属实打开了我的新世界了。
对答案进行二分。
1.根据题目要求右边只能降,左边只能升。第一个值可以从后面所有的值里面取过来。
2.所以对一个最大值m是否符合条件,我们可以从左往右遍历,如果这个数k比他小那么,则需要m-k个位置,如果比他大,那么把需要减掉,这时如果需求为负数,则一定不是这个数。

但是最大值如果很大,那么都会是需要位置而没有剪掉的呢?
因为我们二分会去找最小可能,即找到符合的最左边就行。

public int minimizeArrayValue(int[] nums) {
	int l = 0,r = Integer.MAX_VALUE;
	while(l < r){
		int m = (l+r)>>>1;
		if(check(nums,m))
			r = m;
		else
			l = m+1;
	}
	return r;
}
public static boolean check(int[]a,int m){
	long count = 0;
	for(int i:a){
		if(i<m)
			count+=m-i;
		else{
			count -= i-m;
			if(count < 0)
			return false;
		}
	}
	return true;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/378889.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Word处理控件Aspose.Words功能演示:使用 Java 比较 MS Word 文档

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

动态规划初阶-爬楼梯问题

示例1&#xff1a; 输入&#xff1a;cost [10,15,20] 输出&#xff1a;15 解释&#xff1a;你将从下标为 1 的台阶开始。 - 支付 15 &#xff0c;向上爬两个台阶&#xff0c;到达楼梯顶部。 总花费为 15 。示例2&#xff1a; 输入&#xff1a;cost [1,100,1,1,1,100,1,1,10…

使用Docker安装MongoDB,整合SpringBoot

使用Docker安装MongoDB MongoDB 和 MySQL 都是常用的数据库管理系统&#xff0c;但它们的设计目标不同&#xff0c;因此在某些方面的性能表现也有所不同。 MongoDB 是一个文档型数据库&#xff0c;它采用了面向文档的数据模型&#xff0c;支持动态查询和索引&#xff0c;适合…

Docker部署实战

文章目录Docker部署应用准备制作容器镜像启动容器上传镜像docker exec数据卷&#xff08;Volume&#xff09;声明原理实践Docker部署 应用准备 这一次&#xff0c;我们来用 Docker 部署一个用 Python 编写的 Web 应用。这个应用的代码部分&#xff08;app.py&#xff09;非常…

【同步、共享和内容协作软件】上海道宁与​ownCloud让您的团队随时随地在任何设备上轻松处理数据

ownCloud是 一款开源文件同步、共享和 内容协作软件 可让团队随时随地 在任何设备上轻松处理数据 ownCloud开发并提供 用于内容协作的开源软件 使团队能够轻松地无缝 共享和处理文件 而无需考虑设备或位置 开发商介绍 ownCloud成立于2010年&#xff0c;是一个托管和同…

设计模式-笔记

文章目录七大原则单例模式桥模式 bridge观察者模式 observer责任链模式 Chain of Responsibility命令模式 Command迭代器模式 Iterator中介者模式 Mediator享元模式 Flyweight Pattern组合模式 composite装饰模式 Decorator外观模式 Facade简单工厂模式工厂方法模式工厂抽象模式…

Postgresql中的unlogged table

在PG中&#xff0c;有一种表的类型为unlogged table&#xff0c;名如其字&#xff0c;该种类型的表不会写入wal日志中&#xff0c;所以在写入的速度上比普通的堆表快很多&#xff0c;但是该表在数据库崩溃的时候&#xff0c;会被truncate,数据会丢失&#xff0c;而且该表也不支…

Leetcode21. 合并两个有序链表

一、题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4]输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2…

Java程序开发中如何使用lntelliJ IDEA?

完成了IDEA的安装与启动&#xff0c;下面使用IDEA创建一个Java程序&#xff0c;实现在控制台上打印HelloWorld!的功能&#xff0c;具体步骤如下。 1.创建Java项目 进入New Project界面后&#xff0c;单击New Project选项按钮创建新项目&#xff0c;弹出New Project对话框&…

【k8s】Kubernetes的学习(1.k8s概念和架构)

目录 1.首先要知道&#xff0c;Kubernetes为什么简称为k8s? 2.Kubernetes概述 2.1 kubernetes基本介绍 2.2 kubernetes的特性 2.3 kubernetes集群架构组件 2.3.1 Master (主控节点) 2.3.2 node (工作节点) 2.4 k8s核心概念 2.4.1 Pod 2.4.2 controller 2.4.3 Se…

操作系统权限提升(十九)之Linux提权-SUID提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 SUID提权 SUID介绍 SUID是一种特殊权限&#xff0c;设置了suid的程序文件&#xff0c;在用户执行该程序时&#xff0c;用户的权限是该程序文件属主的权限&#xff0c;例如程序文件的属主是root&#xff0c;那么执行该…

redux-saga

redux-saga 官网&#xff1a;About | Redux-Saga 中文网&#xff1a;自述 Redux-Saga redux-saga 是一个用于管理 异步获取数据(副作用) 的redux中间件&#xff1b;它的目标是让副作用管理更容易&#xff0c;执行更高效&#xff0c;测试更简单&#xff0c;处理故障时更容易… …

C#:Krypton控件使用方法详解(第十讲) ——kryptonColorButton

今天介绍的Krypton控件中的kryptonColorButton&#xff0c;下面介绍这个控件的外观属性&#xff1a;Cursor属性&#xff1a;表示鼠标移动过该控件的时候&#xff0c;鼠标显示的形状。属性值如下图所示&#xff1a;EmptyBorderColor属性&#xff1a;表示当所选颜色为空时&#x…

七、JUC并发工具

文章目录JUC并发工具CountDownLatch应用&源码分析CountDownLatch介绍CountDownLatch应用CountDownLatch源码分析有参构造await方法countDown方法CyclicBarrier应用&源码分析CyclicBarrier介绍CyclicBarrier应用CyclicBarrier源码分析CyclicBarrier的核心属性CyclicBarr…

echarts实现知识图谱,生产项目

echarts实现知识图谱&#xff0c;生产项目内容简介效果演示代码逻辑结束语内容简介 在实际生产项目中&#xff0c;需要对后端知识数据进行展示。需求如下&#xff1a; 点击节点可以展示与此节点相关的节点信息右键点击节点可以对节点的信息进行修改悬浮在节点上可以查看节点的…

通用人工智能(AGI):人工智能的下一个阶段

除了人工智能(AI)的改进和新应用之外&#xff0c;大多数人都认为&#xff0c;当通用人工智能(AGI)出现时&#xff0c;人工智能的下一次飞跃将发生。我们将AGI宽泛地定义为机器或计算机程序理解或学习人类可以完成的任何智力任务的假设能力。然而&#xff0c;对于何时以及如何实…

系统性能测试指标

性能测试的目的 1.评估系统的能力&#xff0c;测试中得到的负荷和响应时间数据可以被用于验证所计划的模型的能力&#xff0c;并帮助作出决策。 2.识别体系中的弱点&#xff1a;受控的负荷可以被增加到一个极端的水平&#xff0c;并突破它&#xff0c;从而修复体系的瓶颈或薄…

leetcode: Swapping Nodes in a Linked List

leetcode: Swapping Nodes in a Linked List1. 题目描述2. 题目解答3. 总结1. 题目描述 You are given the head of a linked list, and an integer k.Return the head of the linked list after swapping the values of the kth node from the beginning and the kth node f…

ECMAScript 详解

ECMAScript 历史我们首先来看 ECMA 是什么。ECMA&#xff0c;是欧洲计算机制造商协会&#xff08;European Computer Manufacturers Association&#xff09;的简称&#xff0c;是一家国际性会员制度的信息和电信标准组织。1994 年之后&#xff0c;由于组织的标准牵涉到很多其他…

【Leetcode 剑指Offer】第 6 天 搜索与回溯算法(简单)

搜索与回溯剑指 Offer 32 - I. 从上到下打印二叉树层序遍历 广搜 BFScollections双端队列 deque剑指 Offer 32 - II. 从上到下打印二叉树 II剑指 Offer 32 - III. 从上到下打印二叉树 III剑指 Offer 32 - I. 从上到下打印二叉树 题;从上到下打印出二叉树的每个节点&#xff0c…