C++實現LeetCode(162.求數組的局部峰值)

[LeetCode] 162.Find Peak Element 求數組的局部峰值

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] = -∞.

Example 1:

Input: nums = [1,2,3,1]
Output: 2
Explanation: 3 is a peak element and your function should return the index number 2.

Example 2:

Input: nums = [1,2,1,3,5,6,4]
Output: 1 or 5
Explanation: Your function can return either index number 1 where the peak element is 2,
or index number 5 where the peak element is 6.

Note:

Your solution should be in logarithmic complexity.

這道題是求數組的一個峰值,如果這裡用遍歷整個數組找最大值肯定會出現Time Limit Exceeded,但題目中說瞭這個峰值可以是局部的最大值,所以我們隻需要找到第一個局部峰值就可以瞭。所謂峰值就是比周圍兩個數字都大的數字,那麼隻需要跟周圍兩個數字比較就可以瞭。既然要跟左右的數字比較,就得考慮越界的問題,題目中給瞭nums[-1] = nums[n] = -∞,那麼我們其實可以把這兩個整型最小值直接加入到數組中,然後從第二個數字遍歷到倒數第二個數字,這樣就不會存在越界的可能瞭。由於題目中說瞭峰值一定存在,那麼有一個很重要的corner case我們要註意,就是當原數組中隻有一個數字,且是整型最小值的時候,我們如果還要首尾墊數字,就會形成一條水平線,從而沒有峰值瞭,所以我們對於數組中隻有一個數字的情況在開頭直接判斷一下即可,參見代碼如下:

C++ 解法一:

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        if (nums.size() == 1) return 0;
        nums.insert(nums.begin(), INT_MIN);
        nums.push_back(INT_MIN);
        for (int i = 1; i < (int)nums.size() - 1; ++i) {
            if (nums[i] > nums[i - 1] && nums[i] > nums[i + 1]) return i - 1;
        }
        return -1;
    }
};

Java 解法一:

class Solution {
    public int findPeakElement(int[] nums) {
        if (nums.length == 1) return 0;
        int[] newNums = new int[nums.length + 2];
        System.arraycopy(nums, 0, newNums, 1, nums.length);
        newNums[0] = Integer.MIN_VALUE;
        newNums[newNums.length - 1] = Integer.MIN_VALUE;
        for (int i = 1; i < newNums.length - 1; ++i) {
            if (newNums[i] > newNums[i - 1] && newNums[i] > newNums[i + 1]) return i - 1;
        }
        return -1;
    }
}

我們可以對上面的線性掃描的方法進行一些優化,可以省去首尾墊值的步驟。由於題目中說明瞭局部峰值一定存在,那麼實際上可以從第二個數字開始往後遍歷,如果第二個數字比第一個數字小,說明此時第一個數字就是一個局部峰值;否則就往後繼續遍歷,現在是個遞增趨勢,如果此時某個數字小於前面那個數字,說明前面數字就是一個局部峰值,返回位置即可。如果循環結束瞭,說明原數組是個遞增數組,返回最後一個位置即可,參見代碼如下:

C++ 解法二:

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        for (int i = 1; i < nums.size(); ++i) {
            if (nums[i] < nums[i - 1]) return i - 1;
        }
        return nums.size() - 1;
    }
};

Java 解法二:

public class Solution {
    public int findPeakElement(int[] nums) {
        for (int i = 1; i < nums.length; ++i) {
            if (nums[i] < nums[i - 1]) return i - 1;
        }
        return nums.length - 1;
    }
}

由於題目中提示瞭要用對數級的時間復雜度,那麼我們就要考慮使用類似於二分查找法來縮短時間,由於隻是需要找到任意一個峰值,那麼我們在確定二分查找折半後中間那個元素後,和緊跟的那個元素比較下大小,如果大於,則說明峰值在前面,如果小於則在後面。這樣就可以找到一個峰值瞭,代碼如下:

C++ 解法三:

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < nums[mid + 1]) left = mid + 1;
            else right = mid;
        }
        return right;
    }
};

Java 解法三:

public class Solution {
    public int findPeakElement(int[] nums) {
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < nums[mid + 1]) left = mid + 1;
            else right = mid;
        }
        return right;
    }
}

類似題目:

Peak Index in a Mountain Array

參考資料:

https://leetcode.com/problems/find-peak-element

https://leetcode.com/problems/find-peak-element/discuss/50232/find-the-maximum-by-binary-search-recursion-and-iteration

到此這篇關於C++實現LeetCode(162.求數組的局部峰值)的文章就介紹到這瞭,更多相關C++實現求數組的局部峰值內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: