博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
算法导论 XOR双向循环链表——内存高效链表
阅读量:4088 次
发布时间:2019-05-25

本文共 8938 字,大约阅读时间需要 29 分钟。

XOR双向循环链表——内存高效链表

1. 算法导论原题

10.2-8 ★(★代表题目略有难度)

Explain how to implement doubly linked lists using only one pointer value x.np per item instead of the usual two(next and pre). Assume that all pointer values can be interpreted as k-bit integers, and define x.np to be x.np = x.next XOR x.pre,the k-bit“exclusive-or” of x.next and x.pre.(The value NIL is represented by 0.) Be sure to describe what information you need to access the head of the list. Show how to implement the SEARCH, INSERT, and DELETE operations on such a list. Also show how to reverse such a list in O(1) time.
译:解释如何在每个结点里不用两个指针(next指针和pre指针),而是只使用一个指针x.np实现双链表(译者注:x指结点对象)。假设所有指针指都是k位二进制的整数,并且定义x.np为x.next XOR x.pre(即x.next和x.pre的k位按位异或)。确保描述你需要进入链表第一个结点的所有信息。展示在这样一个链表如何实现搜索、插入和删除操作。同时展示如何在O(1)的时间复杂度内反转链表。

2. XOR双向循环链表如何实现?

跟普通双向链表不同的是,指向前结点的指针pre和指向后结点的指针next都按位异或成一个指针np。

1. 第一个问题,如何通过这一个指针np来遍历链表呢?
按位异或有这样一个功能:已知A^B=C,那么有C^A=B,C^B=A。(注意的是顺序不影响按位异或的结果,A^B等价于B^A)(若不能理解按位异或的功能请看更详细的参考博文)。那么同理已知prev^next=NP,那么next^NP=preprev^NP=next。也就是说假设这里有链表已知两个结点A,B(且A的下一个结点就是B),那么要得到B的下一个结点C,只需要将结点A的地址(B.pre)和B.NP进行按位异或,就得到结点C的地址(B.next)。同理要得到结点A的前一个结点(A.pre),只需要将A.NP和结点B的地址(A.next)进行按位异或即可。(若不能理解这一点,请看更详细的参考博文)。
2. 第二个问题,如何得到链表第一个结点?
因为要两个结点才能访问下一个结点,所以我们将链表定义为两个结点的结合(头结点和尾结点),而不是原来只有一个头节点。这样每次循环都可以通过当前两个结点来访问到下一个结点。
3. 第三个问题,如何在O(1)时间内反转链表?
因为在开始遍历的时候,我们将尾结点的地址和头结点的NP按位异或得到头节点的下一个结点。我们知道只要通过两个结点就可以访问上一个或下一个结点(即头节点的地址和尾结点的NP按位异或得到尾结点的上一个结点),那么只要我们将头节点和尾结点交换,是可以反转这个遍历的顺序的:这个时候尾结点的地址就是原来头节点的地址,头结点的NP就是原来尾结点的NP,所以链表会按照从原来头节点到原来尾结点的顺序进行遍历。

3. C++ 实现

//XORCircularDoubleLink.h#pragma once#include 
#include
#include "Util.h"template
class Node{public: Node(ElemType* pData = NULL, Node
* pNext = NULL, Node
* pPrev = NULL); ElemType* const& GetData() const; void SetData(ElemType* val); ElemType* HavaData(); Node
* GetNext(Node
* prevNode) const; Node
* GetPrev(Node
* nextNode) const; Node
* const& GetNP() const ; void SetNP(Node
* prevNode, Node
* nextNode);private: ElemType* m_pData; Node
* m_pNP;};template
class XORCircularDoubleLink{public: XORCircularDoubleLink(); unsigned int const& GetLength() const; bool Insert(ElemType elem, unsigned int pos); bool Delete(unsigned int pos, ElemType* elem); bool Search(unsigned int pos, ElemType* elem) const; bool Visit(ElemType* elem, const unsigned int& pos) const; bool Empty(); bool Reverse(); Node
* HavaHeadNode(); Node
* const& GetHeadNode() const;private: //移动Prev、Current、Next指针 void MoveNCP(int offset, Node
** pPrev, Node
** pCurrent, Node
** pNext) const; //检测位置是否超出范围 bool OutOfRangeDectection(unsigned int pos, bool canEuqalLength = false) const;private: Node
* m_pHeadNode; Node
* m_pTailNode; unsigned int m_length;};//————————————————————————————————//Node类的实现template
inline Node
::Node(ElemType* pData /* = NULL */, Node
* pNext /* = NULL */, Node
* pPrev /* = NULL */) :m_pData(pData),m_pNP(Util::XOR(pPrev,pNext)){}template
inline void Node
::SetNP(Node
* prevNode , Node
* nextNode){ m_pNP = Util::XOR
*>(prevNode,nextNode);}template
inline Node
* const& Node
::GetNP() const{ return m_pNP;}template
inline Node
* Node
::GetNext(Node
* prevNode) const{ return Util::XOR
*>(prevNode,m_pNP);}template
inline ElemType* const& Node
::GetData() const { return m_pData;}template
inline void Node
::SetData(ElemType* val){ m_pData = val; }template
inline ElemType* Node
::HavaData() { return m_pData; }template
inline Node
* Node
::GetPrev(Node
* nextNode) const{ return Util::XOR
*>(nextNode,m_pNP);}//————————————————————————————————//XORCircularDoubleLink类实现template
inline XORCircularDoubleLink
::XORCircularDoubleLink() :m_pHeadNode(new Node
()),m_length(0),m_pTailNode(new Node
()){ m_pHeadNode->SetNP(m_pTailNode,m_pTailNode); m_pTailNode->SetNP(m_pHeadNode,m_pHeadNode);}template
inline bool XORCircularDoubleLink
::Insert(ElemType elem, unsigned int pos){ OutOfRangeDectection(pos,true); Node
*pCurrentNode = NULL, *pPrevNode = NULL, *pNextNode = NULL; MoveNCP(pos,&pPrevNode,&pCurrentNode,&pNextNode); Node
* insertNode = new Node
(new ElemType(elem),pCurrentNode,pNextNode); pCurrentNode->SetNP(pPrevNode,insertNode); pNextNode->SetNP(insertNode,pNextNode->GetNext(pCurrentNode)); ++m_length; return true;}template
inline bool XORCircularDoubleLink
::Delete(unsigned int pos, ElemType* elem){ OutOfRangeDectection(pos); Node
*pCurrentNode = NULL, *pPrevNode = NULL, *pNextNode = NULL; MoveNCP(pos+1,&pPrevNode,&pCurrentNode,&pNextNode); *elem = *pCurrentNode->GetData(); pPrevNode->SetNP(pPrevNode->GetPrev(pCurrentNode),pNextNode); pNextNode->SetNP(pPrevNode,pNextNode->GetNext(pCurrentNode)); --m_length; delete pCurrentNode; return true;}template
inline unsigned int const& XORCircularDoubleLink
::GetLength() const{ return m_length;}template
inline bool XORCircularDoubleLink
::Search(unsigned int pos, ElemType* elem) const{ OutOfRangeDectection(pos); Node
*pCurrentNode = NULL, *pPrevNode = NULL, *pNextNode = NULL; MoveNCP(pos,&pPrevNode,&pCurrentNode,&pNextNode); *elem = *pNextNode->GetData(); return true;}template
inline bool XORCircularDoubleLink
::Visit(ElemType* elem, const unsigned int& pos) const{ OutOfRangeDectection(pos); return Search(pos,elem);}template
inline bool XORCircularDoubleLink
::Empty(){ return !m_length;}template
inline Node
* XORCircularDoubleLink
::HavaHeadNode(){ return m_pHeadNode;}template
inline Node
* const& XORCircularDoubleLink
::GetHeadNode() const{ return m_pHeadNode;}template
inline bool XORCircularDoubleLink
::Reverse(){ Util::Swap(&m_pHeadNode,&m_pTailNode); return true;}template
void XORCircularDoubleLink
::MoveNCP(int offset, Node
** pPrev, Node
** pCurrent, Node
** pNext) const{ *pPrev = m_pTailNode; *pCurrent = m_pHeadNode; *pNext = (*pCurrent)->GetNext(*pPrev); while(offset-- != 0) { *pPrev = *pCurrent; *pCurrent = *pNext; *pNext = (*pCurrent)->GetNext(*pPrev); }}template
inline bool XORCircularDoubleLink
::OutOfRangeDectection(unsigned int pos, bool canEqualLength) const{ if ( (pos > GetLength() || pos < 0) || (!canEqualLength && pos == GetLength()) ) { assert(false && "Error: SinglyLink's search pos is out of range!\n"); return true; } return false;}
//Util.h#pragma oncenamespace Util{    template
void PrintMemory(const T& dateStruct, unsigned int size) { cout << "PrintMemory: "; for (int i = 0; i != size; i++) { ElemType tempElem; if (!dateStruct.Visit(&tempElem,i)) { printf("\n"); return; } printf("%d ",tempElem); } printf("\n"); } template
T XOR(T const& lhs, T const& rhs) { return (T)(((unsigned long)lhs)^((unsigned long)rhs)); } template
void Swap(T* lhs, T* rhs) { *rhs = XOR(*lhs,*rhs); *lhs = XOR(*lhs,*rhs); *rhs = XOR(*lhs,*rhs); }}
//main.cpp#include "Util.h"#include "XORCircularDoubleLink.h"#include 
using namespace std;typedef int ElemType;int main(){ XORCircularDoubleLink
testXORCircularDoubleLink; cout << "testXORCircularDoubleLink is " << (testXORCircularDoubleLink.Empty() ? "Empty." : "Not Empty.") << endl; Util::PrintMemory(testXORCircularDoubleLink,testXORCircularDoubleLink.GetLength()); for (int i = 0; i != 5; i++) { testXORCircularDoubleLink.Insert(i+1,i); cout << "\nInsert:" << i+1 << endl; cout << "testXORCircularDoubleLink is " << (testXORCircularDoubleLink.Empty() ? "Empty." : "Not Empty.") << endl; Util::PrintMemory(testXORCircularDoubleLink,testXORCircularDoubleLink.GetLength()); } testXORCircularDoubleLink.Reverse(); cout << "\nReverse testXORCircularDoubleLink..." << endl; Util::PrintMemory(testXORCircularDoubleLink,testXORCircularDoubleLink.GetLength()); for (int i = 0; i != 2; i++) { ElemType tempElem; testXORCircularDoubleLink.Delete(i,&tempElem); cout << "\nDelete:" << tempElem << endl; cout << "testXORCircularDoubleLink is " << (testXORCircularDoubleLink.Empty() ? "Empty." : "Not Empty.") << endl; Util::PrintMemory(testXORCircularDoubleLink,testXORCircularDoubleLink.GetLength()); } return 0;}

4. 程序运行结果

testXORCircularDoubleLink is Empty.

PrintMemory:

Insert:1

testXORCircularDoubleLink is Not Empty.
PrintMemory: 1

Insert:2

testXORCircularDoubleLink is Not Empty.
PrintMemory: 1 2

Insert:3

testXORCircularDoubleLink is Not Empty.
PrintMemory: 1 2 3

Insert:4

testXORCircularDoubleLink is Not Empty.
PrintMemory: 1 2 3 4

Insert:5

testXORCircularDoubleLink is Not Empty.
PrintMemory: 1 2 3 4 5

Reverse testXORCircularDoubleLink…

PrintMemory: 5 4 3 2 1

Delete:5

testXORCircularDoubleLink is Not Empty.
PrintMemory: 4 3 2 1

Delete:3

testXORCircularDoubleLink is Not Empty.
PrintMemory: 4 2 1

5. 参考博文

1. 《深入理解按位异或运算符》:

2. 《XOR Linked List – A Memory Efficient Doubly Linked List》:(虽然全文英语,但单词都是很简单的,所以本人不作翻译)

转载地址:http://yiyii.baihongyu.com/

你可能感兴趣的文章
(python版)《剑指Offer》JZ18:二叉树的镜像
查看>>
(python版)《剑指Offer》JZ22:从上往下打印二叉树
查看>>
(python版)《剑指Offer》JZ24:二叉树中和为某一值的路径
查看>>
(python版)《剑指Offer》JZ38:二叉树的深度
查看>>
(python版)《剑指Offer》JZ39:判断是否为平衡二叉树
查看>>
(python版)《剑指Offer》JZ57:二叉树的下一个结点
查看>>
(python版)《剑指Offer》JZ58:对称的二叉树
查看>>
(python版)《剑指Offer》JZ60:把二叉树打印成多行 (又名:从上往下打印二叉树 II)
查看>>
(python版)《剑指Offer》JZ59:按之字形顺序打印二叉树(又名:从上到下打印二叉树 III)【附手写图解】
查看>>
(python版)《剑指Offer》JZ61:序列化二叉树【超详解,附手写图解】
查看>>
(python版)《剑指Offer》JZ01:二维数组中的查找
查看>>
(python版)《剑指Offer》JZ06:旋转数组的最小数字
查看>>
(python版)《剑指Offer》JZ13:调整数组顺序使奇数位于偶数前面
查看>>
(python版)《剑指Offer》JZ28:数组中出现次数超过一半的数字
查看>>
(python版)《剑指Offer》JZ30:连续子数组的最大和
查看>>
(python版)《剑指Offer》JZ32:把数组排成最小的数
查看>>
(python版)《剑指Offer》JZ40:数组中只出现一次的数字
查看>>
(python版)《剑指Offer》JZ50:数组中重复的数字
查看>>
(python版)《剑指Offer》JZ51:构建乘积数组
查看>>
(python版)《剑指Offer》JZ02:替换空格
查看>>