【问题标题】:How to find peak elements in an array when there are multiple peaks?当有多个峰时如何在数组中找到峰元素?
【发布时间】:2018-01-24 14:33:56
【问题描述】:

对于具有单个峰值元素的数组,可以使用二进制搜索在 o(logn) 中完成,但是如果数组中有多个峰值元素,我们应该使用什么方法?

---提供更多信息---- 一个峰值元素是比它的邻居更大的东西,例如,看看下面的数组,

[1,3,20,4,1,0,7,5,2]

里面有2个山峰,20个和7个。

我们需要设计一个算法来找到这个数组中的峰值元素。

【问题讨论】:

  • 只按值排序?
  • 究竟什么是多峰?最大值,出现多次?需要对数组进行排序以进行二进制搜索。如果已排序,这些峰必须彼此相邻,因此找到它们并不是问题,对吧?
  • @GillesLesire 排序是 O(n log n),线性扫描是 O(n),有序数组的最大元素在最后,所以对于一个有序数组来说,复杂度应该是 O(1)。不知道 O(log n) 是从哪里来的。
  • @f1sh 多个峰值可能意味着系列中的多个值显着高于其他值,但不一定彼此相等。例如,我们在频谱(频率)分析中看到了这种峰值。如果是这种情况,那么如果值的原始索引也没有以某种方式存储,那么排序将无济于事。因为在这种情况下,指标可能比峰值的值更重要。
  • @f1sh 我同意。如果 OP 正在谈论一个相当数学的问题,我猜他是,那么我也不希望在任何地方看到任何二进制搜索算法。他可能首先需要定义什么是“峰值”。它只是一个比其他值更大的值,还是一个比其他值“更大”的值? “更大”是多少?我认为问题的定义还不够好。

标签: java arrays algorithm binary-search


【解决方案1】:

我可能不明白你的问题,因为找到单峰可以在 O(logn) 中完成,需要首先对数组进行排序。

我建议您存储一个差异数组,该数组将生成如下输出:[1,3,20,4,1,0,7,5,2] => [1, 1,-1,-1,-1, 1,-1,-1] 这只是生成一个大小为n - 1 的数组并将增加的方向放在数组中。这可以在 O(n) 单程中完成。

在第二遍中,您将寻找 [1, -1] 对,这是出现峰值的地方。

如果要在起点和终点找到峰值,则需要检查起点是否为-1,终点是否为1。

希望这会有所帮助。

【讨论】:

  • o(logn) 中你能想到的任何东西
  • 如果你有单个数组是不可能的。
  • 如果恰好有一个峰,则为二分搜索:O(log n)。如果可能有任意数量的峰值,则意味着您必须检查所有元素是一种方式或其他方式,因此它不能比 O (n) 快
【解决方案2】:

我最近在一个编程网站上遇到了类似的问题,但是除了找到峰值之外,还有另一个重要的限制。首先,可以有多个高峰,但也可以有高原!高原是出现在 arr: [1,2,2,2,1] 中的东西,在这种情况下,我们必须将峰值返回为 2。在问题中,我们还被要求返回峰值所在的第一个位置发生,所以在这种情况下,它将是位置 = 1。我们必须保持小心的唯一情况是在 arr: [1,2,2,2,3,4,5,1] 中,有峰值5 在倒数第二个位置,但没有高原,即使有一系列重复的第一眼看起来像高原。

因此,我考虑的基本算法是使用差异数组,正如已经建议的那样,使用 {1,0,-1},当我们在连续流中获得一系列重复项时,我们使用 0。每当我们在检查差异数组时得到 0 时,我们就开始寻找第一个非零条目,无论它是 1 还是 -1 并相应地输出。但是我们必须再次注意,Difference 数组可能看起来像 Diff = [-1,1,-1,1,0,0,0],在这种情况下没有高原,只有一个峰值。当输入 arr 时,python 代码如下所示:

n = len(arr)
if arr == []:
    return {"pos":[],"peaks":[]}
Diff = []
for i in range(0,n-1):
    if arr[i]< arr[i+1]:
        Diff.append(1)
    elif arr[i] == arr[i+1]:
        Diff.append(0)
    if arr[i] > arr[i+1]:
        Diff.append(-1)
pos = []
peaks = []
j = 0
for i in range(0,n-2):
    if Diff[i] == 1 and Diff[i+1] == -1:
        pos.append(i+1)
        peaks.append(arr[i+1])
    if Diff[i] == 1 and Diff[i+1] == 0:
        if Diff[i+1:] == [Diff[i+1]]:
            break
        j = i+1
        while(Diff[j] == 0 and j < n-2):
            j = j+1
        if Diff[j] == -1:
            pos.append(i+1)
            peaks.append(arr[i+1])


return {"pos":pos, "peaks":peaks} 

【讨论】:

    【解决方案3】:

    用于在有多个峰时查找所有峰元素。

    class Solution(object):
        def findPeakElement(self, nums):
            """
            :type nums: List[int]
            :rtype: int
            """
            left, right = 0, len(nums) - 1
            while left < right:
                mid = left + right >> 1
                if nums[mid] < nums[mid + 1]:
                    left = mid + 1
                else:
                    right = mid
            return left
    

    【讨论】:

      【解决方案4】:
      int arr[] = {50, 12, 13, 20, 16, 19, 11, 7, 24};
      int size = sizeof(arr) / sizeof(arr[0]);
      
      int peak_arr[size / 2];
      int i, j, k = 0;
      int next, previous = 0;
      
      for(i = 0; i < size; i++){
          if(i + 1 == size){
              next = 0;
          }
          else{
              next = arr[i + 1];
          }
          
          if(arr[i] > next && arr[i] >= previous){
              peak_arr[k++] = arr[i];
          }
          previous = arr[i];
      }
      

      【讨论】:

      • 虽然您的答案可能是正确的解决方案,但对您的代码进行一些解释会有所帮助。
      【解决方案5】:

      使用递归方法查找多个峰元素。

      def Div(lst, low, high):
          mid = (low + high)//2
          if low > high:
              return ""
          else:
              if mid+1 < len(lst) and lst[mid] > lst[mid+1] and lst[mid] > lst[mid -1]:
                  return str(lst[mid]) + " " + Div(lst, low, mid-1) + Div(lst, mid+1, high)
              else:
                  return Div(lst, low, mid-1) + Div(lst, mid + 1, high)
      
      def peak(lst):
          if lst == []:
              return "list is empty"
          elif len(lst) in [1, 2]:
              return "No peak"
          else:
              return Div(lst, 0, len(lst))
      
      print(peak([0, 5, 2, 9, 1, 10, 1]))
      

      【讨论】:

      • 您的答案不是有效的 Python 代码,请编辑。
      • @JariTurkia 代码现已修复。谢谢你指出。我是stackoverflow的新手,所以不知道如何格式化代码。
      【解决方案6】:

      这个想法是使用修改后的二进制搜索。

      如果arr[mid+1]&gt;arr[mid],那么你的峰值总是在右半边。

      如果arr[mid-1]&gt;arr[mid],那么您的峰值始终存在于左半部分。 因为 arr[i+1] 只有两个选项,要么大于 arr[i],要么小于 arr[i-1]

      我没有 java 实现,但这里有一个 python 实现。

      def FindAPeak(arr, i, j):
          mid = (i+j)/2
          # if mid element is peak
          if (mid == len(arr)-1 or arr[mid] > arr[mid+1]) and (mid == 0 or arr[mid] > arr[mid-1]):
              return arr[mid]
          # when your peak exists in the right half
          if arr[mid] < arr[mid+1] and mid+1 < len(arr):
              return FindAPeak(arr, mid+1, j)
          # when your peak exists in the left half
          else:
              return FindAPeak(arr, i, mid-1)
      
      
      In [31]: arr = [1,2,3,2,1]
          ...: FindAPeak(arr, 0, len(arr)-1)
          ...: 
      Out[31]: 3
      
      
      In [32]: 
          ...: arr = [1,2,3,4,5,6]
          ...: FindAPeak(arr, 0, len(arr)-1)
          ...: 
      Out[32]: 6
      

      【讨论】:

      • 不一定,对面还是可以有一个峰,所以要找到所有的峰,上面的算法是错误的。 eg: 5,4,3,2,1,0,5,2 两边都有一个峰
      猜你喜欢
      • 1970-01-01
      • 2018-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-05
      • 2017-09-17
      • 1970-01-01
      相关资源
      最近更新 更多