[数据结构(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 }
时间: 2024-11-05 13:46:47

[数据结构(ywm版)] 异或指针双向链表的相关文章

数据结构复习之C语言指针与结构体

数据结构指针复习: #include <stdio.h> void main() { int a[5] = {8, 2, 3, 4, 5}; // a[3] == *(3+a) printf("%d\n", *(3+a)); // a[3] 4 printf("*a其实就是a[0]: %d\n", *a); // 8 // 地址是连续的 printf("%p\n", a+1); printf("%p\n", a+2

大话数据结构(八)Java程序——双向链表的实现

线性链表--双向链表 双向链表定义: 双向链表(double linked list): 是在单表单的每个结点中,再设置一个指向前驱结点的指针域.因此,在双向链表中的结点都有两个指针域,一个指向前驱,一个指向后继. 双向链表的存储结构 typedef struts DulNode{ Element data; Struct DulNode *prior;前驱指针 Struct DulNode *next;后继指针 }DulDouble, *DulLinkList; 双向链表的插入与删除 双向链表

看数据结构写代码(6)双向链表的实现

双向链表 只是 比 单链表 多了 一个 指向 前驱的 指针,在插入 和 删除 元素的 时候 得多处理一些.其余 没什么 区别. 而循环链表 的 尾指针 不再 指向 NULL,而是 指向 头指针,这样 既可以循环遍历,又节省 存储空间 . 每种链表 都有 好处,至于 如何 取舍,得看需求. 下面 奉上 双向链表的实现代码: // DoubleLinkList.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <stdlib.h

数据结构 Java版二叉排序树

二叉搜索树,又称为二叉查找树和二叉搜索树.它或者是一颗空树,或者具有下列性质的二叉树. 1 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值. 2 若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值. 3 它的左.右子树也都为二叉搜索树. 构造一颗二叉搜索树,目的不是为了排序,而是为了提高查找.插入和删除关键字的速度.一个有序数据集上的查找速度总是要快于无序数据集,而二叉搜索树这种非线性的结构,也有利于插入和删除的实现. 二叉搜索树的查找性能取决于二叉搜索树的形状,问

数据结构与算法-线性表之双向链表

参考博客: http://www.cnblogs.com/skywang12345/p/3561803.html https://blog.csdn.net/howlaa/article/details/38513235 1.概述 线性表是一种线性结构,由数组.单项链表和双向链表组成,这里讲讨论双向链表的理论知识和实现代码. 双向链表和单项链表类似,双向链表由两个指针作为指针域,分别指向前驱节点和后驱节点.可以从任何节点开始向前或者向后删除.增加.查找节点. 双链表的示意图如下: 在插入节点的时

数据结构C++版-队列

一.概念 分类: 二.补充说明 1.<面向对象的队列设计>课程问答: 首先要明确数据结构和数据存储结构的概念. 数据结构是指数据对象之间的逻辑关系,例如二叉树,队列,栈等,而数据存储结构是描述数据在计算机上的存储方式,它分为顺序存储和非顺序存储,数组就是典型的顺序存储. 而链表就是典型的非顺序存储,它需要知道前一个数据和后一个数据.链表是一种数据存储方式,是非顺序存储,而队列是一种数据结构,它可以采用顺序存储也可以采用非顺序存储. 2.<循环队列实现下>课程评论:要实现循环队列的循

数据结构开发(9):循环链表与双向链表

0.目录 1.循环链表的实现 2.双向链表的实现 3.小结 1.循环链表的实现 什么是循环链表? 概念上 任意数据元素都有一个前驱和一个后继 所有的数据元素的关系构成一个逻辑上的环 实现上 循环链表是一种特殊的单链表 尾结点的指针域保存了首结点的地址 循环链表的逻辑构成: 循环链表的继承层次结构: 循环链表的实现思路: 通过模板定义CircleList类,继承自LinkList类 定义内部函数 last_to_first(),用于将单链表首尾相连 特殊处理:首元素的插入操作和删除操作 重新实现:

数据结构C#版笔记--顺序表(SeqList)

线性结构(Linear Stucture)是数据结构(Data Structure)中最基本的结构,其特征用图形表示如下: 即:每个元素前面有且只有一个元素(称为"前驱"),同样后面有且只有一个元素(称为"后继")--注:起始元素的前驱认为是空,末尾元素的后继认为也是空,这样在概念上就不冲突了. 线性表(List)是线性结构的一种典型实现,它又可以分为:顺序表(SeqList)和链表(LinkList)二大类. 顺序表(SeqList)的基本特征为:元素在内部存储时

实现一个简洁版的智能指针

//智能指针可以自动销毁,用引用计数技术实现 #include<iostream> using namespace std; template<class T> class SmartPtr { protected:                  T* _ptr;                  size_t* _cout;                  void clear()                 {