算法23,寻找峰值
这是一道经典的二分查找应用题寻找峰值Find Peak Element。笔记中已经总结了核心逻辑我将为你梳理其背后的数学原理二段性并提供标准的代码实现。1. 核心原理什么是“二段性”这道题之所以能用二分查找是因为它满足一种特殊的二段性笔记右侧画了很棒的示意图定义对于数组中的任意位置mid我们比较它和它右侧邻居的大小 (nums[mid]vsnums[mid1])。情况 ①nums[mid] nums[mid1]结论左边一定存在峰值。理由因为mid到mid1处于上升趋势。即使后面一直下降那个最高点mid1及其附近也会成为峰值如果后面还有更高的那更高的地方也可能是峰值。但只要在上升峰值就肯定在mid的右边包含mid1。情况 ②nums[mid] nums[mid1]结论右边一定存在峰值。理由因为mid到mid1处于下降趋势。即使前面一直上升那个最高点mid及其附近也会成为峰值如果前面还有更低的那更低的地方不可能是峰值因为会被mid挡住。只要在下降峰值就肯定在mid的左边包含mid。简单来说不看绝对值只看“趋势”。只要不是平的顺着趋势走一定能撞上或者路过峰值。2. 代码实现Java根据笔记中的逻辑这是标准的二分查找模板。需要注意的是为了防止数组越界我们通常搜索区间是[0, len-2]因为我们要时刻访问mid1。public class FindPeakElement { public int findPeakElement(int[] nums) { int left 0; // 注意right 设为 length - 2 // 因为我们在循环里要判断 nums[mid1]如果 right 是最后一个元素mid1 就越界了 int right nums.length - 2; while (left right) { int mid left (right - left) / 2; // 防止溢出 // 核心判断比较 mid 和它的右邻居 if (nums[mid] nums[mid 1]) { // 处于上升趋势峰值在右边left 跨越到 mid 的右侧 left mid 1; } else { // 处于下降趋势或相等峰值在左边包含 mid 自己right 收缩到 mid right mid - 1; } } // 循环结束时left 指向的就是一个峰值的下标 // 证明如果最后 left right说明最后一次移动是 left mid 1 // 此时 nums[left] nums[left-1] (因为是从小于变成大于走出来的) // 且 nums[left] nums[left1] (或者是边界) return left; } }3. 细节解析针对笔记中的疑问笔记中提到“arr[mid] arr[mid1]右边存在峰值左边不一定有”。这句话非常精准。为什么左边不一定有假设数组是[1, 5, 4, 3]。如果mid指向1(值为5)它大于右边的4。此时左边确实没有峰值1 5。峰值在右边5 本身就是。如果数组是[1, 2, 5, 4]。如果mid指向2(值为2)它小于右边的5。我们会去右边找。最终找到5。此时左边2也不是峰值。为什么循环条件可以是left right因为我们每次收缩边界时如果是向左移动right mid - 1我们抛弃的是mid及其左侧的一部分。由于我们已经判断过nums[mid] nums[mid1]所以mid位置有可能是峰值如果它是边界或者左边更小所以我们用可以保证mid被检查到。4. 复杂度分析时间复杂度O(logN)。每次将搜索范围减半。空间复杂度O(1)。只使用了常数级别的额外空间。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2608899.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!