ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

用 XOR 双指针反转链表:原理剖析与 C++ 源码实现(Cosmos 仓库实战)

用 XOR 双指针反转链表:原理剖析与 C++ 源码实现(Cosmos 仓库实战) 用 XOR 双指针反转链表原理剖析与 C 源码实现Cosmos 仓库实战【免费下载链接】cosmosWorlds largest Contributor driven code dataset | Used in Quark Search Engine, OpenGenus IQ, OpenGenus Visual Project项目地址: https://gitcode.com/gh_mirrors/co/cosmos导读本文围绕 Cosmos 仓库 code/languages/cpp/reverse_linked_list/README.md 所讲解的核心技巧展开如何仅用2 个指针借助XOR异或位运算完成单链表反转。相比传统的 3 指针法XOR 技巧用异或的数学性质替代了一个额外指针把内存占用与循环体进一步压缩到极致。读完本文你将掌握链接反转link reversal与值交换的本质区别、3 指针基线的完整思路、XOR 双指针的逐步推导、uintptr_t指针强转的底层原因以及仓库中对应 C 源码的逐行解读与编译运行方法。一、核心思想反转链接而不是值原文档开篇即点明一个容易混淆的关键前提我们通过**链接反转link reversal**来反转给定的链表而不是通过交换链表节点的值。这两种思路的差异在于值交换swap values保持节点对象与链接结构不变仅把data字段在两两节点间搬运。它不改变链的拓扑只是数据搬家。链接反转link reversal节点与数据都不动只把每个节点的next指针指向前驱节点最终让整条链的走向倒转头指针更新为原链表的尾节点。之所以强调这一点是因为链接反转才是所有高效反转算法3 指针、递归、XOR 双指针的共同基础也决定了最终复杂度能做到O(n) 时间、O(1) 额外空间。若采用值交换则无法在常数空间内完成且对链表这样指针即结构的数据结构而言语义上并不优雅。在 reverse_linked_list_2pointers.cpp 中可以看到核心循环结束后还有一句至关重要的收尾hptr prev; // updating the head pointer这印证了上述思想反转完成后原来的尾节点循环结束时prev停留的位置成为新的头节点必须更新全局头指针hptr否则后续遍历如print()会从错误的起点出发。二、链表节点结构与代码骨架两个实现双指针与三指针共用了同一套节点定义与插入逻辑定义在 reverse_linked_list_2pointers.cpp#include cstdlib #include iostream typedef uintptr_t ut; // 为指针位运算准备的整数别名 struct node { int data; struct node *nptr; // next 指针 }; struct node *hptr NULL; // 全局头指针 void insertNode(int pos, int x) { struct node *temp new node; if (temp NULL) std::cout Insert not possible\n; temp-data x; if (pos 1) { temp-nptr hptr; // 头插 hptr temp; } else { int i 1; struct node *thptr hptr; while (i pos - 1) { // 找到第 pos-1 个节点 thptr thptr-nptr; i; } temp-nptr thptr-nptr; // 中间插入 thptr-nptr temp; } }要点说明pos 1走头插分支pos 1走中间/尾插分支insertNode(2, 20)会把20接在10之后因此main()中依次insertNode(1,10) … insertNode(5,50)构造出的链表为10 - 20 - 30 - 40 - 50。typedef uintptr_t ut;是后续 XOR 技巧的伏笔C/C 不允许对指针直接做位运算必须先把指针强制转换成整数类型uintptr_t再异或详见第四节。值得一提的是仓库的 C 编码风格指南 强调使用using而非typedef、避免#include bits/stdc.h、缩进使用 4 空格等约定。本文两个示例为保持与文档一致的经典写法使用了typedef与 C 风格结构体读者在工程化改写时可参考风格指南统一风格另外uintptr_t在标准中定义于cstdint跨平台工程中建议显式包含该头文件以保证可移植性。三、基线方法3 指针反转经典教科书解法原文档明确说明常见的反转链表技术涉及 3 个指针仓库提供了对应的完整实现 reverse_linked_list_3pointers.cppvoid reverseList() { struct node *current hptr; struct node *next; struct node *prev NULL; // link reversal while (current ! NULL) { next current-nptr; // 1. 先保存后继防止断链 current-nptr prev; // 2. 反转当前节点的链接 prev current; // 3. prev 前移 current next; // 4. current 前移 } // updating the head pointer after link reversal hptr prev; }3.1 为什么必须先保存后继单链表节点只持有下一个节点的地址。当执行current-nptr prev后current原本的后继就再也找不到了因此必须提前用next指针把后继保存下来。这就是 3 指针法需要current、prev、next三个指针的根本原因——每一个指针都承担一个不可省略的职责。3.2 迭代过程演示对10 - 20 - 30 - 40 - 50每轮循环状态如下轮次currentprev执行效果110NULL10-nptr 置 NULL10 成为新尾2201020-nptr 置 103302030-nptr 置 204403040-nptr 置 305504050-nptr 置 40退出NULL50hptr prev新头为 50最终输出50 40 30 20 10链接完全倒转。复杂度为O(n) 时间、O(1) 额外空间仅 3 个局部指针。四、进阶技巧XOR 双指针反转原文档的核心论点是利用 XOR 运算的性质可以把 3 指针压缩为 2 指针从而消除对额外指针的需求。仓库实现见 reverse_linked_list_2pointers.cppvoid reverseList() { struct node *current hptr; struct node *prev NULL; while (current ! NULL) { current (struct node *)((ut)prev ^ (ut)current ^ (ut)(current-nptr) ^ (ut)(current-nptr prev) ^ (ut)(prev current)); // link reversal } hptr prev; // updating the head pointer }4.1 数学基础XOR 的自反性质XOR 位运算满足两个关键性质自反性a ^ a 0可逆性a ^ b ^ b a。由此可推导出用异或恢复原值的模式x ^ y ^ z中只要知道其中任意两个就能还原第三个。这正是经典XOR 交换变量技巧的原理也是本算法消灭第三个指针的理论根基。4.2 逐项拆解核心表达式假设进入循环某轮时prev P前驱、current C当前节点、current-nptr N原始后继。则赋值语句current (P) ^ (C) ^ (N) ^ (current-nptr P) ^ (prev C)中的五项分别为项值作用(ut)prevP前驱地址(ut)currentC当前地址(ut)(current-nptr)N原始后继地址(ut)(current-nptr prev)P副作用把当前节点的链接反转为前驱(ut)(prev current)C副作用prev 前进到当前节点计算P ^ C ^ N ^ P ^ C根据自反性P ^ P 0、C ^ C 0结果恰为N——也就是当前节点的原始后继地址。于是表达式整体求值结果 原始后继N赋给current等价于current next两条赋值副作用分别完成了current-nptr prev反转链接与prev currentprev 前进。一次表达式求值同时完成了 3 指针法循环体内的三个动作只用prev和current两个指针就推进了整个循环。循环退出时current NULLprev停留在原链表尾节点hptr prev完成换头。4.3 为什么不直接对指针做异或原文档特别强调了一个工程细节对于双指针技术我们需要将指针强制转换为uintptr_t类型然后执行位运算此处为异或运算因为无法直接对指针执行位运算。原因有两点语言层面的禁止C/C 标准规定指针仅支持加减、比较、解引用等有限操作^、、|等位运算符对指针类型未定义大多数编译器会直接报错或产生不可移植行为。平台相关性的消除uintptr_t是cstdint代码中经cstdlib引入定义的无符号整数类型其宽度恰好足以容纳任意指针即sizeof(uintptr_t) sizeof(void*)且按位运算结果与指针二进制表示一致。将指针转为uintptr_t再异或既合法又可跨平台复现相同结果。代码中typedef uintptr_t ut;正是为了缩短强制转换的书写长度让核心表达式保持紧凑可读。五、复杂度对比与适用边界方法时间额外空间指针数可读性值交换—需额外存储或多次遍历O(1)语义不符不适用于指针结构3 指针法O(n)O(1)3★★★XOR 双指针法O(n)O(1)2★★表达式较隐晦双指针法在渐进复杂度上并不优于三指针法——两者都是 O(n)/O(1)。它的价值在于省掉一个指针变量体现了用位运算性质换取更少状态的极致优化思路适合对内存占用极其敏感的场景如嵌入式环境也常被用作面试中考察位运算功底的高阶追问。需要诚实指出的限制从源码结构可推断可读性与维护成本把三句话压缩进一个含多条副作用的表达式正确性依赖对 XOR 性质的深刻理解后续维护者容易误读若评价顺序被调整行为会改变。工程实践中若追求清晰仍推荐三指针写法。适用前提算法假设链表节点地址值不重复且允许按整数位运算uintptr_t的取值可逆对空链表hptr NULL循环体直接跳过hptr prev NULL行为正确无需特判。示例中insertNode对new失败只打印提示而继续使用temp属演示代码的简化处理工程实现应检查分配结果。六、仓库中的关联实现与扩展阅读为便于读者在仓库内横向对比同一主题的多语言、多变体实现以下文件与本主题直接相关code/languages/cpp/reverse_linked_list/reverse_linked_list_2pointers.cppXOR 双指针反转本文主解读对象code/languages/cpp/reverse_linked_list/reverse_linked_list_3pointers.cpp三指针基线实现code/data_structures/src/Linked_List/reversing_linkedlist.cpp数据结构模块中以面向对象方式LL类封装的三指针反转展示了Node(int)构造器与push头插等工程化写法code/data_structures/src/Linked_List/reverse_linked_list_in_k_groups.cpp反转每 K 个节点的进阶变体循环体内同样使用prev/curr/temp三指针分组反转可作为三指针模式的实战延伸对照阅读code/data_structures/src/Linked_List/creating_linked_list.cpp、traverse_a_linked_list.cpp链表创建与遍历的配套实现便于构造自己的测试数据guides/coding_style/c/README.md仓库的 C 编码规范涵盖命名、缩进、头文件包含等约定供重写风格时参考test/c/test_sample.cpp仓库 C 测试目录的示例文件展示了基于 test/c/catch.hpp 的单元测试写法读者可仿照其为反转算法编写断言用例。七、编译与运行验证两个源文件均为独立可编译的完整程序按仓库 C 示例的标准流程编译运行# 编译三指针版本 g -stdc11 reverse_linked_list_3pointers.cpp -o reverse3 ./reverse3 # 输出50 40 30 20 10print() 以换行分隔即每行一个值 # 编译 XOR 双指针版本 g -stdc11 reverse_linked_list_2pointers.cpp -o reverse2 ./reverse2 # 输出同样为50 40 30 20 10main()中构造的链表为10 - 20 - 30 - 40 - 50两种算法反转后打印结果一致可用于交叉验证只要输出序列恰好逆序即证明链接反转正确完成。若在编译时遇到uintptr_t未声明部分编译器严格模式下cstdlib不保证导出该类型补充#include cstdint即可。八、总结通过本文可以建立一条清晰的认知链路反转链表的本质是反转链接而非交换数值三指针法以先保存后继为铁律用 3 个指针各司其职地完成迭代XOR 双指针法借助a ^ a 0的自反性把取后继这一信息压缩进一条表达式配合对uintptr_t的强制转换合法完成指针位运算从而将指针数从 3 降到 2无论哪种方法最终都要用hptr prev更新头指针才能让链表在新头下可被正确遍历。该技巧的价值不在于打破 O(n) 的复杂度下限而在于展示位运算与指针表示之间的精妙互动是理解指针本质上是整数地址这一底层事实的绝佳案例。仓库中的 README 与两份 C 源码 完整保留了从理论到可运行代码的全过程适合作为链表与位运算交叉训练的入门到进阶素材。【免费下载链接】cosmosWorlds largest Contributor driven code dataset | Used in Quark Search Engine, OpenGenus IQ, OpenGenus Visual Project项目地址: https://gitcode.com/gh_mirrors/co/cosmos创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表