Leetcode : 162 /852 /1095 寻找局部最大/最小值

题目:
A peak element is an element that is greater than its neighbors.

Given an input array nums, where nums[i] ≠ nums[i+1], find a peak element and return its index.

The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.

You may imagine that nums[-1] = nums[n] = -∞.
Your solution should be in logarithmic complexity.

题意:
从一个不含重复元素的int 数组中,在 O ( l o g ) O(log) 时间里寻找其中的局部最大值。

例如:nums=[2,3,4,1],那么nums[2]==4就是局部最大值。
nums=[1,2,1,3,4,5,2],那么nums[1]=2,nums[5]=5 都是局部最大值。

解法:
题目不难,关键是如何在 O ( l o g n ) O(logn) 时间内找出一个局部最大值出来。

一个 O ( n ) O(n) 的思路是遍历数组,找到第一个局部最大值输出即可,但是这种局部最大附带了额外的约束:从左往右首次出现的局部最大值。而题目并没有这个约束,输出结果只要是个局部就行,于是也就没有必要遍历了。
于是可以使用分治法来解。

我们要求 nums[0… n-1]的局部最大值,于是我们看能不能把这个问题分成两个子问题:求nums[0…mid]的局部最大值和nums[mid+1…n-1]的局部最大值。又因为这个局部最大值 左边右边都可以,于是实际上只需要求nums[0…mid]的局部最大值 nums[mid+1…n-1]的局部最大值。

接下来就是定,到底求那一边的局部最大值。一个有效的策略是那边大求那边。

于是时间复杂度为: T ( n ) = T ( n / 2 ) + c T(n)=T(n/2)+c ,可以解的: T ( n ) = c l o g ( n ) = O ( l o g n ) T(n)=clog(n)=O(logn)

代码:

具体实现的时候,需要考虑边界条件。

这个套路,可以直接用于解 https://leetcode.com/problems/peak-index-in-a-mountain-array/ ,只存在唯一一个局部最大值的情况。

这个套路,一样可以用于解 https://leetcode.com/problems/find-in-mountain-array/ ,这个题只允许对只存在一个peak的数组的元素进行有限次query,而不能只能获取数组元素,显然需要考虑 O ( l o g n ) O(logn) 的解法。方法是先通过这里的方法找到最大值的下标。比如最大值的下标为 m i m_i ,那么再分别nums [ 0 , m i ] [0,m_i] 单调递增二分target,以及nums [ m i , n 1 ] [m_i,n-1] 单调递减二分target。复杂度为 3 O ( l o g n ) 3 O(logn) 。要求query最大为100,用这个方法是绰绰有余的。

方法一:

为了避免对边界的讨论,直接在一头一尾插入INT_MIN最小值。

class Solution {
public:
	int findPeakElement(vector<int>& nums) {
		int index = 0;
		int l = 1, r = nums.size();
		int n = r;
		if (n == 1) return 0;
		nums.insert(nums.begin(), INT_MIN);
		nums.insert(nums.end(), INT_MIN);
		while (l<=r)
		{
			int mid = (l + r+1) / 2;
			if (nums[mid] > nums[mid + 1] && nums[mid] > nums[mid - 1])
			{
				index = mid;
				break;
			}
			else if (nums[mid - 1] > nums[mid + 1])
			{
				r = mid - 1;
			}
			else
			{
				l = mid + 1;
			}

		}
		return index-1;
	}
};

方法二

分情况考虑边界条件

class Solution {
public:
	int findPeakElement(vector<int>& nums) {
		int index = 0;
		int l = 1, r = nums.size();
		int n = r;
		if (n == 1) return 0;
		while (l<r)
		{
			int mid = (l + r) / 2;
			if (((mid + 1) < n) && (mid >= 1))
			{
				if (nums[mid] > nums[mid + 1] && nums[mid] > nums[mid - 1])
				{
					index = mid;
					break;
				}
				else if (nums[mid - 1] > nums[mid + 1])
				{
					r = mid;
				}
				else
				{
					l = mid + 1;
				}
			}
			else if ((mid+1)<n)
				//mid+1<n 且 mid<1,说明mid不要和左边的比
			{
				if (nums[mid + 1] < nums[mid])
					//nums[mid]>nums[-1]且 nums[mid]>nums[mid+1]
				{
					index = mid;
					break;
				}
				else
					//nums[mid]<nums[mid+1]
				{
					l = mid + 1;
				}
			}
			else if (mid>=1)
				//mid+1>=n 且mid>=1,说明mid到了右端点
			{
				if (nums[mid] > nums[mid - 1])
				{
					index = mid;
					break;
				}
				else
				{
					r = mid;
				}
			}
		}
		return index;
	}
};
发布了307 篇原创文章 · 获赞 268 · 访问量 56万+

猜你喜欢

转载自blog.csdn.net/jmh1996/article/details/102674464