二分查找

学习目的:通读算法入门书籍《算法图解》,学习二分查找,了解大O表示法,分析算法运行时间。

自己也从过去经历的项目中实践过Java的脚步开发和编程,实际没有用到过算法。还是认为学习算法是提高水平的必经之路,而且面试过程中也会用到。

最近继续刷牛客网的基础语法编程,觉得算法是编程的灵魂,能成功完成一些简单算法的编程题并运行成功,非常有成就感。逐渐对算法有了一些兴趣,希望自己能一直保持下去,学习的更加系统和深入。周末趁着有时间,在网上看别人学习感悟和学习路径。从github上找了一些算法书籍,从入门的《算法图解》、到《数据结构与算法分析:Java 语言描述》,等等还有很多相关书籍资料。贪多嚼不烂,先从这2本书开始,慢慢学习。

二分查找算法思想

最简单的理解,就像《算法图解》作者的举例,就是我们小时候玩的猜数字游戏,从1~100中抽一个数字,让另外的小朋友去猜,看谁最快猜到正确数字。

 

有序的序列,每次都是以序列的中间位置的数来与待查找的关键字进行比较,每次缩小一半的查找范围,直到匹配成功。

一个情景:将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

大 O 表示法

大O表示法是一种特殊的表示法,指出了算法的速度有多快。
算法的运行时间以不同的速度增加
有鉴于此,仅知道算法需要多长时间才能运行完毕还不够,还需知道运行时间如何随列表增长而增加。这正是大O表示法的用武之地。
 
大O表示法指出了算法有多快。例如,假设列表包含n个元素。简
单查找需要检查每个元素,因此需要执行n次操作。使用大O表示法,
这个运行时间为O(n)。单位秒呢?没有——大O表示法指的并非以秒为单位的速度。大O表示法
让你能够比较操作数,它指出了算法运行时间的增速。
 

一些常见的大 O 运行时间

下面按从快到慢的顺序列出了你经常会遇到的5种大O运行时间。
 O(log n),也叫对数时间,这样的算法包括二分查找。
 O(n),也叫线性时间,这样的算法包括简单查找。
 O(n * log n),这样的算法包括第4章将介绍的快速排序——一种速度较快的排序算法。
 O(n2),这样的算法包括第2章将介绍的选择排序——一种速度较慢的排序算法。
 O(n!),这样的算法包括接下来将介绍的旅行商问题的解决方案——一种非常慢的算法。
 
 算法的速度指的并非时间,而是操作数的增速。
 谈论算法的速度时,我们说的是随着输入的增加,其运行时间将以什么样的速度增加。
 算法的运行时间用大O表示法表示。
 O(log n)比O(n)快,当需要搜索的元素越多时,前者比后者快得越多。

二分查找优缺点

优点:

  1. 时间复杂度低: 二分查找的时间复杂度是 O(log n),相比线性搜索 O(n),在大型有序数据集上具有更高的效率。
  2. 简单清晰: 算法逻辑相对简单,易于理解和实现。
  3. 节省空间: 二分查找通常只需要很少的额外空间,主要用于存储左右边界、中间索引等变量。

缺点:

  1. 仅适用于有序数据: 二分查找要求数据集是有序的,如果数据集无序,需要先进行排序,这可能增加额外的时间复杂度。
  2. 不适用于链表: 二分查找需要随机访问,对于链表这样的数据结构,二分查找的效率较低,因为它无法直接跳到中间元素。
  3. 只能查找单一元素: 二分查找适用于查找单一元素的情况,如果需要查找多个匹配的元素,可能需要其他算法。
  4. 不适用于动态数据集: 如果数据集经常发生变化,频繁的插入和删除可能会导致数组重新排序,使得二分查找的优势减弱。

总体来说,二分查找是一种非常有用的算法,尤其适用于有序数据集的查找操作。然而,使用前需要考虑数据集的特点和操作需求,以确保选择最适合的算法。

线性时间:

回到前面的二分查找。使用它可节省多少时间呢?简单查找逐个地检查数
字,如果列表包含100个数字,最多需要猜100次。如果列表包含40亿个数字,最
多需要猜40亿次。换言之,最多需要猜测的次数与列表长度相同,这被称为线性
时间(linear time)。

 

以下是二分查找的详细步骤:

  1. 初始化左右边界:将初始查找范围设置为整个数组,即left = 0right = array.length - 1
  2. 计算中间元素的索引:mid = (left + right) / 2
  3. 检查中间元素是否是目标元素:
    • 如果 array[mid] == target,则找到目标元素,返回 mid
    • 如果 array[mid] < target,说明目标元素在右半部分,更新 left = mid + 1
    • 如果 array[mid] > target,说明目标元素在左半部分,更新 right = mid - 1
  1. 重复步骤2和步骤3,直到找到目标元素或左边界大于右边界

 

实例:

描述

请实现无重复数字的升序数组的二分查找
 
给定一个 元素升序的、无重复数字的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标(下标从 0 开始),否则返回 -1
 
数据范围:0≤𝑙𝑒𝑛(𝑛𝑢𝑚𝑠)≤2×1050len(nums)2×105 , 数组中任意值满足 ∣𝑣𝑎𝑙∣≤109val109
进阶:时间复杂度 𝑂(log⁡𝑛)O(logn) ,空间复杂度 𝑂(1)O(1)
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 left = 0;
        int right = nums.length - 1;
        int mid = 0;
        while(left<=right){
            mid=left+( right-left) / 2;
            if (target==nums[mid]) {
                return mid;
            } else if (target <nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;       
    }
}

 

posted @ 2024-06-22 12:31  迷迷糊糊的礼物  阅读(1)  评论(0编辑  收藏  举报