[数据结构(ywm版)] 异或指针双向链表
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[数据结构(ywm版)] 异或指针双向链表相关的知识,希望对你有一定的参考价值。
在《数据结构题集》中看到这种链表,实际上就是把一般的双向链表的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 }
以上是关于[数据结构(ywm版)] 异或指针双向链表的主要内容,如果未能解决你的问题,请参考以下文章
剑指Offer面试题27(Java版):二叉搜索树与双向链表