【排序-中等】1509. 三次操作后最大值与最小值的最小差

本文介绍了一个有趣的数组操作问题:如何通过三次修改元素值的操作使数组的最大值与最小值之差尽可能小,并给出了高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目】
给你一个数组 nums ,每次操作你可以选择 nums 中的任意一个元素并将它改成任意值。
请你返回三次操作后, nums 中最大值与最小值的差的最小值。
【示例 1】
输入:nums = [5,3,2,4]
输出:0
解释:将数组 [5,3,2,4] 变成 [2,2,2,2].
最大值与最小值的差为 2-2 = 0 。
【示例 2】
输入:nums = [1,5,0,10,14]
输出:1
解释:将数组 [1,5,0,10,14] 变成 [1,1,0,1,1] 。
最大值与最小值的差为 1-0 = 1 。
【示例 3】
输入:nums = [6,6,0,1,1,4,6]
输出:2
【示例 4】
输入:nums = [1,5,6,14,15]
输出:1
【提示】
1 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
【代码】
【Python】
执行用时:
84 ms, 在所有 Python3 提交中击败了91.01%的用户
内存消耗:
22.4 MB, 在所有 Python3 提交中击败了25.84%的用户

class Solution:
    def minDifference(self, nums: List[int]) -> int:
        if len(nums)<=4:
            return 0
        nums.sort()
        return min([nums[-1]-nums[3],nums[-2]-nums[2],nums[-3]-nums[1],nums[-4]-nums[0]])

【简洁版】
执行用时:
96 ms, 在所有 Python3 提交中击败了61.80%的用户
内存消耗:
22.4 MB, 在所有 Python3 提交中击败了49.44%的用户

class Solution:
    def minDifference(self, nums: List[int]) -> int:
        if len(nums) <= 4:
            return 0
        n = len(nums)
        nums.sort()
        return min(nums[n - 4 + i] - nums[i] for i in range(4))
<think>好的,我现在需要回答用户关于“最大大问题的多种求解方法”的问题。用户之前已经询问过递归算法求最大值最小值,现在他们想了解如何找到最大值大值的多种方法。首先,我需要确定用户的问题核心:他们希望了解不同的算法方法,包括可能的暴力法、递归分治、迭代法等,并且可能需要复杂度分析以及优缺点比较。 首先,我应该回顾一下常见的寻找最大值大值的算法。通常,最直接的方法是遍历数组,第一找最大,第二大,但这样时间复杂度是O(2n),也就是O(n),不过可以优化到一遍历。另外,分治法也是一种方法,通过递归将数组分成两部分,分别找到各自的最大和大,然后合并结果。还有可能使用堆结构,比如构建一个最大堆,然后取出最大和大元素,不过这种方法的时间复杂度可能不如线性方法高效。 接下来,我需要考虑用户可能的背景。他们可能对算法有一定了解,但需要详细的步骤和比较。因此,回答需要结构清晰,分点列出每种方法,包括步骤描述、时间复杂度、空间复杂度以及优缺点。同时,用户之前提到了递归方法,所以分治递归可能是一个重点,需要详细说明。 还要注意用户提到的“多种求解方法”,这意味着需要至少三种不同的方法。可能的候选方法包括:暴力两遍历法、一遍历比较法、递归分治法、堆排序法等。我需要确定这些方法是否都适用,并准确描述它们。 另外,用户之前的回复中使用了LaTeX公式和表格,因此需要保持格式的一致性,使用适当的数学符号和代码块来展示伪代码或示例。同时,要确保中文表达准确,避免技术术语错误。 在思考过程中,需要验证每种方法的正确性。例如,一遍历法如何同时记录最大和大值,确保在遍历过程中正确更新这两个值。分治法中合并步骤的逻辑是否正确,如何处理子问题的结果合并。堆方法中,构建堆的时间复杂度是否为O(n),然后提取最大和大的步骤是否正确。 还需要考虑边界情况,例如数组中所有元素相同,或者数组长度小于2的情况。在描述每种方法时,是否需要提到这些边界情况的处理? 最后,比较各种方法的优缺点时,需要基于时间、空间复杂度,实现难度,适用场景等维度,以表格形式呈现可能更清晰,如用户之前的回复所示。 总结步骤: 1. 列举几种不同的求解方法:暴力两遍历、一遍历优化、递归分治、堆方法。 2. 对每种方法详细描述步骤,给出伪代码或示例。 3. 分析每种方法的时间和空间复杂度。 4. 比较优缺点,制作对比表格。 5. 注意使用正确的LaTeX格式,如时间复杂度用$O(n)$,变量用$a_i$等。 6. 确保回答结构清晰,符合用户之前接受的格式。 现在需要检查是否有遗漏的方法,例如是否还有其他不常见的算法,如锦标赛法,或者使用选择排序的思路。锦标赛法在找最大和大时可能比较高效,但实现较为复杂,是否值得包含? 例如,锦标赛法的思想是通过类似比赛的方式,两两比较,记录被最大值击败的元素,再在这些元素中找大值。这种方法的时间复杂度也是O(n),但需要额外的空间记录比较路径。这可能是一个值得介绍的方法,但需要准确描述。 因此,可能的解决方法包括: 1. 暴力两遍历法 2.遍历优化法 3. 递归分治法 4. 锦标赛法 5. 堆方法 这样共有五种方法,但需要确保每种方法都正确且值得介绍。需要确认每种方法的步骤和时间复杂度,避免错误。 例如,堆方法中,构建最大堆需要O(n)时间,然后取出最大元素是O(1),但大元素可能需要在剩下的堆中找,或者需要调整堆,这可能增加时间复杂度。或者,构建堆后,大元素可能是根节点的子节点之一,但这并不总是正确。因此,堆方法可能不是最优的,或者需要更复杂的处理,这可能不太适合作为推荐方法。因此可能排除堆方法,或者指出其局限性。 因此,最终决定介绍前四种方法,或者前三种加上锦标赛法。需要根据准确性和实用性进行权衡。 现在开始组织内容: 首先,问题定义:给定数组,找出最大值大值(大值必须小于最大值,且允许重复元素的情况)。 方法一:暴力两遍历 - 步骤:第一遍历找最大,第二遍历找大(小于最大) - 时间复杂度:O(2n) → O(n) - 空间:O(1) - 优点:简单 - 缺点:两遍历,实际比较数较多 方法二:一遍历优化 - 步骤:维护当前最大和大,遍历时比较更新 - 初始化max1和max2,遍历每个元素,若当前元素>max1,则max2=max1,max1=当前;否则如果当前>max2则更新max2 - 需要注意初始化问题,比如数组前两个元素的比较 - 时间复杂度O(n),空间O(1) - 优点:一遍历,效率更高 - 缺点:需要处理初始化和元素重复的情况 方法三:递归分治法 -数组分成左右两部分,分别求左右的最大和大,然后合并 - 合并时比较左右的最大,确定全局最大,大在左右大和另一个部分的最大中寻找 - 时间复杂度递推式:T(n) = 2T(n/2) + 2 → O(n) - 空间复杂度O(logn)递归栈 - 优点:分治思想,适合并行处理 - 缺点:空间复杂度较高,实现较复杂 方法四:锦标赛法 - 两两比较,构建二叉树,记录被最大元素击败的节点,然后在这些节点中找- 时间复杂度O(n),因为比较数为n-1找最大,然后log n- 空间复杂度O(n)存储比较路径 - 优点:理论最优比较- 缺点:实现复杂,需要额外空间 然后,对比表格,总结优缺点。 需要确保每种方法的描述准确,尤其是分治法和锦标赛法的合并步骤是否正确。 例如,分治法中,当左右部分返回各自的最大和大,合并时,全局最大是左最大或右最大,假设左最大是全局最大,那么大可能是左大或右最大,反之亦然。因此合并时的大是max(left_second, right_max)当左最大更大时。需要正确描述这个逻辑。 在锦标赛法中,大值只能出现在最大值直接比较过的元素中,因此需要记录这些元素,然后在这些元素中找最大,即为大值。这种方法确实需要额外的空间来记录这些元素,但比较数为n + log n - 2,是理论最优的。 需要验证这些方法的正确性,比如一遍历法是否能处理所有情况,例如数组递减或递增的情况。 例如,数组[5,4,3,2,1],一遍历法正确找到5和4;数组[1,3,5,7,9],正确找到9和7;数组[2,2,2],则大值不存在或也为2,根据问题定义可能需要处理。 在问题定义中,是否需要大值必须严格小于最大,还是允许等于?这会影响实现,但通常大是严格小于,但用户问题可能需要明确说明。但用户之前的问题中,递归算法是处理最大和最小,所以此处可能默认大是严格小于最大的,但需要根据具体情况处理。 现在,将这些思考整理成结构化的回答,使用用户之前接受的格式,包括分节、代码块、LaTeX公式和表格。</think>以下是对寻找最大值大值问题的多种解法的详细说明: --- ### 问题定义 给定数组 $a_1, a_2, ..., a_n$,要求: - **最大值**:$MAX = \max(a_1, a_2, ..., a_n)$ - **大值**:$SECOND\_MAX = \max\\{x \mid x < MAX\\}$(若无严格大值则返回特定值) --- ### 方法一:暴力两遍历法 **步骤**: 1. **第一遍历**:找到最大值 $MAX$ 2. **第二遍历**:在剩余元素中找到最大值即为大值 **伪代码**: ```python def find_max_second_max(arr): if len(arr) < 2: return None # 无解 max1 = max(arr) max2 = -inf for num in arr: if num != max1 and num > max2: max2 = num return (max1, max2) ``` **复杂度**: - 时间复杂度:$O(2n) \rightarrow O(n)$ - 空间复杂度:$O(1)$ **优缺点**: - ✅ 简单直观 - ❌ 两遍历,实际比较数较多 --- ### 方法二:一遍历优化法 **核心思想**:在单遍历中动态维护当前最大值大值 **步骤**: 1. 初始化 $max1$ 和 $max2$(需处理前两个元素的比较) 2. 遍历每个元素 $x$: - 若 $x > max1$:更新 $max2 = max1$,$max1 = x$ - 否则若 $x > max2$:更新 $max2 = x$ **伪代码**: ```python def find_max_second_max(arr): if len(arr) < 2: return None max1 = max(arr[0], arr[1]) max2 = min(arr[0], arr[1]) for i in range(2, len(arr)): if arr[i] > max1: max2 = max1 max1 = arr[i] elif arr[i] > max2: max2 = arr[i] return (max1, max2) ``` **复杂度**: - 时间复杂度:$O(n)$ - 空间复杂度:$O(1)$ **边界处理**: - 若所有元素相同(如 `[5,5,5]`),需特殊处理大值 **优缺点**: - ✅ 单遍历,效率高 - ❌ 需要仔细处理初始化和边界条件 --- ### 方法三:递归分治法 **核心思想**:将数组分为左右两部分,递归求解后合并结果 **合并策略**: - 全局最大值 $G_{max} = \max(L_{max}, R_{max})$ - 全局大值 $G_{second} = \max\\{L_{second}, R_{second}, \min(L_{max}, R_{max})\\}$ **伪代码**: ```python def find_max_second_max(arr, left, right): if left == right: return (arr[left], -inf) # 单元素无大值 if left + 1 == right: return (max(arr[left], arr[right]), min(arr[left], arr[right])) mid = (left + right) // 2 L_max, L_second = find_max_second_max(arr, left, mid) R_max, R_second = find_max_second_max(arr, mid+1, right) global_max = max(L_max, R_max) global_second = max(L_second, R_second, L_max if R_max > L_max else R_max) return (global_max, global_second) ``` **复杂度分析**: - 递推关系:$T(n) = 2T(n/2) + 2 \rightarrow T(n) = O(n)$ - 空间复杂度:$O(\log n)$(递归调用栈) **优缺点**: - ✅ 分治思想适合并行计算 - ❌ 递归栈空间开销较大 --- ### 方法四:锦标赛法 **核心思想**:通过两两比较构建“锦标赛树”,大值只能是被最大值直接击败的元素 **步骤**: 1. **构建锦标赛树**:记录所有最大值比较过的元素 2. **在败者集合中找大值** **示例**: ``` 数组 [5, 3, 9, 2] 的锦标赛过程: 第一轮:5 vs3 → 5胜;9 vs2 →9胜 决赛:5 vs9 →9胜(MAX=9) 败者集合:5(被9击败),2(被9击败) → 大值为5 ``` **复杂度**: - 时间复杂度:$O(n)$(比较数为$n + \log n -2$) - 空间复杂度:$O(n)$(存储比较路径) **优缺点**: - ✅ 理论最优比较- ❌ 实现复杂,需额外空间 --- ### 方法对比总结 | 方法 | 时间复杂度 | 空间复杂度 | 实现难度 | 适用场景 | |--------------------|------------|------------|----------|------------------| | 暴力两遍历 | $O(n)$ | $O(1)$ | 简单 | 小数据量 | | 一遍历优化 | $O(n)$ | $O(1)$ | 中等 | 通用场景 | | 递归分治法 | $O(n)$ | $O(\log n)$| 复杂 | 分治练习/并行化 | | 锦标赛法 | $O(n)$ | $O(n)$ | 困难 | 理论最优比较数 | --- ### 关键问题讨论 1. **重复元素处理**:若数组全为相同值,需定义大值为特殊标记(如`None`) 2. **初始化问题**:一遍历法需正确处理前两个元素的初始比较 3. **极端情况**:数组长度小于2时需返回错误或特定值 **实际应用建议**: - 优先选择 **一遍历优化法**(效率简洁性平衡) - 大规模数据避免递归分治(栈溢出风险) - 锦标赛法适用于对比较数敏感的场景
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值