本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
若旋转 7 次,则可以得到 [0,1,4,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
示例 1:
输入:nums = [1,3,5]
输出:1
示例 2:
输入:nums = [2,2,2,0,1]
输出:0
提示:
n == nums.length1 <= n <= 5000-5000 <= nums[i] <= 5000nums原来是一个升序排序的数组,并进行了1至n次旋转
进阶:
- 这道题是 寻找旋转排序数组中的最小值 的延伸题目。
- 允许重复会影响算法的时间复杂度吗?会如何影响,为什么?
二、思路分析:
- 遍历后查找最小值 AC代码
/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
let min = nums[0];
for(let i = 1; i < nums.length; i ++) {
if(nums[i] < min) min = nums[i];
}
return min;
};
执行结果
执行结果:通过
执行用时:96 ms, 在所有 JavaScript 提交中击败了21.48%的用户
内存消耗:39.1 MB, 在所有 JavaScript 提交中击败了22.46%的用户
缺点:时间复杂度最大
- 数组重新升序排序后取第一个 AC代码
/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
nums.sort((a, b) => {
return a - b;
});
return nums[0];
};
执行结果
执行结果:通过
执行用时:88 ms, 在所有 JavaScript 提交中击败了55.08%的用户
内存消耗:38.9 MB, 在所有 JavaScript 提交中击败了66.60%的用户
缺点:时间复杂度也最长
- 二分法
- 查找有序数组,尝试使用
二分法 - 使用
(end - start) / 2获得中间位置mid,使用Math.floor向下取整 - 判断中间位置值
nums[mid]和末尾位置值nums[end]是否是升序,是升序则说明最小值在中间位置的左边部分,把结束位置置为原中间位置end = mid - 若是不是升序,因为存在重复元素,所以再判断中间位置值是否大于末尾位置值,若大于则最小值在右边部分,把开始位置置为原中间位置加一
start = mid + 1 - 否则末尾位置值向左移一位
- 继续二分,直到起始位置不小于结束位置,返回起始位置的值
nums[start]AC代码
/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
let start = 0;
let end = nums.length - 1;
let min = nums[0];
while(start < end) {
let mid = start + Math.floor((end - start) / 2);
if(nums[mid] < nums[end]) {
end = mid;
} else if(nums[mid] > nums[end]) {
start = mid + 1;
} else {
end --;
}
}
return nums[start];
};
执行结果
执行结果:通过 显示详情
执行用时:84 ms, 在所有 JavaScript 提交中击败了73.83%的用户
内存消耗:39 MB, 在所有 JavaScript 提交中击败了57.42%的用户
三、总结:
- 数组遍历查找效率最低
- 利用数组自带排序sort可利用JavaScript内部优化,提升部分效率
- 二分查找提高查找效率