852. Peak Index in a Mountain Array

本文探讨了如何在山形数组中寻找峰值索引的方法,包括遍历、二分查找等算法,并分析了各种方法的时间复杂度。对于特定类型的数组问题,采用二分查找能够显著提高效率。

Description

Let’s call an array arr a mountain if the following properties hold:
arr.length >= 3
There exists some i with 0 < i < arr.length - 1 such that:
arr[0] < arr[1] < ... arr[i-1] < arr[i]
arr[i] > arr[i+1] > ... > arr[arr.length - 1]
Given an integer array arr that is guaranteed to be a mountain, return any i such that arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i + 1] > ... > arr[arr.length - 1].

Thoughts

We can traverse the whole list and find the first position i that has arr[i + 1] > arr[i] since there is not dup in the problem. The time complexity is O(n)

We find some regularity in the array. It’s increasing and then decreasing. We can use binary search to see if the peak is in the first half or the last half. This will reduce the time complexity to O(logn)


Exceptions

  • No special cases if we use bruteforce to traverse the list.
  • It’s important that there’s no duplicate numbers in the array. Otherwise the binary search method will not work. Because for the mid with nums[mid + 1] == nums[mid] we cannot identify which part it should be.
  • To avoid any index out of range or infinite loop, we only consider the binary part when the length of sliced array is larger than 2. For any array that has only 1 or 2 elements, we just compare the elements and return the large one.

Python Code

Brute-force

    def peakIndexInMountainArray(self, arr: List[int]) -> int:       
        for i in range(len(arr) - 1):
            if arr[i + 1] < arr[i]:
                return i

While-loop Binary Search

    def peakIndexInMountainArray(self, arr: List[int]) -> int:
        start, end = 0, len(arr) - 1
        '''
        This is to avoid index out of range or infinite loop
        '''
        while start < end - 1:
            mid = (start + end) // 2
            if arr[mid + 1] > arr[mid]:
                start = mid
            if arr[mid + 1] < arr[mid]:
                end = mid
        '''
        We have 1 or 2 elements after the while loop.
        Need to check them and return the large one.
        '''
        if arr[start] >= arr[end]:
            return start
        return end

Recursive Binary Search

    def peakIndexInMountainArray(self, arr: List[int]) -> int:
        return self.binarysearch(arr, 0, len(arr) - 1)
        
    def binarysearch(self, arr, start, end):
        '''
        This is the same as start < end - 1 in above while-loop.
        We only do binary search to the array which has at least 
        3 elements to avoid index out of range or infinite loop.
        In recursive method, we treat them as special cases and check it at the begining.
        '''
        if start >= end - 1:
            if arr[start] >= arr[end]:
                return start 
            return end
        
        mid = (start + end) // 2
        if arr[mid + 1] > arr[mid]:
            return self.binarysearch(arr, mid, end)
        if arr[mid + 1] < arr[mid]:
            return self.binarysearch(arr, start, mid)

Summary

We can think of binary search if we see any array related problems which has the up or down trend. We can try binary search as long as we can find rules to check if the result is in the left side and right side.


以下是修改后的代码,添加了画图过程。在每次处理完一行数据后,绘制滤波前后的信号以及找到的峰值点。 ```python import numpy as np from scipy.signal import find_peaks, butter, filtfilt from scipy.io import savemat import matplotlib.pyplot as plt # 加载.npy文件 data = np.load('ch1_data.npy') # 滤波参数设置(Butterworth低通滤波器) fs = 3200_000_000 # 3.2 GHz cutoff = 5000_000 # 截止频率为5 MHz nyquist = 0.5 * fs normal_cutoff = cutoff / nyquist order = 4 # 滤波器阶数 # 创建Butterworth低通滤波器 b, a = butter(order, normal_cutoff, btype='low', analog=False) # 初始化存储结果的列表 peak1_list = [] peak2_list = [] peak3_list = [] peaks_list = [] # 遍历每一组数据 (5000行,每行1024个数据) for idx, row in enumerate(data): # 对当前行数据进行滤波 filtered_row = filtfilt(b, a, row) # 查找峰值 peaks, _ = find_peaks(filtered_row, height=0) # 根据需求调整height阈值 num_peaks = len(peaks) # 绘制原始信号、滤波后信号及找到的峰值 plt.figure(figsize=(12, 6)) plt.subplot(2, 1, 1) plt.plot(row, label="Original Signal", color="blue") plt.title(f"Row {idx} - Original Signal") plt.legend() plt.subplot(2, 1, 2) plt.plot(filtered_row, label="Filtered Signal", color="green") plt.plot(peaks, filtered_row[peaks], "x", label="Peaks", color="red") plt.title(f"Row {idx} - Filtered Signal with Peaks (Num Peaks: {num_peaks})") plt.legend() plt.tight_layout() plt.show() # 根据峰值数量分类保存 if num_peaks == 1: peak1_list.append(row) # 保存原始数据或滤波后的数据 elif num_peaks == 2: peak2_list.append(row) elif num_peaks == 3: peak3_list.append(row) else: # 峰值数量大于等于4 peaks_list.append(row) # 将分类好的数据保存为npy文件 np.save('peak1.npy', np.array(peak1_list)) np.save('peak2.npy', np.array(peak2_list)) np.save('peak3.npy', np.array(peak3_list)) np.save('peaks.npy', np.array(peaks_list)) savemat('peaks.mat', {'peaks': peaks_list}) print("处理完成!") ``` ### 上述代码解释: 1. **加载数据**:使用`np.load`加载`.npy`文件中的数据。 2. **滤波器设计**:通过`butter`函数设计一个4阶Butterworth低通滤波器,截止频率为5 MHz。 3. **滤波处理**:对每一行数据使用`filtfilt`进行零相位滤波,以避免相位失真。 4. **峰值检测**:使用`find_peaks`函数查找滤波后信号中的峰值,并根据峰值数量分类保存到不同列表中。 5. **绘图**: - 使用`matplotlib`库绘制两幅子图: - 第一幅子图显示原始信号。 - 第二幅子图显示滤波后的信号及找到的峰值点。 - 每次处理完一行数据后都会弹出一个窗口显示图形。 6. **保存结果**:将分类后的数据保存为`.npy`文件和`.mat`文件。 ### 注意事项: - 如果数据量较大(如5000行),每次绘制图形可能会导致程序运行较慢。可以考虑每隔若干行绘制一次图形,或者仅绘制部分行的图形。 - `height=0`可以根据实际需求调整为合适的阈值,以过滤掉不感兴趣的较小峰值。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值