2026-07-09:替换最多一个元素后的最长等差子数组。用go语言,给你一个整数数组 nums。 如果某个连续子数组满足:子数组里任意相邻两个元

网易专栏4周前发布 nxnqh
38 0 0

🤖 AI总结

主题

替换最多一个元素后的最长等差子数组算法解析

摘要

本文详细解析了替换最多一个元素后求最长等差子数组长度的算法,通过分段处理原生等差区间并尝试左右修改元素来延长,复杂度为O(n)。

关键信息

  • 1 通过枚举等差区间并尝试修改相邻元素来延长子数组
  • 2 算法时间复杂度O(n),空间复杂度O(1)
  • 3 提供Go、Python、C++三种语言实现

2026-07-09:替换最多一个元素后的最长等差子数组。用go语言,给你一个整数数组 nums。

如果某个连续子数组满足:子数组里任意相邻两个元素之差都一样(也就是差值恒定),那么这个子数组称为等差子数组。

你最多可以做一次操作:把数组中的任意一个元素替换成任意整数(只允许替换一个位置,且可以选择不替换)。

在完成这次替换(或不替换)后,从数组中挑选一个等差子数组。

要求你返回:在这种条件下,你最多能找到的最长等差子数组的长度。

4 <= nums.length <= 100000。

1 <= nums[i] <= 100000。

输入: nums = [9,7,5,10,1]。

输出: 5。

解释:

将 nums[3] = 10 替换为 3,数组变为 [9, 7, 5, 3, 1]。

选择子数组 [9, 7, 5, 3, 1],它是等差数组,相邻元素的公差为 -2。

题目来自力扣3872。

一、逐步骤拆解执行流程

数组下标:0:9,1:7,2:5,3:10,4:1

步骤1:初始化基础变量

函数入口:

n = len(nums) = 5

  • ans = 0,保存全局最优长度;

  • • 循环变量i从1开始,作为滑动右边界指针。

    步骤2:外层循环遍历每一段原生等差区间

    循环逻辑:每次取i-1、i作为当前等差段的起始两个点,计算本段公差d,再向右延伸找到本段完整原生等差区间。

    第一轮循环初始状态 i=1

    1. 标记本段起点start = i-1 = 0

  • 2. 计算公差d = nums[1]-nums[0] = 7-9 = -2

  • 3. 向右延伸指针i,不断判断下一对相邻差是否等于d:

    • i=2:nums[2]-nums[1]=5-7=-2 == d → i自增到3;

  • • i=3:nums[3]-nums[2]=10-5=5 ≠ -2 → 停止延伸;
    此时确定原生等差区间 [start, i-1] = [0,2],对应元素[9,7,5],原生长度3。

    步骤3:方案1——尝试修改区间左侧紧邻元素,向左拼接延长

    逻辑:修改start-1位置的数字,看能否和当前等差段合并。

    1. 判断左边界是否存在:start=0start >=2不成立,无法向左拼接两段;

  • 2. 左端点最远只能取max(start-1,0)=0

  • 3. 当前区间右边界是 i-1=2,区间长度 =i - max(start-1,0) = 3-0 =3

  • 4. 更新ans,当前 ans=3。

    步骤4:方案2——尝试修改区间右侧紧邻元素,向右拼接延长

    右侧断点是下标 i=3(值10),尝试修改该点,和下一个元素拼接:

    1. 判断右侧是否还有元素:i=3 < n-1(4),存在下一位 i+1=4(值1);

  • 2. 校验拼接条件:nums[i+1] - nums[i-1] == d*2

    • nums[i+1]=1,nums[i-1]=5,差值=1-5=-4;

  • • d=-2,d*2=-4,等式成立;
    含义:修改i位置元素后,nums[i-1]、修改后的nums[i]、nums[i+1] 三者公差均为d,能连成等差序列。

    3. 继续向右延伸指针j,从i+2=4开始,判断后续相邻差是否等于d:

    • j=4:nums[4]-nums[3]=1-10=-9≠-2,循环终止;

    4. 可拼接完整区间是[start, j-1] = [0,4],长度j-start =4-0=5

    5. 更新全局ans,ans从3变为5。

    步骤5:判断是否遍历完数组,进入下一段

    当前i=3,不等于n=5,外层循环继续,i保持3进入下一轮区间查找。

    第二轮外层循环 i=3

    1. 本段起点start = i-1 =2

  • 2. 公差d = nums[3]-nums[2] =10-5=5

  • 3. 向右延伸i:i自增到4,nums[4]-nums[3]=1-10=-9≠5,停止;
    原生等差区间[2,3],元素[5,10],原生长度2。

    步骤3:修改左侧元素向左拼接

    start=2 ≥2,校验nums[start]-nums[start-2] == d*2

    • nums[start]=10,nums[start-2]=9,差值1;

  • • d*2=10,1≠10,不满足拼接条件;

  • • 左端点取max(2-1,0)=1,区间长度 i – 1 =4-1=3;
    ans当前是5,不更新。

    步骤4:修改右侧元素向右拼接

    i=4,i < n-1(4<4)不成立;
    直接计算区间长度i-start+1 =4-2+1=3,ans仍为5。

    步骤5:判断i==n

    当前i=4≠5,外层循环继续,i=4进入下一轮区间查找。

    第三轮外层循环 i=4

    1. start = i-1=3;

  • 2. d = nums[4]-nums[3] =1-10=-9;

  • 3. i自增到5,等于数组长度n,停止向右延伸;
    原生等差区间[3,4],原生长度2。

    步骤3:修改左侧元素向左拼接

    start=3≥2,校验nums[3]-nums[1] == d*2
    nums[3]=10,nums[1]=7,差值3;d*2=-18,不相等;
    左端点取max(3-1,0)=2,区间长度 i -2 =5-2=3,ans不变。

    步骤4:修改右侧元素向右拼接

    i=5,i < n-1不成立;区间长度5-3+1=3,ans不变。

    步骤5:i==n=5,满足返回条件,退出循环,返回ans=5

    最终输出结果5,和题目示例匹配。

    二、算法通用完整逻辑(脱离示例,通用流程) 阶段1:分割所有天然等差连续段

    1. 指针i从1出发,每次取i-1、i作为一段等差区间的起始两点,算出公差d;

  • 2. i持续右移,直到相邻差值不等于d,得到一段完整天然等差区间[start, i-1]

  • 3. 针对当前这段等差区间,分左右两种修改方案计算最长可拼接长度。

    阶段2:左改方案(修改start-1位置元素,向左延长)

    目标:只修改1个点,把左边片段和当前等差段合并。

    1. 边界判断:若start≥2(左边至少存在两个元素nums[start-2]、nums[start-1]);

  • 2. 拼接判定条件:nums[start] - nums[start-2] == 2*d

    • 原理:修改nums[start-1]后,nums[start-2]、修改后nums[start-1]、nums[start] 公差为d,两段可以无缝拼接;

  • • 满足条件:合并后区间左边界到start-2,长度i - start + 2

  • • 不满足条件:最多只能修改start-1,左边界到start-1,长度i - max(start-1,0)

    3. 用算出的长度更新全局最大值ans。

    阶段3:右改方案(修改i位置元素,向右延长)

    目标:只修改1个断点i,把当前等差段和右侧元素拼接。

    1. 边界判断:i < n-1(右侧还有至少一个元素nums[i+1]);

  • 2. 拼接判定条件:nums[i+1] - nums[i-1] == 2*d

    • 原理:修改nums[i]后,nums[i-1]、修改后nums[i]、nums[i+1] 公差为d;

  • • 满足条件:继续向右遍历所有后续公差为d的元素,得到最远j,合并区间长度j - start

  • • 不满足条件:仅修改i,区间右边界到i,长度i - start + 1

    3. 用算出的长度更新全局最大值ans。

    阶段4:循环终止判定

    当i右移到数组末尾n时,所有等差段处理完毕,返回全局最大ans。

    三、时间复杂度、空间复杂度分析 1. 时间复杂度 O(n),n为数组长度

    • 所有指针i、j均只单向向右移动,全程不会回退;

  • • 数组每个下标只会被i或j访问1次,不存在嵌套循环重复遍历元素;

  • • 仅单层线性遍历,所有判断、计算均为常数O(1)操作;

  • • 数组上限1e5,线性复杂度满足性能要求。

    2. 额外空间复杂度 O(1)

    • 仅使用固定数量基础变量:n、ans、i、start、d、j;

  • • 没有开辟数组、哈希表、切片等动态存储,不随输入数组长度变化;

  • • 仅常数级临时空间。

    Go完整代码如下:

    package main

    import (
    "fmt"
    )

    func longestArithmetic(nums []int) (ans int) {
    n := len(nums)
    for i := 1; ; {
    // 枚举 i-1 和 i 作为等差子数组的前两项,且我们不改 nums[i-1] 和 nums[i]
    start := i - 1
    d := nums[i] - nums[i-1]

    // 往右移动,直到 nums[i] 不满足等差
    for i++; i < n && nums[i]-nums[i-1] == d; i++ {
    }

    // 现在 [start, i-1] 是等差子数组
    // 要想让子数组更长,要么改 nums[start-1],要么改 nums[i]

    // 改 nums[start-1]
    if start >= 2 && nums[start]-nums[start-2] == d*2 { // 可以和 nums[start-2] 连起来
    ans = max(ans, i-start+2) // 等差子数组 [start-2, i-1]
    // 继续往左延长的情况等同于上一段继续往右延长,无需重复计算
    } else { // 子数组左端点最远只能到 max(start-1,0)
    ans = max(ans, i-max(start-1, 0)) // 等差子数组 [max(start-1,0), i-1]
    }

    if i == n {
    return
    }

    // 改 nums[i]
    if i < n-1 && nums[i+1]-nums[i-1] == d*2 { // 可以和 nums[i+1] 连起来
    // 继续往右延长
    j := i + 2
    for ; j < n && nums[j]-nums[j-1] == d; j++ {
    }
    ans = max(ans, j-start) // 等差子数组 [start, j-1]
    } else { // 子数组右端点最远只能到 i
    ans = max(ans, i-start+1) // 等差子数组 [start, i]
    }
    }
    }

    func main() {
    nums := []int{9, 7, 5, 10, 1}
    result := longestArithmetic(nums)
    fmt.Println(result)
    }

    2026-07-09:替换最多一个元素后的最长等差子数组。用go语言,给你一个整数数组 nums。 如果某个连续子数组满足:子数组里任意相邻两个元

    Python完整代码如下:

    # -*-coding:utf-8-*-

    def longestArithmetic(nums):
    n = len(nums)
    if n < 2:
    return n

    ans = 0
    i = 1
    while True:
    start = i - 1
    d = nums[i] - nums[i - 1]

    # 向右扩展,直到不满足等差
    while i < n and nums[i] - nums[i - 1] == d:
    i += 1

    # 现在 [start, i-1] 是最长等差子数组(不修改任何元素)

    # 尝试修改 nums[start-1] 来延长
    if start >= 2 and nums[start] - nums[start - 2] == d * 2:
    ans = max(ans, i - start + 2) # 可延长到 start-2
    else:
    ans = max(ans, i - max(start - 1, 0)) # 左端点最多到 max(start-1, 0)

    if i == n:
    return ans

    # 尝试修改 nums[i] 来延长
    if i < n - 1 and nums[i + 1] - nums[i - 1] == d * 2:
    j = i + 2
    while j < n and nums[j] - nums[j - 1] == d:
    j += 1
    ans = max(ans, j - start) # 可延长到 j-1
    else:
    ans = max(ans, i - start + 1) # 右端点最多到 i

    # 继续下一轮,i 指向第一个不满足等差的位置

    def main():
    nums = [9, 7, 5, 10, 1]
    result = longestArithmetic(nums)
    print(result)

    if __name__ == "__main__":
    main()

    2026-07-09:替换最多一个元素后的最长等差子数组。用go语言,给你一个整数数组 nums。 如果某个连续子数组满足:子数组里任意相邻两个元

    C++完整代码如下:

      
    


    using namespace std;

    int longestArithmetic(vector& nums) {
    int n = nums.size();
    if (n < 2) return n;

    int ans = 0;
    int i = 1;

    while (true) {
    // 枚举 i-1 和 i 作为等差子数组的前两项,且我们不改 nums[i-1] 和 nums[i]
    int start = i - 1;
    int d = nums[i] - nums[i - 1];

    // 往右移动,直到 nums[i] 不满足等差
    i++;
    while (i < n && nums[i] - nums[i - 1] == d) {
    i++;
    }

    // 现在 [start, i-1] 是等差子数组
    // 要想让子数组更长,要么改 nums[start-1],要么改 nums[i]

    // 改 nums[start-1]
    if (start >= 2 && nums[start] - nums[start - 2] == d * 2) {
    // 可以和 nums[start-2] 连起来
    ans = max(ans, i - start + 2); // 等差子数组 [start-2, i-1]
    // 继续往左延长的情况等同于上一段继续往右延长,无需重复计算
    } else {
    // 子数组左端点最远只能到 max(start-1,0)
    ans = max(ans, i - max(start - 1, 0)); // 等差子数组 [max(start-1,0), i-1]
    }

    if (i == n) {
    return ans;
    }

    // 改 nums[i]
    if (i < n - 1 && nums[i + 1] - nums[i - 1] == d * 2) {
    // 可以和 nums[i+1] 连起来
    // 继续往右延长
    int j = i + 2;
    while (j < n && nums[j] - nums[j - 1] == d) {
    j++;
    }
    ans = max(ans, j - start); // 等差子数组 [start, j-1]
    } else {
    // 子数组右端点最远只能到 i
    ans = max(ans, i - start + 1); // 等差子数组 [start, i]
    }
    }
    }

    int main() {
    vector nums = {9, 7, 5, 10, 1};
    int result = longestArithmetic(nums);
    cout << result << endl;
    return0;
    }

    2026-07-09:替换最多一个元素后的最长等差子数组。用go语言,给你一个整数数组 nums。 如果某个连续子数组满足:子数组里任意相邻两个元

    我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。 欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。

    © 版权声明

    相关文章