前言
本文承继acwing算法基础课笔记2-二分法
全部为左闭右闭写法
题目
思路
初始想法
同旋转数组的思路一样,每次搜索将区间分成一个有序的部分和一个无序的部分。
本题设定存在“山峰值”,那么每次划分一定是严格的 一部分有序,另一部分无序。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public int peakIndexInMountainArray(int[] arr) { int n = arr.length; int l = 1, r = n - 2; while (l <= r) { int mid = l + r >> 1; if (arr[mid] >= arr[l]) { if (arr[mid - 1] > arr[mid]) r = mid - 1; else l = mid + 1; } else { r = mid - 1; } } return r; } }
|
细节:
根据题目定义,山峰不可能出现在数组头或者尾部,因此初值 l=1,r=n-2
当arr[mid]值小于arr[l]时,此时arr[mid]一定处于下降侧/右侧
,
当arr[mid]值大于arr[l]时,分情况讨论:
- 若
arr[mid-1] > arr[mid]
,一定处于下降侧,则区间向左侧收紧,且mid一定不是山峰,r = mid - 1
- 反之
l = mid + 1
但是,这样判断很不优雅,而且我在写代码的过程中也发现了更优的判断方法:
- arr[mid - 1] < arr[mid] 一定处于上升侧
- arr[mid - 1] > arr[mid] 一定处于下降侧
基于此,更新后的代码
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public int peakIndexInMountainArray(int[] arr) { int n = arr.length; int l = 1, r = n - 2; while (l < r) { int mid = l + r + 1>> 1; if (arr[mid - 1] <= arr[mid]) l = mid; else r = mid - 1; } return l; } }
|
细节:
这里的arr[mid - 1] <= arr[mid]
,意义
- arr[mid]在峰值的左侧,等价于 check(mid) <= target,向右收紧搜索区间
- 此时的arr[mid]是可能为峰值的,搜索区间的端点要取到mid
综上两点,搜索区间更新方式为 l = mid;
。别忘了 mid=2l+r+1
关于更新方式,请记住(仅限本人常用的模板一二)
R+1=L
l=mid -->r就更新为r=mid-1
l=mid+1 -->r就更新为r=mid
左端点更新为r = mid - 1
亿点点拓展-找不同
下面给出几组正确代码
逻辑是一致的,重点在于不同的写法
退出循环条件为 l < r
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public int peakIndexInMountainArray(int[] arr) { int n = arr.length; int l = 1, r = n - 2; while (l < r) { int mid = l + r >> 1; if (arr[mid] <= arr[mid + 1]) l = mid + 1; else r = mid; } return l; } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public int peakIndexInMountainArray(int[] arr) { int n = arr.length; int l = 1, r = n - 2; while (l < r) { int mid = l + r + 1>> 1; if (arr[mid - 1] >= arr[mid]) r = mid - 1; else l = mid; } return l; } }
|
退出循环条件为 l <= r
2.1
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public int peakIndexInMountainArray(int[] arr) { int n = arr.length; int l = 1, r = n - 2; while (l <= r) { int mid = l + r >> 1; if (arr[mid - 1] >= arr[mid]) r = mid - 1; else l = mid + 1; } return l - 1; } }
|
2.2
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public int peakIndexInMountainArray(int[] arr) { int n = arr.length; int l = 1, r = n - 2; while (l <= r) { int mid = l + r >> 1; if (arr[mid + 1] >= arr[mid]) l = mid + 1; else r = mid - 1; } return l; } }
|
2.1最后返回值是l-1
也就是r
,2.2是l
思考一下为什么?(不思考也没事,这种麻烦也是不选用l<=r写法的原因)
个人理解
bullshit, 就别用这种退出循环的方式!!!