[21]数组中的第K个最大元素和寻找峰值

*内容来自leetcode

1.数组中的第K个最大元素

题目要求

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:

1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104

思路

最为简单的应该就是找到数组中最大元素,然后删除该元素,重复k-1次,再获取得到的最大数就是第k个最大元素。

这种方法的问题在于时间复杂度较高,使用内置的max()和remove()函数需要多次遍历列表,无法满足本题要求。

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        for i in range(1,k):
            nums.remove(max(nums))
        return max(nums)

。。。让newbing对这段代码进行优化:这段代码可以通过使用heapq.nlargest()函数来优化。heapq.nlargest()函数可以返回列表中的前k个最大元素,而不会改变原始列表。

就是将列表当成堆来看待,能够顺利通过,时间复杂度为O(nlogk)。

#import heapq

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return heapq.nlargest(k, nums)[-1]

可以考虑用上一篇中提到的堆来解决,遍历数组建立大根堆,然后再在堆中进行查找。相较于上面的直接用nlargest()函数,时间复杂度和空间复杂度表现有所提升。

#python中heapq默认为小根堆,所以需要做出相应变化
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        heap = []
        for i in range(len(nums)):
            heappush(heap,nums[i])
        for i in range(1,len(nums)-k+1):
            heappop(heap)
        return heappop(heap)

考虑到在python 中堆有现成的容器可以直接用,但是堆的实现方式也很重要。在这里用python对官解中自建堆的解法进行复现

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        #自建堆来完成
        def maxHeapify(arr,i,heapSize):
            left, right = i*2 + 1, i*2 + 2
            largest = i
            if left < heapSize and arr[left] > arr[largest]:
                largest = left
            if right < heapSize and arr[right] > arr[largest]:
                largest = right
            if largest != i:
                arr[i], arr[largest] = arr[largest], arr[i]
                maxHeapify(arr, largest,heapSize)

        def buildMaxHeap(arr, heapSize):
            build = heapSize // 2
            while build >= 0 :
                maxHeapify(arr,build,heapSize)
                build -=1
                

        heapSize = len(nums)
        buildMaxHeap(nums, heapSize)
        for i in range(len(nums)-1,len(nums)-k,-1):
            nums[0], nums[i] = nums[i], nums[0]
            heapSize -=1
            maxHeapify(nums,0,heapSize)
        return nums[0]

然后第二种方法是基于快速排序实现,是基于官方给的C++版本修改得到的。

import random
class Solution:
    def quickSelect(self,arr,left,right,index):
        q = self.randomPartition(arr,left,right)
        if q == index:
            return arr[q]
        else:
            if q < index:
                return self.quickSelect(arr,q+1,right,index)
            else:
                return self.quickSelect(arr,left,q-1,index)

    def randomPartition(self,arr,left,right):
        i = random.randint(left,right)
        arr[i], arr[right] = arr[right], arr[i]
        return self.partition(arr, left, right)

    def partition(self,arr,left,right):
        x = arr[right]
        i = left - 1
        for j in range(left,right):
            if arr[j] < x:
                i +=1
                arr[i], arr[j] = arr[j], arr[i]
        i +=1
        arr[i], arr[right] = arr[right], arr[i]
        return i

    def findKthLargest(self, nums: List[int], k: int) -> int:
        return self.quickSelect(nums,0,len(nums)-1,len(nums)-k)
        

2.寻找峰值

题目描述

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞ 。

你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

示例 1:

输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
示例 2:

输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5 
解释:你的函数可以返回索引 1,其峰值元素为 2;
     或者返回索引 5, 其峰值元素为 6。

提示:

1 <= nums.length <= 1000
-231 <= nums[i] <= 231 - 1
对于所有有效的 i 都有 nums[i] != nums[i + 1]

思路

简单来说,一个数比左右的数都大,那就是一个峰值。最为简单的办法就是用遍历数组来完成。性能也还可以。

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        n = len(nums)
        if n <= 1 or nums[0] > nums[1]:
            return 0
        for i in range(1,n-1):
            if nums[i] > nums[i+1] and nums[i] > nums[i-1]:
                return i
        if nums[n-1] > nums[n-2]:
            return n-1

对于所有有效的 i 都有 nums[i] != nums[i + 1],由于有这个条件存在,所以可以直接找到数组中的最大值即可。就不进行具体实现了。

直接来到迭代爬坡的算法,思路就是不断的往高处走,总能走到最高峰。基于此思路用二分查找进行优化,即可达到题目要求的O(logn)时间复杂度。具体为

对于当前可行的下标范围 [l,r],我们随机一个下标 i;

如果下标 i 是峰值,我们返回 i 作为答案;

如果 nums[i]<nums[i+1],那么我们抛弃 [l,i]的范围,在剩余 [i+1,r]的范围内继续随机选取下标;

如果 nums[i]>nums[i+1],那么我们抛弃 [i,r]的范围,在剩余 [l,i−1]的范围内继续随机选取下标。

在上述算法中,如果我们固定选取 i 为 [l,r]的中点,那么每次可行的下标范围会减少一半,成为一个类似二分查找的方法,时间复杂度为 O(log⁡n)。

写了一个二分查找的函数来递归完成,在返回坐标时其实有些麻烦。因为每次递归的是一个子数组,得到的坐标也是相对于子数组的坐标。可以传递左右边界来替代传递子数组来让代码更为直观。

class Solution:
    def erfenSearch(self,nums):
        n = len(nums)
        ans = 0
        def get(index):
            if index == -1 or index == n:
                return float('-inf')
            return nums[index]

        i = (0 + n)//2
        if get(i) > get(i-1) and get(i) > get(i+1):
            return i

        if get(i) < get(i+1):
            return i + self.erfenSearch(nums[i+1:]) + 1
            
        if get(i) > get(i+1):
            return self.erfenSearch(nums[:i])
            
        
    def findPeakElement(self, nums: List[int]) -> int:
        if len(nums) == 1:
            return 0
        return self.erfenSearch(nums)

以下为官方用循环完成的解法

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        n = len(nums)

        # 辅助函数,输入下标 i,返回 nums[i] 的值
        # 方便处理 nums[-1] 以及 nums[n] 的边界情况
        def get(i: int) -> int:
            if i == -1 or i == n:
                return float('-inf')
            return nums[i]
        
        left, right, ans = 0, n - 1, -1
        while left <= right:
            mid = (left + right) // 2
            if get(mid - 1) < get(mid) > get(mid + 1):
                ans = mid
                break
            if get(mid) < get(mid + 1):
                left = mid + 1
            else:
                right = mid - 1
        
        return ans

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值