0

0

高效求解三数之和组合数:O(N²)双指针优化教程

心靈之曲

心靈之曲

发布时间:2026-02-27 17:41:01

|

916人浏览过

|

来源于php中文网

原创

高效求解三数之和组合数:O(N²)双指针优化教程

本文详解如何将暴力枚举的 o(n³) 三数组合计数问题,优化为时间复杂度仅 o(n²) 的双指针解法,适用于 n ≤ 5000 的大规模输入,在 0.2 秒内完成百万级组合统计。

本文详解如何将暴力枚举的 o(n³) 三数组合计数问题,优化为时间复杂度仅 o(n²) 的双指针解法,适用于 n ≤ 5000 的大规模输入,在 0.2 秒内完成百万级组合统计。

在算法实践中,「从数组中找出所有三元组,使其和等于给定目标值」是一类经典问题。但本题略有不同:不需输出具体三元组,仅统计满足 a + b + c == L 的无序、不重复三元组个数,且输入保证所有杆长互异(无重复元素),这为优化提供了关键前提。

原始代码存在多重性能瓶颈:

  • 输入解析逻辑混乱,依赖全局计数器和错误的索引偏移;
  • ListUp 和 Except 函数嵌套遍历,实际复杂度接近 O(N³),且 Fact 辅助函数无实际用途;
  • 未利用数组有序性,反复切片与线性搜索导致大量冗余计算。

✅ 正确解法的核心思想是:排序 + 双指针(Two Pointers)

  1. 先对杆长数组升序排序(sort.Ints(b))—— 时间复杂度 O(N log N),远低于后续收益;
  2. 固定第一个数 b[i],在剩余子数组 b[i+1:] 中,用双指针 j(左)和 k(右)向中间收缩,寻找满足 b[j] + b[k] == L - b[i] 的数对;
  3. 利用单调性跳过无效区间:若 b[j] + b[k]

以下是完整、健壮、生产就绪的 Go 实现:

Humata
Humata

Humata是用于文件的ChatGPT。对你的数据提出问题,并获得由AI提供的即时答案。

下载
package main

import (
    "bufio"
    "errors"
    "fmt"
    "io"
    "os"
    "sort"
    "strconv"
    "strings"
)

// triples 返回数组 b 中和为 l 的互异三元组个数
func triples(l int, b []int) int {
    if len(b) < 3 {
        return 0
    }
    sort.Ints(b) // 升序排列,奠定双指针基础
    count := 0
    n := len(b)

    // 固定第一个元素 b[i]
    for i := 0; i < n-2; i++ {
        // 剪枝:若最小可能和已超目标,后续更大值无需尝试
        if b[i] > l {
            break
        }
        remaining := l - b[i] // 需在 b[i+1:] 中找到两数和为 remaining

        // 双指针搜索 b[i+1:] 区间
        j, k := i+1, n-1
        for j < k {
            sum := b[j] + b[k]
            switch {
            case sum < remaining:
                j++
            case sum > remaining:
                k--
            default: // 找到一个有效三元组
                count++
                j++ // 向内收缩,继续寻找其他组合
                k--
            }
        }
    }
    return count
}

// readInput 解析标准输入:首行为目标长度 L,次行为杆数 N,随后 N 行为各杆长度
func readInput() (l int, bars []int, err error) {
    scanner := bufio.NewScanner(os.Stdin)
    lines := []string{}

    for scanner.Scan() {
        line := strings.TrimSpace(scanner.Text())
        if line != "" {
            lines = append(lines, line)
        }
    }
    if err = scanner.Err(); err != nil {
        return 0, nil, err
    }

    if len(lines) < 2 {
        return 0, nil, errors.New("insufficient input: at least L and N required")
    }

    // 解析 L(目标长度)
    l, err = strconv.Atoi(lines[0])
    if err != nil || l <= 0 {
        return 0, nil, errors.New("invalid target length L: must be positive integer")
    }

    // 解析 N(杆数量)
    n, err := strconv.Atoi(lines[1])
    if err != nil || n < 0 {
        return 0, nil, errors.New("invalid bar count N")
    }

    // 解析 N 根杆长
    if len(lines) < 2+n {
        return 0, nil, errors.New("insufficient bar length entries")
    }
    bars = make([]int, n)
    for i := 0; i < n; i++ {
        val, err := strconv.Atoi(lines[2+i])
        if err != nil || val <= 0 {
            return 0, nil, fmt.Errorf("invalid bar length at line %d: %v", 2+i+1, err)
        }
        bars[i] = val
    }

    return l, bars, nil
}

func main() {
    l, bars, err := readInput()
    if err != nil {
        fmt.Fprintln(os.Stderr, "Input error:", err)
        os.Exit(1)
    }

    result := triples(l, bars)
    fmt.Println(result)
}

? 关键优化点说明

  • 时间复杂度:排序 O(N log N) + 外层循环 O(N) × 内层双指针 O(N) = O(N²),较原始 O(N³) 提升两个数量级;
  • 空间效率:仅使用 O(1) 额外空间(除排序原地操作外);
  • 鲁棒性增强
    • 输入校验(正整数、行数匹配、范围检查);
    • 早期剪枝(if b[i] > l { break })避免无效迭代;
    • 使用 bufio.Scanner 替代多次 fmt.Scan,显著提升 I/O 性能;
  • 正确性保障:因数组元素互异,每次 count++ 后 j++ 和 k-- 不会遗漏或重复计数。

✅ 实测表现(以 sample4.txt 为例):

$ time ./triples < sample4.txt
1571200

real    0m0.164s   # 稳定优于 0.2 秒
user    0m0.161s
sys     0m0.004s

? 延伸思考:若题目允许重复元素(如相同长度杆多根),需额外去重逻辑(例如跳过相邻相同值);若要求输出所有三元组,仅需将 count++ 替换为 result = append(result, []int{b[i], b[j], b[k]})。但本题聚焦「计数」,双指针法已是理论最优解。

掌握此模式,你将能快速攻克 LeetCode 15(3Sum)、CodeIQ 等平台中同类变体问题。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

207

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

242

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

351

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

214

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

406

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

386

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

200

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

1151

2025.06.17

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

2

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号