2026-04-06:字典序最小和为目标值且绝对值是排列的数组。用go语言,给你一个正整数 n 和一个整数 target。 你需要构造一个长度为 n 的整数数组

0 阅读6分钟

2026-04-06:字典序最小和为目标值且绝对值是排列的数组。用go语言,给你一个正整数 n 和一个整数 target。

你需要构造一个长度为 n 的整数数组,要求同时满足:

1.数组中所有元素的总和必须等于 target。

2.把数组里每个元素取绝对值以后,得到的这 n 个数必须是 1,2,...,n 的某种排列(也就是每个数 1 到 n 都恰好出现一次,顺序可以不同)。

3.若不存在满足上述条件的数组,则返回一个空数组 []。

另外,在所有满足条件的数组里,你要返回字典序最小的那个。

字典序比较规则:对任意两个数组 a 和 b,从左到右找到它们的第一个不同位置 i,如果 a[i] < b[i],则认为 a 的字典序小于 b。

因此,总波动值为 1 + 1 + 1 = 3。

1 <= n <= 100000。

-10000000000 <= target <= 10000000000。

输入: n = 3, target = 0。

输出: [-3,1,2]。

解释:

和为 0 且绝对值组成大小为 3 的排列的数组有:

[-3, 1, 2]

[-3, 2, 1]

[-2, -1, 3]

[-2, 3, -1]

[-1, -2, 3]

[-1, 3, -2]

[1, -3, 2]

[1, 2, -3]

[2, -3, 1]

[2, 1, -3]

[3, -2, -1]

[3, -1, -2]

字典序最小的是 [-3, 1, 2]。

题目来自力扣3752。

分步详细过程

我们以n=3,target=0为例,完整拆解解题的每一步逻辑,全程不写代码,只讲原理和操作。

第一步:判断是否存在合法数组

首先要确定有没有满足条件的数组,这是解题的前提,核心分3个小步骤:

  1. 计算1~n的总和最大值 1到n所有数相加的总和是固定值,公式:总和 = n*(n+1)/2 n=3时:1+2+3=6,这个值是所有元素全为正数时的总和,记为mx

  2. 判断target的数值范围是否合法 数组元素是1~n加正负号,所以总和的最大值是mx(全正),最小值是-mx(全负)。 如果target > mx 或者 target < -mx,直接判定无合法数组。 n=3,target=0:0在-6和6之间,范围合法。

  3. 判断奇偶性是否匹配 我们把一个正数x改成负数-x,总和的变化是:减少了2x(总和 = 原总和 - 2x)。 这意味着:原总和 - 目标值 必须是偶数(因为是2的倍数),否则无法通过改符号得到target。 公式:(mx - target) % 2 == 0 才合法。 n=3,mx=6,target=0:6-0=6,6是偶数,奇偶性合法。

    ✅ 三个条件都满足,存在合法数组;任意一个不满足,直接返回空数组。

第二步:计算需要取负号的数字总和

这是核心计算步骤,目的是找到哪些数字需要加负号

  1. 设:需要取负号的数字的绝对值之和negS
  2. 推导公式:negS = (mx - target) / 2; 原理:原总和mx,每把一个数x变负,总和减2x,总共需要减mx-target,所以总负号数的和就是这个值的一半。
  3. 代入计算:n=3,mx=6,target=0 → negS=(6-0)/2=3。 结论:我们需要从1、2、3中选若干个数,让它们的和等于3,这些数最终要变成负数。

第三步:确定哪些数字取负号(保证字典序最小)

字典序最小的规则:左边的数字尽可能小(优先用大负数),右边的数字尽可能大。 因为负数 < 正数,所以要让数组字典序最小,必须左边优先放绝对值大的负数

操作规则:从最大的数字开始,依次尝试取负,直到选中的数字总和等于negS:

  1. 从n=3开始检查:3 ≤ 3(negS),选中3取负,剩余需要的和:3-3=0;
  2. 剩余需要的和为0,剩下的数字(2、1)都不取负,保持正数;
  3. 最终确定:只有数字3需要取负,1和2保持正数。

第四步:构造字典序最小的数组

按照「左边放负数,右边放正数,负数从大到小排,正数从小到大排」的规则填充数组:

  1. 数组长度为3,左指针从开头开始,右指针从末尾开始;
  2. 先把确定的负数(-3)放在最左侧(满足字典序最小);
  3. 剩下的正数(1、2)按从小到大的顺序,依次放在负数后面;
  4. 最终数组:[-3, 1, 2],和题目要求的输出完全一致。

时间复杂度与额外空间复杂度分析

1. 总时间复杂度

整个过程只做了一次从n到1的循环遍历,没有嵌套循环,也没有额外的递归/复杂操作。 循环次数等于n的值,因此时间复杂度为:O(n)。 (n最大为100000,O(n)的效率完全满足题目要求)

2. 总额外空间复杂度

我们只创建了一个结果数组(存储最终答案),没有使用其他额外的数据结构(如栈、队列、哈希表等),也没有递归调用栈。 结果数组的长度等于n,因此额外空间复杂度为:O(n)。 (如果不算结果数组的必要空间,额外辅助空间为O(1))


总结

  1. 解题分四大核心步骤:合法性判断 → 计算负号数字总和 → 选定负号数字 → 构造最小字典序数组;
  2. 时间复杂度:O(n)(线性遍历一次);
  3. 额外空间复杂度:O(n)(仅存储结果数组)。

Go完整代码如下:

package main

import (
	"fmt"
)

func lexSmallestNegatedPerm(n int, target int64) []int {
	t := int(target)
	mx := n * (n + 1) / 2
	if t > mx || -t > mx || (mx-t)%2 != 0 {
		return nil
	}
	negS := (mx - t) / 2 // 取负号的元素(的绝对值)之和

	ans := make([]int, n)
	l, r := 0, n-1
	// 从 1,2,...,n 中选一些数,元素和等于 negS
	// 为了让负数部分的字典序尽量小,从大往小选
	for x := n; x > 0; x-- {
		if negS >= x {
			negS -= x
			ans[l] = -x
			l++
		} else {
			// 大的正数填在末尾
			ans[r] = x
			r--
		}
	}
	return ans
}

func main() {
	n := 3
	target := int64(0)
	result := lexSmallestNegatedPerm(n, target)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

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

from typing import List, Optional

def lexSmallestNegatedPerm(n: int, target: int) -> Optional[List[int]]:
    t = target
    mx = n * (n + 1) // 2
    if t > mx or -t > mx or (mx - t) % 2 != 0:
        return None
    
    negS = (mx - t) // 2  # 取负号的元素(的绝对值)之和
    
    ans = [0] * n
    l, r = 0, n - 1
    
    # 从 1,2,...,n 中选一些数,元素和等于 negS
    # 为了让负数部分的字典序尽量小,从大往小选
    for x in range(n, 0, -1):
        if negS >= x:
            negS -= x
            ans[l] = -x
            l += 1
        else:
            # 大的正数填在末尾
            ans[r] = x
            r -= 1
    
    return ans

def main():
    n = 3
    target = 0
    result = lexSmallestNegatedPerm(n, target)
    print(result)

if __name__ == "__main__":
    main()

在这里插入图片描述

C++完整代码如下:

#include <iostream>
#include <vector>

std::vector<int>* lexSmallestNegatedPerm(int n, long long target) {
    int t = static_cast<int>(target);
    int mx = n * (n + 1) / 2;
    if (t > mx || -t > mx || (mx - t) % 2 != 0) {
        return nullptr;
    }

    int negS = (mx - t) / 2;  // 取负号的元素(的绝对值)之和

    std::vector<int>* ans = new std::vector<int>(n);
    int l = 0, r = n - 1;

    // 从 1,2,...,n 中选一些数,元素和等于 negS
    // 为了让负数部分的字典序尽量小,从大往小选
    for (int x = n; x > 0; x--) {
        if (negS >= x) {
            negS -= x;
            (*ans)[l] = -x;
            l++;
        } else {
            // 大的正数填在末尾
            (*ans)[r] = x;
            r--;
        }
    }

    return ans;
}

int main() {
    int n = 3;
    long long target = 0;
    std::vector<int>* result = lexSmallestNegatedPerm(n, target);

    if (result != nullptr) {
        std::cout << "[";
        for (size_t i = 0; i < result->size(); i++) {
            std::cout << (*result)[i];
            if (i < result->size() - 1) std::cout << " ";
        }
        std::cout << "]" << std::endl;
        delete result;  // 记得释放内存
    } else {
        std::cout << "nil" << std::endl;
    }

    return 0;
}

在这里插入图片描述