我同事当了别人小三,全公司都知道了 就她觉得自己藏得挺好。
这姐们儿是真把全公司当瞎子啊。
同事最近突然生活升级,之前中午还跟大家一起热饭盒,现在天天外卖起步,偶尔还去商场楼上吃。包换得勤,鞋也像刚从专柜拎出来的。问题是大家工资都差不多,五千多一个月,她这消费水平看着像项目奖金按天发。
更离谱的是,楼下经常有个四十来岁的男人开车来接她,车挺扎眼。她一口咬定那是亲戚。可那人下车又是搭肩又是贴腰的,怎么看都不像普通亲戚。有人开玩笑问了一句,她还急了,说别人管太宽。
关键全公司其实早就看明白了,就她还觉得自己演得挺自然。茶水间都快传成连续剧了,她还在那儿装没事人。打工人最烦这种,工资没涨,瓜倒是越来越刺激。
数组像这样:
[1, 3, 7, 12, 9, 4, 2]
峰顶下标是 3,因为 12 最大。
这题第一眼很容易写成这样:
defpeak_index(arr):
return arr.index(max(arr))
能过吗?大概率能过一部分。
但这写法我一般不太愿意交,原因很简单:它把“山脉数组”这个条件浪费掉了。题目已经告诉你,数组先严格递增,再严格递减。你还从头扫一遍找最大值,那就有点像数据库明明有索引,结果你非要全表扫。
山脉数组大概长这样:
下标:0 1 2 3 4 5
值: 2 5 9 7 3 1
^
峰顶
站在任意一个位置 mid,只看它和右边那个数:
arr[mid] < arr[mid + 1]
说明还在往上爬,峰顶肯定在右边。
比如:
2, 5, 9, 7, 3
^
mid = 1
5 < 9
这时候左边不用看了,峰顶不会在 mid 以及 mid 左边。
反过来:
arr[mid] > arr[mid + 1]
说明已经开始下坡了,峰顶要么就是 mid,要么在 mid 左边。
这里别急着把 mid 扔掉,这个地方我见过不少人写错。
正确代码我会这么写:
defmountain_top_index(nums):
left = 0
right = len(nums) - 1
while left < right:
mid = (left + right) // 2
if nums[mid] < nums[mid + 1]:
left = mid + 1
else:
right = mid
return left
这段代码短,但里面有个细节要盯一下:
right = mid
不是:
right = mid - 1
因为当 nums[mid] > nums[mid + 1] 时,mid 自己是有可能成为峰顶的。你把它减掉,可能直接把答案删了。
拿一组数据跑一下:
nums = [0, 2, 6, 10, 8, 5, 1]
print(mountain_top_index(nums))
print(nums[mountain_top_index(nums)])
输出:
3
10
再看边界一点的:
cases = [
[0, 1, 0],
[1, 4, 7, 3],
[2, 6, 9, 11, 5],
]
for arr in cases:
idx = mountain_top_index(arr)
print(arr, "峰顶下标:", idx, "峰顶值:", arr[idx])
这种题不难,难的是别把它写成“找最大值”。
普通扫描是这样:
defscan_top_index(nums):
best = 0
for i in range(1, len(nums)):
if nums[i] > nums[best]:
best = i
return best
时间复杂度是 O(n)。
二分写法是 O(log n)。
数组长度小的时候,两者没什么差别。但算法题考的不是你能不能跑出来,而是你有没有吃到题目给的条件。
山脉数组这四个字,真正有用的地方就在这里: 一边上坡,一边下坡。 只要能判断当前位置处在上坡还是下坡,就能把一半数据直接扔掉。
最后再放一个完整版本:
classSolution:
defpeakIndexInMountainArray(self, arr):
left = 0
right = len(arr) - 1
while left < right:
mid = (left + right) // 2
if arr[mid] < arr[mid + 1]:
left = mid + 1
else:
right = mid
return left
这题交这个就够了。
别加太多花活,越花越容易把 mid 那个位置搞丢。