【数据结构(ywm版)】异或指针双向链表

简介: 在《数据结构题集》中看到这种链表,实际上就是把一般的双向链表的next和prior两个指针通过异或运算合并为一个指针域来存储,每个结点确实可以减少一个指针的空间,但会带来取指针值时运算的开销。 实现的时候,先搞清双向链表,把握异或指针域的原理公式,然后从双向链表出发进行转换即可。

在《数据结构题集》中看到这种链表,实际上就是把一般的双向链表的next和prior两个指针通过异或运算合并为一个指针域来存储,每个结点确实可以减少一个指针的空间,但会带来取指针值时运算的开销。

实现的时候,先搞清双向链表,把握异或指针域的原理公式,然后从双向链表出发进行转换即可。

1 typedef struct XorNode{ //结点的定义
2     ElemType data;
3     struct XorNode* LRPtr;
4 }XorNode, *XorPointer;
5 
6 typedef struct{//无头结点的异或指针双向链表
7     XorPointer Left, Right; //只保存指向首、末结点的指针
8 }XorList;

每个结点的指针域存的是它的前驱和后继的异或值,即 LRPtr = prior ^ next,指针异或运算本质上就是整型的内存地址的按位异或。

1 //指针异或函数,注意指针变量不支持^运算,需要强转为整型(内存地址,长度与机器有关,C将之封装为size_t型)
2 XorPointer XorP(XorPointer p, XorPointer q){
3     size_t x = (size_t)p;
4     size_t y = (size_t)q;
5     return (XorPointer)(x^y);
6 }

这相当于把前驱和后继的指针编码到一个指针域中了,那么如何解码呢,只凭LRPtr本身是不可以的,必须再已知prior或next才行,这得益于异或运算特有的“对称”性质。

异或运算满足结合律,0为幺元,每个元素和自身互为逆元;所以设a, b为两个内存地址,则有

a^(a^b) = (a^a)^b = b;

(a^b)^b = a^(b^b) = a;

有了这两条,则可推出取得前驱或后继的式子,即

prior = LRPtr ^ next;

next = prior ^ LRPtr;

根据这两个关系,把双向链表的next和prior的运算用LRPtr替换即可。注意由于是循环链表,只有一个元素时,其前驱和后继都为自身。

注意我实现的是无头结点的循环链表,而链表的表长又不是显式维护的,所以要以回到起点作为循环退出条件之一,并设一个记录步数的变量k来避免 i 值超过表长而导致的“兜圈子”;在边界操作时注意修改首末元素指针。

 1 Status CreateList(XorList& L, int n){
 2 //头插法建表,无头结点,入口条件:n>=1
3 n--; 4 XorPointer p; 5 p = (XorPointer)malloc(sizeof(XorNode)); 6 L.Left = L.Right = p; 7 p->data = n; 8 p->LRPtr = XorP(p, p); 9 while(n--){ 10 p = (XorPointer)malloc(sizeof(XorNode)); 11 p->data = n; 12 p->LRPtr = XorP(L.Right, L.Left); 13 L.Left->LRPtr = XorP(p, XorP(L.Right, L.Left->LRPtr)); 14 L.Right->LRPtr = XorP(XorP(L.Right->LRPtr,L.Left),p); 15 L.Left = p; 16 } 17 } 18 19 Status Insert(XorList& L, int i, ElemType e){ 20 //在第i个位置前插入结点e(i的合法值为1~表长+1) 21 //由于无头结点,故判断表长是否够i,要通过计步变量k 22 XorPointer p = L.Left, q = L.Right; 23 int k = 1; 24 while(p!=L.Right && k<i){ //p指向i, q指向i-1;保证至多扫描一遍后退出 25 XorPointer t = p; 26 p = XorP(q, p->LRPtr); 27 q = t; 28 k++; 29 } 30 if(k+1<i) return ERROR; //表长不够i-1 31 if(k+1==i){p = L.Left; q = L.Right;} //插到表长+1的位置,即最后一个元素之后 32 XorPointer s = (XorPointer)malloc(sizeof(XorNode)); 33 s->data = e; 34 s->LRPtr = XorP(q, p); 35 q->LRPtr = XorP(XorP(q->LRPtr, p), s); 36 p->LRPtr = XorP(s, XorP(q, p->LRPtr)); 37 if(i==1) L.Left = s; 38 if(i==k+1) L.Right = s; 39 return OK; 40 } 41 42 Status Delete(XorList& L, int i, ElemType& e){ 43 //删除第i个位置的元素,用e返回其值(i的合法值为1~表长) 44 XorPointer p = L.Left, q = L.Right; 45 int k = 1; 46 while(p!=L.Right && k<i){ //p指向i, q指向i-1 47 XorPointer t = p; 48 p = XorP(q, p->LRPtr); 49 q = t; 50 k++; 51 } 52 if(k<i) return ERROR; //表长不够i 53 if(p==L.Right) L.Right = q; //删除最后一个结点 54 e = p->data; 55 XorPointer m = XorP(q, p->LRPtr); //m暂存p的后继 56 q->LRPtr = XorP(XorP(q->LRPtr, p), m); 57 m->LRPtr = XorP(q, XorP(p, m->LRPtr)); 58 free(p); 59 if(i==1) L.Left = m; //删除第一个结点 60 return OK; 61 } 62 63 Status Traverse(XorList& L, void (*visit)(XorNode)){ 64 XorPointer p = L.Left, q = L.Right; 65 while(p != L.Right){ 66 visit(*p); 67 XorPointer t = p; 68 p = XorP(q,p->LRPtr); 69 q = t; 70 } 71 visit(*p); 72 return OK; 73 } 74 75 void print(XorNode xn){ 76 printf("[%d]",xn.data); 77 } 78 79 int main() 80 { 81 XorList l; 82 CreateList(l,10); 83 ElemType e; 84 if(Delete(l,5,e)) 85 Traverse(l,print); 86 return 0; 87 }

 

目录
相关文章
|
11月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
172 4
|
8月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
200 30
|
8月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
310 25
|
9月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
337 5
|
10月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
11月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
282 5
|
11月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
201 4
|
11月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
805 4
|
11月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
11月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法