LeetCode 982题解:位运算优化三元组计数问题
1. 问题背景与核心挑战今天遇到一道有趣的LeetCode题目编号982要求统计数组中满足特定条件的三元组数量。题目描述很简单给定一个整数数组nums返回满足nums[i] nums[j] nums[k] 0的三元组(i, j, k)的数量其中0 ≤ i, j, k nums.length。这个按位与操作的三元组问题看似直接实则暗藏玄机。当我第一次看到这个题目时脑海中立即浮现出几个关键疑问暴力解法的时间复杂度是多少在数据量较大时是否可行按位与运算有哪些特性可以利用来优化是否存在某种数学规律或位运算技巧可以降低计算复杂度经过一番探索我发现这个问题完美展示了位运算与算法优化的精妙结合。下面分享我的解题思路和最终实现的优化方案。2. 暴力解法分析与复杂度评估最直观的解法当然是三重循环暴力枚举public int countTriplets(int[] nums) { int count 0; int n nums.length; for (int i 0; i n; i) { for (int j 0; j n; j) { for (int k 0; k n; k) { if ((nums[i] nums[j] nums[k]) 0) { count; } } } } return count; }这个解法的时间复杂度是O(n³)当n1000时循环次数将达到10亿次显然无法在合理时间内完成。在LeetCode上测试时这个解法会直接超时。提示在实际面试中即使你能想到优化方案也应该先提出暴力解法并分析其复杂度这展示了你的系统性思维。3. 位运算特性与优化思路3.1 按位与运算的基本性质按位与()运算有几个重要特性任何数与0进行按位与运算结果都是0按位与具有结合律(a b) c a (b c)按位与的结果不会大于任一操作数这些性质提示我们可以利用中间结果进行优化避免重复计算。3.2 关键优化思路预计算两数组合观察到三元组的按位与可以拆分为两步先计算nums[i] nums[j]的所有可能结果然后检查这些结果与nums[k]的按位与是否为0这样我们可以将O(n³)的问题转化为O(n²) O(n²)的问题。具体步骤预计算所有nums[i] nums[j]的结果存储它们的频率对于每个预计算结果和每个nums[k]检查它们的按位与是否为0根据频率统计有效三元组数量4. 优化实现与代码解析基于上述思路下面是优化后的Java实现public int countTriplets(int[] nums) { int maxNum 1 16; // 题目中nums[i] 2^16 int[] freq new int[maxNum]; int n nums.length; // 预计算所有nums[i] nums[j]的频率 for (int i 0; i n; i) { for (int j 0; j n; j) { freq[nums[i] nums[j]]; } } int count 0; // 检查每个预计算结果与nums[k]的按位与 for (int k 0; k n; k) { for (int m 0; m maxNum; m) { if ((m nums[k]) 0) { count freq[m]; } } } return count; }4.1 复杂度分析空间复杂度O(2¹⁶)用于存储频率数组时间复杂度O(n² n*2¹⁶)预计算阶段O(n²)统计阶段O(n*2¹⁶)虽然理论复杂度仍然较高但在实际测试中这个解法能够通过LeetCode的所有测试用例因为2¹⁶65536是一个固定常数。5. 进一步优化位掩码技巧我们可以利用位运算的性质进一步优化内层循环public int countTriplets(int[] nums) { int maxNum 1 16; int[] freq new int[maxNum]; int n nums.length; for (int num : nums) { for (int num2 : nums) { freq[num num2]; } } int count 0; for (int num : nums) { int mask num ^ 0xFFFF; // 取反操作 int subset mask; do { count freq[subset]; subset (subset - 1) mask; } while (subset ! mask); } return count; }这个优化利用了位掩码的枚举技巧将内层循环从遍历所有可能的m改为只遍历与nums[k]按位与为0的那些m。这种方法在最坏情况下复杂度相同但在实际运行中通常更快。6. 边界条件与测试用例在实现这类位运算问题时特别需要注意边界条件空数组输入应该返回0单个元素数组如果元素为0返回1(0000)否则返回0全0数组任何三元组都满足条件返回n³全1数组只有所有元素按位与才为1不满足条件返回0测试用例示例Test public void testCountTriplets() { Solution solution new Solution(); assertEquals(12, solution.countTriplets(new int[]{2, 1, 3})); assertEquals(27, solution.countTriplets(new int[]{0, 0, 0})); assertEquals(0, solution.countTriplets(new int[]{1, 1, 1})); assertEquals(1, solution.countTriplets(new int[]{0})); assertEquals(0, solution.countTriplets(new int[]{1})); }7. 同类问题与扩展思考这类按位运算的组合计数问题在编程竞赛中很常见。类似的问题包括按位或为零的三元组计数按位异或为特定值的三元组计数子数组按位与/或/异或的统计解决这类问题的通用思路是分析位运算的性质寻找可以预计算的中间结果利用位掩码技巧优化枚举过程考虑分治或按位处理的策略对于更大的数据规模如n10⁵可能需要更高级的数据结构或数学方法如快速沃尔什变换(FWT)等。

相关新闻

深入解析有状态与无状态系统的核心差异与应用场景

深入解析有状态与无状态系统的核心差异与应用场景

1. 状态的概念与分类:从会话到存储的多维视角"有状态"和"无状态"这两个术语在不同技术文档中频繁出现,但很多人对其理解往往停留在表面。要真正掌握这一概念,首先需要明确我们讨论的是"什么的状态"。就像医生问…

2026/10/26 21:04:26 阅读更多 →
Ubuntu 22.04 上搭建高效 Android 开发环境:从 JDK 到 AVD 的完整指南

Ubuntu 22.04 上搭建高效 Android 开发环境:从 JDK 到 AVD 的完整指南

1. 项目概述:为什么要在Ubuntu上搞Android开发? 如果你是一名Android开发者,或者正准备踏入这个领域,可能第一反应是:用Windows或者macOS不香吗?确实,很多教程和默认的IDE(比如Andr…

2026/10/25 22:24:23 阅读更多 →
TCP与UDP协议对比:核心差异与工程实践指南

TCP与UDP协议对比:核心差异与工程实践指南

1. TCP与UDP的本质差异解析从事网络开发十五年来,我处理过无数因协议选择不当导致的性能问题。TCP和UDP这对"网络双生子"看似简单,实则藏着许多工程师容易忽略的细节差异。让我们抛开教科书定义,从实际工程角度重新审视这两个协议。…

2026/10/21 18:53:43 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/26 12:00:21 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/25 22:36:10 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/26 4:52:12 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/26 12:00:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/26 5:00:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/26 3:16:46 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 12:00:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 13:21:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 8:34:37 阅读更多 →