【数据结构(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 }

 

目录
相关文章
|
28天前
|
存储 缓存 算法
数据结构-链表(一)
链表(Linked List)是一种常见的数据结构,用于存储和组织数据。与数组不同,链表的元素(节点)在内存中不必连续存储,而是通过指针链接在一起。 链表由多个节点组成,每个节点包含两部分:数据(存储实际的元素值)和指针(指向下一个节点的引用)。链表的第一个节点称为头节点,最后一个节点称为尾节点,尾节点的指针通常指向空值(null)。
31 1
|
30天前
|
存储 C++
数据结构第六弹---带头双向循环链表
数据结构第六弹---带头双向循环链表
|
1月前
|
存储
【单链表】数据结构单链表的实现
【单链表】数据结构单链表的实现
|
10天前
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构|双向链表|带头结点|头插|尾插|尾删|头删
数据结构|双向链表|带头结点|头插|尾插|尾删|头删
|
13天前
数据结构--链表刷题(一)快慢指针(上)
数据结构--链表刷题(一)快慢指针
16 0
|
22天前
|
缓存 算法 搜索推荐
【数据结构】链表(单链表与双链表实现+原理+源码)
【数据结构】链表(单链表与双链表实现+原理+源码)
|
26天前
|
存储 编译器 C语言
【数据结构】深入浅出理解链表中二级指针的应用
【数据结构】深入浅出理解链表中二级指针的应用
28 0
|
1月前
|
存储
【双向链表】数据结构双向链表的实现
【双向链表】数据结构双向链表的实现
|
1月前
|
存储 算法
数据结构——单链表的基本操作
数据结构——单链表的基本操作