数据结构与算法之二分查找算法----------超级详细!全网不同!拓展!详细介绍了根据二分查找算法的变种常见算法题!

1.二分查找算法概述

二分查找算法,也称为折半查找,是一种高效的查找算法。它在一个已经排序的数组中查找目标值,时间复杂度为 O(logn)。

2.二分查找法思维步骤

  • 初始边界:定义左边界 (left) 和右边界 (right)。
  • 取中间值:计算中间值索引 mid = (left + right) / 2
  • 比较目标值与中间值
    • 如果目标值等于中间值,查找成功,返回索引。
    • 如果目标值小于中间值,继续在左半部分搜索,调整右边界为 mid - 1
    • 如果目标值大于中间值,继续在右半部分搜索,调整左边界为 mid + 1
  • 查找结束:当左边界超过右边界时,说明目标值不在数组中,查找失败,返回 -1

3. JAVA代码实现

public class BinarySearch {
    // 二分查找算法实现
    public static int binarySearch(int[] array, int target) {
        int left = 0;                // 左边界
        int right = array.length - 1; // 右边界

        // 当左边界小于等于右边界时继续查找
        while (left <= right) {
            // 取中间索引,防止 (left + right) 可能溢出
            int mid = left + (right - left) / 2;

            // 如果中间值等于目标值,返回索引
            if (array[mid] == target) {
                return mid;
            }

            // 如果目标值小于中间值,则在左半部分继续查找
            if (array[mid] > target) {
                right = mid - 1;
            } else { // 如果目标值大于中间值,则在右半部分继续查找
                left = mid + 1;
            }
        }

        // 如果找不到目标值,返回 -1
        return -1;
    }

    public static void main(String[] args) {
        // 测试数据
        int[] array = {1, 3, 5, 7, 9, 11, 13, 15};
        int target = 7;

        // 调用二分查找
        int result = binarySearch(array, target);

        // 输出结果
        if (result != -1) {
            System.out.println("目标值 " + target + " 在数组中的索引为: " + result);
        } else {
            System.out.println("目标值 " + target + " 未在数组中找到。");
        }
    }
}

这里面不知道有没有人注意到,我定义中间索引的时候使用了mid = left + (right - left) / 2

其实,这样做的目的是为了计算出来的mid防止溢出,因为mid定义的数据类型是int他的最大值也就只有2的32次方-1,所以这里要注意下。

4.复杂度

  • 时间复杂度:二分查找的时间复杂度为 O(log⁡n)O(\log n)O(logn),因为每次查找都将搜索范围缩小一半。
  • 空间复杂度:二分查找的空间复杂度为 O(1)O(1)O(1),因为只需使用常量空间存储边界和中间值。

5.二分查找的优缺点

  • 优点
    • 查找速度快,适合大规模数据的查找。
    • 实现简单,代码易于理解和维护。
    • 只需要常数空间,节省内存。
  • 缺点
    • 要求数据有序:如果数据没有排序,需要先进行排序,排序的复杂度为 O(nlog⁡n),这会增加开销。
    • 不适用于链表等不支持随机访问的结构,适合数组或随机访问时间复杂度为 O(1) 的数据结构。

6.变种算法

  • 查找第一个等于给定值的元素:当数组中有多个目标值时,二分查找可以调整为找到第一个目标值。
  • 查找最后一个等于给定值的元素:与查找第一个类似,只需在更新边界时做相应调整。
  • 查找第一个大于等于目标值的元素:用于查找不小于目标值的最小元素。
  • 查找最后一个小于等于目标值的元素:查找不大于目标值的最大元素。

6.1 查找第一个等于给定值的元素

public static int findFirstEqual(int[] array, int target) {
    int left = 0, right = array.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (array[mid] == target) {
            if (mid == 0 || array[mid - 1] != target) {
                return mid; // 第一个等于目标值
            } else {
                right = mid - 1; // 继续在左边查找
            }
        } else if (array[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1; // 没有找到
}

这里改动很简单,因为数组是有序的,就是当我们第一次找出目标值的时候,然后让right-1,紧接着接着在左侧查找,然后要判断一种直到mid到最左侧边界还是等于目标值的特殊情况,二是往第一次查找到的目标值的左侧去挨个判断,为什么是这样判断,还是因为它是有序数组,和由多个目标值的时候,一定是连续的,像这样1,2,3,4,4,4,4,5,6,7,8,9,9,10不能是1,2,3,4,5,4,5,4,6,7这样就是无序的了。

6.2 查找最后一个等于给定值的元素

public static int findLastEqual(int[] array, int target) {
    int left = 0, right = array.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (array[mid] == target) {
            if (mid == array.length - 1 || array[mid + 1] != target) {
                return mid; // 最后一个等于目标值
            } else {
                left = mid + 1; // 继续在右边查找
            }
        } else if (array[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1; // 没有找到
}

这和第一种情况一样,只需要改动边界值就好

6.3 查找第一个大于等于目标值的元素

public static int findFirstGreaterEqual(int[] array, int target) {
    int left = 0, right = array.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (array[mid] >= target) {
            if (mid == 0 || array[mid - 1] < target) {
                return mid; // 第一个大于等于目标值的元素
            } else {
                right = mid - 1; // 继续在左边查找
            }
        } else {
            left = mid + 1;
        }
    }
    return -1; // 没有找到
}

这里的思想就是在一个有序的数组中,先通过mid折中随便找到一个只要大于等于目标值的值,然后在向这个值的左侧去挨个比较就好,直到小于目标值,其实这里最坏情况就很坏了,时间复杂度会变成O(n/2),例如,我的目标恰巧是数组第一个值,那第一次找到的中间值,是(n-1)/2,那样就会从中间值的位置挨个向左比较。

6.4 查找最后一个小于等于目标值的元素

public static int findLastLessEqual(int[] array, int target) {
    int left = 0, right = array.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (array[mid] <= target) {
            if (mid == array.length - 1 || array[mid + 1] > target) {
                return mid; // 最后一个小于等于目标值的元素
            } else {
                left = mid + 1; // 继续在右边查找
            }
        } else {
            right = mid - 1;
        }
    }
    return -1; // 没有找到
}

这里和6.3的实例一样的思维,只要改变边界值就好。

6.5 未查找到,就按顺序插入

public class BinarySearch {
    // 二分查找算法实现
    public static int binarySearch(int[] array, int target) {
        int left = 0;                // 左边界
        int right = array.length - 1; // 右边界

        // 当左边界小于等于右边界时继续查找
        while (left <= right) {
            // 取中间索引,防止 (left + right) 可能溢出
            int mid = left + (right - left) / 2;

            // 如果中间值等于目标值,返回索引
            if (array[mid] == target) {
                return mid;
            }

            // 如果目标值小于中间值,则在左半部分继续查找
            if (array[mid] > target) {
                right = mid - 1;
            } else { // 如果目标值大于中间值,则在右半部分继续查找
                left = mid + 1;
            }
        }

        // 如果找不到目标值,返回插入点
        return right + 1;
    }

//插入函数

这里其实就是利用了,right和left的含义,如果没有找到目标值,left就是返回的插入点,而right需要+1才能使插入点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值