1734. Decode XORed Permutation:利用 XOR 自反性还原奇数长度排列(LeetCode-Go 解法全解)
2026/9/13 14:27:37 网站建设 项目流程

1734. Decode XORed Permutation:利用 XOR 自反性还原奇数长度排列(LeetCode-Go 解法全解)

【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

导读

本题(1734. Decode XORed Permutation 仓库中的实现为主线,从 XOR 的三个基本性质出发,推导出如何借助"全集异或"与"奇数位异或"先锁定perm[0],再沿编码链逐位还原整个数组,并给出复杂度分析与可运行的测试验证,帮助读者掌握一类"已知相邻差分还原序列"的位运算通解。


一、题目回顾与核心难点

1.1 题意重述

有一个整数数组perm,它是前n个正整数的排列(即1, 2, ..., n各出现一次),且n恒为奇数

它被编码为长度为n - 1的数组encoded,编码规则为:

encoded[i] = perm[i] XOR perm[i + 1]

例如perm = [1, 3, 2]时,encoded = [1 XOR 3, 3 XOR 2] = [2, 1]

题目保证答案存在且唯一,给定encoded,要求还原出原始数组perm

1.2 约束条件

  • 3 <= n < 10^5
  • n是奇数
  • encoded.length == n - 1

1.3 示例

示例 1:

Input: encoded = [3,1] Output: [1,2,3]

验证:perm = [1,2,3]时,encoded = [1 XOR 2, 2 XOR 3] = [3,1]

示例 2:

Input: encoded = [6,5,4,6] Output: [2,4,1,5,3]

1.4 核心难点

表面上看,n - 1个方程、n个未知数,似乎信息不足。真正让题目可解的关键有两点:

  1. 排列的封闭性perm是前n个正整数的排列,意味着perm[0] XOR perm[1] XOR ... XOR perm[n-1]等于1 XOR 2 XOR ... XOR n,这个值不需要知道排列顺序也能直接算出来;
  2. n 为奇数的结构性保证n是奇数,才能让"编码数组奇数下标的异或"恰好覆盖perm[1]perm[n-1]的全部元素,从而构造出total ^ odd消去冗余、单点定位perm[0]

二、算法基石:XOR 的三大性质

本题整个解题过程只依赖异或运算的三条性质:

性质表达式说明
自反性x XOR x = 0相同值异或结果为 0
恒等性x XOR 0 = x与 0 异或保持不变
交换律与结合律a XOR b = b XOR a(a XOR b) XOR c = a XOR (b XOR c)异或顺序不影响结果

其中自反性是本题的核心武器:任何出现偶数次的元素会在整体异或中互相抵消。这与仓库中 136. Single Number 的解法 一脉相承——第 136 题正是利用result ^= nums[i]让成对出现的数字全部抵消、留下唯一落单者。原文档解题思路部分也明确指出:"这一题与第 136 题和第 137 题思路类似,借用x ^ x = 0这个性质解题。"


三、推导过程:如何锁定 perm[0]

3.1 第一步:计算全集异或 total

由于perm1 ~ n的排列,无论顺序如何,全部元素异或的结果恒等于:

total = 1 XOR 2 XOR 3 XOR ... XOR n

这一步不需要知道排列,只需要知道n,时间复杂度为 O(n)。

3.2 第二步:计算奇数下标异或 odd

考察encoded奇数下标的元素:

odd = encoded[1] XOR encoded[3] XOR ... XOR encoded[n-2]

由于encoded[i] = perm[i] XOR perm[i+1],把上式展开:

odd = (perm[1] XOR perm[2]) XOR (perm[3] XOR perm[4]) XOR ... XOR (perm[n-2] XOR perm[n-1])

注意:n是奇数,所以encoded的长度n - 1是偶数,其奇数下标取值为1, 3, ..., n-2,恰好覆盖了perm[1]perm[n-1]的全部元素。这就是题目保证 n 为奇数的意义所在——若不是奇数,这一步就无法精确覆盖"除 perm[0] 外的所有元素"。

3.3 第三步:total XOR odd 得到 perm[0]

将 total 与 odd 异或:

total XOR odd = (perm[0] XOR perm[1] XOR ... XOR perm[n-1]) XOR (perm[1] XOR perm[2] XOR ... XOR perm[n-1]) = perm[0] // 其余元素成对出现,经 x XOR x = 0 全部抵消

perm[1] ~ perm[n-1]在两个集合中各出现一次,异或后相互抵消归零,只剩下perm[0]。由此成功"锚定"第一个原始元素。

3.4 第四步:沿编码链递推还原全部元素

拿到perm[0]后,问题就变成了简单的链式递推。因为encoded[i] = perm[i] XOR perm[i+1],两边同时异或perm[i]

perm[i+1] = perm[i] XOR encoded[i]

逐层推导:

encoded[0] = perm[0] XOR perm[1] perm[0] XOR encoded[0] = perm[0] XOR (perm[0] XOR perm[1]) = perm[1] perm[1] XOR encoded[1] = perm[1] XOR (perm[1] XOR perm[2]) = perm[2] ... perm[n-2] XOR encoded[n-2] = perm[n-1]

依次类推,即可还原出原数组perm中的所有数。


四、仓库源码逐行剖析

4.1 核心实现

原文档给出的解法与仓库中的实际源码完全一致,位于 1734. Decode XORed Permutation.go:

package leetcode func decode(encoded []int) []int { n, total, odd := len(encoded), 0, 0 for i := 1; i <= n+1; i++ { total ^= i } for i := 1; i < n; i += 2 { odd ^= encoded[i] } perm := make([]int, n+1) perm[0] = total ^ odd for i, v := range encoded { perm[i+1] = perm[i] ^ v } return perm }

逐段解读:

代码段作用细节说明
n, total, odd := len(encoded), 0, 0初始化n即原始数组长度len(perm),因为encoded长度为n-1,所以perm长度为n+1
for i := 1; i <= n+1; i++ { total ^= i }计算全集异或total = 1 XOR 2 XOR ... XOR (n+1),对应原始文档中的[1, n+1]区间
for i := 1; i < n; i += 2 { odd ^= encoded[i] }计算奇数下标异或步长为 2,遍历encoded[1], encoded[3], ...
perm := make([]int, n+1)分配结果数组长度为n+1,恰好等于len(encoded) + 1
perm[0] = total ^ odd锚定首元素见上文 3.3 节推导
for i, v := range encoded { perm[i+1] = perm[i] ^ v }链式递推利用perm[i+1] = perm[i] XOR encoded[i]逐个还原
return perm返回结果直接返回,无需额外处理

4.2 边界情况与正确性说明

  • n 最小为 3:当n = 3时,encoded长度为 2,total = 1 XOR 2 XOR 3 = 0odd = encoded[1]perm[0] = 0 XOR encoded[1] = encoded[1],随后perm[1] = perm[0] XOR encoded[0]perm[2] = perm[1] XOR encoded[1],逻辑依然成立;
  • 答案唯一性:题目保证答案存在且唯一,因此在3 <= n < 10^5的约束下,上述推导不会出现歧义;
  • 无需排序:整个算法不依赖对perm排序,仅靠位运算性质完成还原,这是相对朴素做法的最大优势。

五、测试用例与运行验证

仓库为本题配套了完整的单元测试,位于 1734. Decode XORed Permutation_test.go,覆盖了题目给出的两个示例:

package leetcode import ( "fmt" "testing" ) type question1734 struct { para1734 ans1734 } // para 是参数 // one 代表第一个参数 type para1734 struct { encoded []int } // ans 是答案 // one 代表第一个答案 type ans1734 struct { one []int } func Test_Problem1734(t *testing.T) { qs := []question1734{ { para1734{[]int{3, 1}}, ans1734{[]int{1, 2, 3}}, }, { para1734{[]int{6, 5, 4, 6}}, ans1734{[]int{2, 4, 1, 5, 3}}, }, } fmt.Printf("------------------------Leetcode Problem 1734------------------------\n") for _, q := range qs { _, p := q.ans1734, q.para1734 fmt.Printf("【input】:%v 【output】:%v\n", p, decode(p.encoded)) } fmt.Printf("\n\n\n") }

测试用例逐一手动推演验证:

用例一:encoded = [3, 1],期望[1, 2, 3]

  • n = 2(encoded 长度),perm长度= 3
  • total = 1 XOR 2 XOR 3 = 0
  • odd = encoded[1] = 1
  • perm[0] = 0 XOR 1 = 1
  • perm[1] = 1 XOR 3 = 2perm[2] = 2 XOR 1 = 3
  • 得到[1, 2, 3],正确。

用例二:encoded = [6, 5, 4, 6],期望[2, 4, 1, 5, 3]

  • n = 4perm长度= 5
  • total = 1 XOR 2 XOR 3 XOR 4 XOR 5 = 1
  • odd = encoded[1] XOR encoded[3] = 5 XOR 6 = 3
  • perm[0] = 1 XOR 3 = 2
  • 递推:perm[1] = 2 XOR 6 = 4perm[2] = 4 XOR 5 = 1perm[3] = 1 XOR 4 = 5perm[4] = 5 XOR 6 = 3
  • 得到[2, 4, 1, 5, 3],正确。

读者可以在仓库根目录通过go test ./leetcode/1734.Decode-XORed-Permutation/运行该测试,验证实现与期望输出一致。


六、复杂度分析与正确性论证

6.1 时间复杂度

整个算法共有四段线性遍历:

  1. 计算total:O(n);
  2. 计算odd:O(n/2);
  3. 递推还原perm:O(n)。

总时间复杂度为O(n),在n < 10^5的约束下非常高效。与 LeetCode-Go 项目对单题解法"runtime beats 100%"的目标定位一致(具体性能表现以实际评测为准,本文不引用未经证实的性能数据)。

6.2 空间复杂度

只额外分配了结果数组perm,长度为n + 1,辅助变量为常数个,因此空间复杂度为O(n)(结果数组本身不计入辅助空间时为 O(1) 额外空间,视 LeetCode 判题约定而定)。

6.3 为什么 n 必须是奇数——一个反例推演

为帮助读者理解题目条件,这里做一个反例推演。假设n = 4(偶数),则encoded长度为 3,奇数下标只有encoded[1]

odd = perm[1] XOR perm[2]

此时odd只覆盖了perm[1], perm[2],并未覆盖perm[3]。计算total XOR odd

(perm[0] XOR perm[1] XOR perm[2] XOR perm[3]) XOR (perm[1] XOR perm[2]) = perm[0] XOR perm[3]

得到的不是单一元素,无法锚定perm[0]。这就是题目强制n为奇数的根本原因:只有奇数长度才能保证"奇数下标编码"与"去掉 perm[0] 的剩余元素集合"一一对应。


七、同源思路:LeetCode-Go 中的 XOR 系列问题

本题的核心思想——"用整体异或抵消成对元素"——在 LeetCode-Go 仓库中是一个反复出现的模式,可以串联学习:

题号题目与本题的关系
136. Single Number找出唯一落单元素最基础的x XOR x = 0应用:全部异或即得答案
137. Single Number II找出只出现一次的元素(其余出现 3 次)在自反性基础上引入按位统计的进阶变形
1734. Decode XORed Permutation还原 XOR 相邻差分排列在全集异或基础上,进一步利用"奇数下标编码覆盖剩余集合"构造锚点

原文档解题思路明确指出本题与第 136、137 题思路类似。第 136 题的源码(136. Single Number.go)只用了一个循环:

func singleNumber(nums []int) int { result := 0 for i := 0; i < len(nums); i++ { result ^= nums[i] } return result }

对比可见:第 136 题是"全集异或"的零成本应用(因为目标元素天然唯一);而第 1734 题由于需要还原整个序列,必须在全集异或之外再构造一个"剔除锚点元素"的集合,这正是odd存在的意义。理解这一层递进关系,有助于在面试中面对"差分 + 排列"类问题时快速定位解题方向。


八、方法总结与扩展思考

8.1 解题方法论沉淀

  1. 遇到"相邻差分"类还原问题,先检查是否具备"全集封闭"特性(如本题的排列性质),若有则可直接计算出全集异或;
  2. 利用奇偶位置构造子集,使子集与"全集去掉某个元素"形成精确互补,从而用total XOR subset单点定位;
  3. 锚定一个元素后,其余元素可沿差分链线性递推,无需任何比较或排序操作;
  4. 整个过程可以总结为一句口诀:"全集异或除锚点,锚点异或差分还原"

8.2 变体与扩展

  • 若题目改为给定encodedperm[0]的值,则totalodd都不再需要,直接沿链递推即可,问题降级为纯 O(n) 遍历;
  • perm不是排列而是任意数组,则缺少"全集封闭"条件,本题的 O(n) 解法不再成立,需要其他约束;
  • 位运算解法的可读性依赖对 XOR 性质的熟悉程度,在代码评审或面试讲解中,建议先口述"成对抵消"的直观含义,再展示代码。

8.3 在 LeetCode-Go 仓库中阅读本题的路径

  • 题目文档:leetcode/1734.Decode-XORed-Permutation/README.md
  • 核心实现:1734. Decode XORed Permutation.go
  • 单元测试:1734. Decode XORed Permutation_test.go
  • 关联基础题:136. Single Number、137. Single Number II

结语

1734 题是"位运算 + 排列结构"结合的经典题目:n为奇数的条件、total ^ odd的锚点构造、以及沿编码链的线性递推,三步环环相扣,最终得到一个 O(n) 时间、O(n) 空间的优雅解法。掌握本题,不仅能应对同类"相邻 XOR 差分还原"问题,更能加深对异或自反性在算法设计中应用的理解——这也是 LeetCode-Go 仓库将其作为 XOR 系列重要一环的价值所在。

【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询