2026-02-23:交换元素后的最大交替和。用go语言,给定一个整数数组 nums,定义其交替和为下标偶数位置元素之和减去奇数

举报
福大大架构师每日一题 发表于 2026/02/23 08:00:08 2026/02/23
【摘要】 2026-02-23:交换元素后的最大交替和。用go语言,给定一个整数数组 nums,定义其交替和为下标偶数位置元素之和减去奇数位置元素之和(即 nums[0] - nums[1] + nums[2] - …)。另有一组下标对 swaps,其中每个 [p, q] 表示你可以任意次交换位置 p 和 q 上的元素。因为交换可以重复进行,这意味着在由这些交换边构成的图的每个连通分量内,数组元素可以...

2026-02-23:交换元素后的最大交替和。用go语言,给定一个整数数组 nums,定义其交替和为下标偶数位置元素之和减去奇数位置元素之和(即 nums[0] - nums[1] + nums[2] - …)。

另有一组下标对 swaps,其中每个 [p, q] 表示你可以任意次交换位置 p 和 q 上的元素。因为交换可以重复进行,这意味着在由这些交换边构成的图的每个连通分量内,数组元素可以任意重排到这些位置上。

在允许按 swaps 规则进行任意交换的情况下,问能够得到的最大交替和是多少?请返回该最大值。

2 <= nums.length <= 100000。

1 <= nums[i] <= 1000000000。

0 <= swaps.length <= 100000。

swaps[i] = [pi, qi]。

0 <= pi < qi <= nums.length - 1。

[pi, qi] != [pj, qj]。

输入:nums = [1,2,3], swaps = [[0,2],[1,2]]。

输出:4。

解释:

当 nums 为 [2, 1, 3] 或 [3, 1, 2] 时,可以实现最大交替和。例如,你可以通过以下方式得到 nums = [2, 1, 3]。

交换 nums[0] 和 nums[2]。此时 nums 为 [3, 2, 1]。

交换 nums[1] 和 nums[2]。此时 nums 为 [3, 1, 2]。

交换 nums[0] 和 nums[2]。此时 nums 为 [2, 1, 3]。

题目来自力扣3695。

解题思路

这是一个基于并查集贪心策略的问题。

1. 理解交换规则

  • 给定一组下标对 [p, q],可以任意次交换位置 p 和 q 的元素
  • 这种交换关系会形成图的连通分量,在每个连通分量内,元素可以任意排列
  • 也就是说,我们可以将每个连通分量内的元素重新分配到该分量中的任意位置上

2. 交替和的定义

  • 交替和 = 偶数下标元素之和 - 奇数下标元素之和
  • 偶数下标位置在计算中取正号,奇数下标位置取负号

3. 贪心策略

为了使交替和最大:

  • 尽量把大的数放在偶数下标(取正号的位置)
  • 尽量把小的数放在奇数下标(取负号的位置)

4. 解题步骤

第一步:构建连通关系

  • 使用并查集将所有可以交换的位置合并到同一个集合中
  • 每个集合代表一个可以自由排列元素的连通分量

第二步:统计每个分量的奇数位置数量

  • 在每个连通分量中,需要知道该分量包含多少个奇数下标
  • 这些奇数位置在最终排列中应该放最小的几个数(因为取负号)

第三步:分组排序

  • 将原始数组中的元素按照它们所属的连通分量分组
  • 对每组内的元素进行升序排序

第四步:分配元素

  • 对于每个连通分量:
    • 设该分量有 k 个奇数位置
    • 将排序后的数组中最小的 k 个数分配到奇数位置(取负号)
    • 剩余的数分配到偶数位置(取正号)
    • 这样能最大化该分量的贡献

第五步:计算结果

  • 将所有分量的贡献相加得到最终的最大交替和

算法复杂度

时间复杂度:O(n log n)

  • 并查集操作近似 O(n)
  • 对每个分组的排序,总体排序复杂度 O(n log n)
  • 遍历数组求和 O(n)

额外空间复杂度:O(n)

  • 并查集数组 O(n)
  • 分组存储数组 O(n)

Go完整代码如下:

package main

import (
	"fmt"
	"slices"
)

type unionFind struct {
	fa  []int // 代表元
	odd []int // 集合中的奇数个数
}

func newUnionFind(n int) unionFind {
	fa := make([]int, n)
	odd := make([]int, n)
	// 一开始有 n 个集合 {0}, {1}, ..., {n-1}
	// 集合 i 的代表元是自己
	for i := range fa {
		fa[i] = i
		odd[i] = i % 2
	}
	return unionFind{fa, odd}
}

// 返回 x 所在集合的代表元
// 同时做路径压缩,也就是把 x 所在集合中的所有元素的 fa 都改成代表元
func (u unionFind) find(x int) int {
	// 如果 fa[x] == x,则表示 x 是代表元
	if u.fa[x] != x {
		u.fa[x] = u.find(u.fa[x]) // fa 改成代表元
	}
	return u.fa[x]
}

// 把 from 所在集合合并到 to 所在集合中
func (u *unionFind) merge(from, to int) {
	x, y := u.find(from), u.find(to)
	if x == y { // from 和 to 在同一个集合,不做合并
		return
	}
	u.fa[x] = y          // 合并集合
	u.odd[y] += u.odd[x] // 更新集合中的奇数个数
}

func maxAlternatingSum(nums []int, swaps [][]int) (ans int64) {
	n := len(nums)
	uf := newUnionFind(n)
	for _, p := range swaps {
		uf.merge(p[0], p[1])
	}

	g := make([][]int, n)
	for i, x := range nums {
		f := uf.find(i)
		g[f] = append(g[f], x) // 相同集合的元素分到同一组
	}

	for i, a := range g {
		if a == nil {
			continue
		}
		slices.Sort(a)
		odd := uf.odd[i]
		// 小的取负号,大的取正号
		for j, x := range a {
			if j < odd {
				ans -= int64(x)
			} else {
				ans += int64(x)
			}
		}
	}
	return
}

func main() {
	nums := []int{1, 2, 3}
	swaps := [][]int{{0, 2}, {1, 2}}
	result := maxAlternatingSum(nums, swaps)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

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

from typing import List

class UnionFind:
    def __init__(self, n: int):
        # 代表元
        self.fa = list(range(n))
        # 集合中的奇数个数
        self.odd = [i % 2 for i in range(n)]
    
    def find(self, x: int) -> int:
        """返回 x 所在集合的代表元,同时做路径压缩"""
        if self.fa[x] != x:
            self.fa[x] = self.find(self.fa[x])
        return self.fa[x]
    
    def merge(self, from_node: int, to: int) -> None:
        """把 from_node 所在集合合并到 to 所在集合中"""
        x, y = self.find(from_node), self.find(to)
        if x == y:  # 已经在同一个集合,不做合并
            return
        self.fa[x] = y  # 合并集合
        self.odd[y] += self.odd[x]  # 更新集合中的奇数个数

class Solution:
    def maxAlternatingSum(self, nums: List[int], swaps: List[List[int]]) -> int:
        n = len(nums)
        uf = UnionFind(n)
        
        # 合并可以交换的位置
        for p in swaps:
            uf.merge(p[0], p[1])
        
        # 将相同集合的元素分到同一组
        groups = [[] for _ in range(n)]
        for i, x in enumerate(nums):
            f = uf.find(i)
            groups[f].append(x)
        
        ans = 0
        for i, group in enumerate(groups):
            if not group:  # 跳过空组
                continue
            group.sort()
            odd_count = uf.odd[i]
            # 小的取负号,大的取正号
            for j, x in enumerate(group):
                if j < odd_count:
                    ans -= x
                else:
                    ans += x
        
        return ans

def main():
    nums = [1, 2, 3]
    swaps = [[0, 2], [1, 2]]
    solution = Solution()
    result = solution.maxAlternatingSum(nums, swaps)
    print(result)

if __name__ == "__main__":
    main()

在这里插入图片描述

C++完整代码如下:

#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;

class UnionFind {
public:
    vector<int> fa;   // 代表元
    vector<int> odd;  // 集合中的奇数个数

    UnionFind(int n) {
        fa.resize(n);
        odd.resize(n);
        // 一开始有 n 个集合 {0}, {1}, ..., {n-1}
        // 集合 i 的代表元是自己
        for (int i = 0; i < n; i++) {
            fa[i] = i;
            odd[i] = i % 2;
        }
    }

    // 返回 x 所在集合的代表元
    // 同时做路径压缩,也就是把 x 所在集合中的所有元素的 fa 都改成代表元
    int find(int x) {
        // 如果 fa[x] == x,则表示 x 是代表元
        if (fa[x] != x) {
            fa[x] = find(fa[x]); // fa 改成代表元
        }
        return fa[x];
    }

    // 把 from 所在集合合并到 to 所在集合中
    void merge(int from, int to) {
        int x = find(from), y = find(to);
        if (x == y) { // from 和 to 在同一个集合,不做合并
            return;
        }
        fa[x] = y;          // 合并集合
        odd[y] += odd[x];   // 更新集合中的奇数个数
    }
};

class Solution {
public:
    long long maxAlternatingSum(vector<int>& nums, vector<vector<int>>& swaps) {
        int n = nums.size();
        UnionFind uf(n);

        // 合并可以交换的位置
        for (const auto& p : swaps) {
            uf.merge(p[0], p[1]);
        }

        // 将相同集合的元素分到同一组
        vector<vector<int>> groups(n);
        for (int i = 0; i < n; i++) {
            int f = uf.find(i);
            groups[f].push_back(nums[i]);
        }

        long long ans = 0;
        for (int i = 0; i < n; i++) {
            if (groups[i].empty()) {
                continue;
            }
            sort(groups[i].begin(), groups[i].end());
            int odd_count = uf.odd[i];
            // 小的取负号,大的取正号
            for (int j = 0; j < groups[i].size(); j++) {
                if (j < odd_count) {
                    ans -= groups[i][j];
                } else {
                    ans += groups[i][j];
                }
            }
        }

        return ans;
    }
};

int main() {
    vector<int> nums = {1, 2, 3};
    vector<vector<int>> swaps = {{0, 2}, {1, 2}};
    Solution solution;
    long long result = solution.maxAlternatingSum(nums, swaps);
    cout << result << endl;
    return 0;
}

在这里插入图片描述

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。