You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言中如何高效检测[][]int中的重复[]int切片?

Go语言中检测有序子集集合重复的高效方法

针对你需要检测有序[][]int中重复子集(遇到第一个重复即返回错误)的需求,下面提供两种符合Go惯用风格的高效实现方案,并分析各自的适用场景:

方案一:哈希表+唯一标识(推荐优先使用)

Go不允许直接将[]int作为map的键,但可以将有序子集转换为可哈希的唯一标识(字符串或哈希值),通过map记录已出现的子集,遍历过程中发现重复立即返回。

实现1:字符串编码(无碰撞风险)

利用字符串拼接将有序子集转为唯一字符串,避免哈希碰撞问题,实现简单直观:

import (
	"fmt"
	"strconv"
	"strings"
)

func check(setOfSubsets [][]int) error {
	seen := make(map[string]struct{})
	for _, subset := range setOfSubsets {
		var sb strings.Builder
		// 用分号作为分隔符,避免[12,3]和[1,23]这类歧义情况
		for i, num := range subset {
			if i > 0 {
				sb.WriteRune(';')
			}
			if _, err := sb.WriteString(strconv.Itoa(num)); err != nil {
				return fmt.Errorf("编码子集失败: %w", err)
			}
		}
		key := sb.String()
		if _, exists := seen[key]; exists {
			return fmt.Errorf("发现重复子集: %v", subset)
		}
		seen[key] = struct{}{}
	}
	return nil
}

实现2:哈希值+碰撞处理(性能更优)

通过哈希函数将子集转为uint64哈希值,减少内存占用并提升速度,同时处理极低概率的哈希碰撞:

import (
	"fmt"
	"hash/fnv"
)

// 计算有序子集的哈希值
func subsetHash(subset []int) uint64 {
	h := fnv.New64a()
	for _, num := range subset {
		// 将int转为固定8字节二进制,确保不同整数的字节表示唯一
		b := [8]byte{}
		for i := 0; i < 8; i++ {
			b[i] = byte(num >> (uint(i) * 8))
		}
		h.Write(b[:])
	}
	return h.Sum64()
}

// 对比两个子集是否完全相等
func equal(a, b []int) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func check(setOfSubsets [][]int) error {
	seen := make(map[uint64][][]int)
	for _, subset := range setOfSubsets {
		hashVal := subsetHash(subset)
		// 检查同哈希值的子集是否存在重复
		if existingSubsets, exists := seen[hashVal]; exists {
			for _, s := range existingSubsets {
				if equal(s, subset) {
					return fmt.Errorf("发现重复子集: %v", subset)
				}
			}
		}
		seen[hashVal] = append(seen[hashVal], subset)
	}
	return nil
}

方案二:排序后检查相邻元素

先对[][]int按自定义规则排序,再遍历检查相邻子集是否相等。此方法适合内存紧张但可接受排序开销的场景,但无法提前返回,必须完成排序后才能检测重复。

import (
	"fmt"
	"sort"
)

// 自定义子集排序规则:先比长度,长度相同则逐个元素比较
type subsetSlice [][]int

func (s subsetSlice) Len() int { return len(s) }
func (s subsetSlice) Less(i, j int) bool {
	a, b := s[i], s[j]
	minLen := len(a)
	if len(b) < minLen {
		minLen = len(b)
	}
	for k := 0; k < minLen; k++ {
		if a[k] != b[k] {
			return a[k] < b[k]
		}
	}
	// 元素全同时,长度短的排前面
	return len(a) < len(b)
}
func (s subsetSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func check(setOfSubsets [][]int) error {
	// 复制原切片避免修改输入数据
	subsets := make([][]int, len(setOfSubsets))
	copy(subsets, setOfSubsets)
	sort.Sort(subsetSlice(subsets))

	for i := 1; i < len(subsets); i++ {
		if equal(subsets[i-1], subsets[i]) {
			return fmt.Errorf("发现重复子集: %v", subsets[i])
		}
	}
	return nil
}

func equal(a, b []int) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

方案对比

方案类型时间复杂度空间复杂度优势劣势
字符串编码哈希表O(n*m)O(n)无碰撞风险,实现简单,可提前返回重复长子集拼接字符串开销大
哈希值+碰撞处理哈希表O(n*m)(平均)O(n)性能更高,内存占用低,可提前返回重复需处理哈希碰撞,代码稍复杂
排序后检查相邻元素O(n log n * m)O(n)(复制)/ O(log n)(原地)内存占用低(原地排序时)无法提前返回,排序开销大

根据你的需求(检测到第一个重复即返回),优先选择哈希表类方案,其中哈希值+碰撞处理的版本更适合百万级子集的高性能场景。

内容的提问来源于stack exchange,提问作者snow_abstraction

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:04:51