高效查找 Bitonic 数组中的目标值:二分查找的应用

在算法面试中,经常会遇到 二分查找(Binary Search)相关的变形题。而今天要讨论的 Bitonic Array Search,正是其中一个 巧妙运用二分查找的经典问题。

这篇文章将带你深入理解 Bitonic 数组的结构,并探讨如何 优化搜索过程,让二分查找真正发挥 O(logN) 的高效特性。

📌 题目背景:什么是 Bitonic 数组?

Bitonic 数组是一种特殊的数组,它先递增后递减,并且在某个位置达到峰值。例如:

ini 复制代码
array = [1, 3, 8, 12, 4, 2]

在该数组中:

  • 左侧 [1, 3, 8, 12] 递增
  • 右侧 [12, 4, 2] 递减
  • 峰值 在索引 3 处,即 12

📝 目标:在这个数组中 高效查找 目标值 target 的索引,如果不存在返回 -1


💡 解题思路

🔹 Step 1: 先找峰值

由于 Bitonic 数组有 单调性变化(先增后减),我们可以用 二分查找 找到峰值索引 peakIndex

  • array[mid] < array[mid+1],说明峰值在 右侧,调整 left = mid + 1
  • 否则,峰值在 左侧或 mid 位置,调整 right = mid

🔹 Step 2: 在两侧进行二分查找

  1. 先查左侧递增区间:

    • 如果 target[0, peakIndex] 这个递增区间,使用普通 二分查找。
  2. 再查右侧递减区间:

    • 如果 target[peakIndex + 1, N-1] 这个递减区间,使用 逆序二分查找(因为该区间是递减的)。

这样,我们仅需 O(logN) + O(logN) = O(logN) 的时间复杂度完成搜索。

🔎 代码实现

ini 复制代码
public class Solution {
    public int search(int[] array, int target) {
        if (array == null || array.length == 0) return -1;

        // 1️⃣ 找到峰值索引
        int peakIndex = findPeak(array);

        // 2️⃣ 在左侧递增部分查找
        int leftResult = binarySearch(array, target, 0, peakIndex, true);
        if (leftResult != -1) return leftResult;

        // 3️⃣ 在右侧递减部分查找
        return binarySearch(array, target, peakIndex + 1, array.length - 1, false);
    }

    /** 找到峰值索引 */
    private int findPeak(int[] array) {
        int left = 0, right = array.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (array[mid] < array[mid + 1]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left; // 返回峰值索引
    }

    /** 在指定区间执行二分查找 */
    private int binarySearch(int[] array, int target, int left, int right, boolean ascending) {
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (array[mid] == target) return mid;
            if (ascending) {
                if (array[mid] < target) left = mid + 1;
                else right = mid - 1;
            } else {
                if (array[mid] < target) right = mid - 1;
                else left = mid + 1;
            }
        }
        return -1;
    }
}

🚀卡点 & 解决方案

在刷这道题的过程中,可能会遇到一些理解障碍,这里针对几个常见问题做拆解分析:

❌ 卡点 1: 为什么要找峰值?

误区:能不能直接进行二分查找?

✅ 解法:

  • Bitonic 数组的单调性变化导致无法直接用标准二分查找。
  • 先找峰值,再在两侧二分查找,保证 O(logN) 复杂度。

❌ 卡点 2: 峰值查找的逻辑不清楚

误区:left = mid + 1 vs. right = mid 不知道如何选择?

✅ 解法:

  • 如果 array[mid] < array[mid+1],说明峰值在 右侧,所以 left = mid + 1
  • 否则,峰值在 左侧 或 mid 本身,调整 right = mid
  • 直到 left == right,即找到峰值索引。

❌ 卡点 3: 二分查找的方向如何调整?

误区:在递增和递减部分都用同一个 binarySearch 逻辑?

✅ 解法:

  • 递增部分:标准二分查找(左小右大)。
  • 递减部分:反向二分查找(左大右小)。

🎯 Follow-up

💡 变形题

  1. 如果数组中有重复元素怎么办?

    • 可能会导致多个局部峰值,需要额外处理。
  2. 如果要求返回插入位置,而不是索引?

    • 需要修改二分查找逻辑,返回 left 指针的位置。
  3. 如果数组可能没有 Bitonic 结构,如何处理?

    • 需要先验证是否符合 Bitonic 条件。
  4. 如果数组是 Bitonic Linked List,而不是数组呢?

    • 不能直接使用索引,需要通过 快慢指针三段二分查找

📝 高级优化

  • 能用一次二分查找完成搜索吗?

    • 直接在二分查找过程中动态判断方向,而不需要先找峰值。
  • 这个解法能否在 O(1) 额外空间下完成?

    • 可以,只需在原数组上操作,无需额外存储。

🔚 总结

关键点 思路
Bitonic 结构 先递增后递减,峰值最大
二分查找峰值 O(logN) 时间找到 peakIndex
两次二分查找 先查左侧(递增),再查右侧(递减)
变形题思考 重复元素、多峰值、插入位置、链表等

✅ 这道题考察的是:

  • 二分查找的变形与灵活运用
  • 如何利用 Bitonic 结构优化搜索
  • 如何处理面试中的 Follow-up 问题
相关推荐
共享家952731 分钟前
顺序表的C语言实现与解析
数据结构·算法
uhakadotcom1 小时前
英伟达最新的GTC 2025提供了哪些新技术新能力?
算法·面试·github
ChoSeitaku2 小时前
NO.42十六届蓝桥杯备战|数据结构|算法|时间复杂度|空间复杂度|STL(C++)
数据结构·算法·蓝桥杯
努力学习的小廉3 小时前
我爱学算法之——滑动窗口攻克子数组和子串难题(上)
开发语言·c++·算法
梦想攻城狮3 小时前
深度学习之神经网络
人工智能·算法·机器学习
阿巴~阿巴~4 小时前
素数判定方法详解:从基础试除法到优化策略
c++·算法
Vitalia4 小时前
图论入门【数据结构基础】:什么是树?如何表示树?
数据结构·算法·图论·
埃菲尔铁塔_CV算法4 小时前
WPF 开发从入门到进阶(五)
深度学习·算法·机器学习·计算机视觉·wpf
Cindy辛蒂4 小时前
C语言:能够规定次数,处理非法字符的猜数游戏(三重循环)
c语言·算法·游戏
小卡皮巴拉5 小时前
【力扣刷题实战】无重复的最长字串
开发语言·c++·算法·leetcode·滑动窗口