XOR鏈表— 一個空間高效的雙向鏈表
傳統的雙向鏈表,要求有兩個地址閾,分別存儲前一個和後一個節點的地址。這裏我們提到的空間高效的雙向鏈表,僅僅爲每一個節點申請一個地址空間,這空間有效的雙向鏈表叫做XOR鏈表。在XOR鏈表中不在存儲一個確定的地址,而是對於每一個節點存儲前一個地址和後一個地址的XOR值。
看上面的雙向鏈表,這是原始的雙向鏈表,下面簡單介紹其和XOR鏈表的不同。
傳統的雙向鏈表:
Node A:
prev = NULL,next = add(B) // 表示前一個是NULL,下一個節點的地址是B
Node B:
prev = add(A),next = add(C) // 表示前一個節點地址是A,下一個節點的地址是C
Node C:
prev = add(B),next = add(D) // 表示前一個節點地址是B,下一個節點的地址是D
Node D:
prev = add(C),next = NULL // 表示前一個節點地址是C,下一個節點是NULL
XOR鏈表:
在每一個節點中的地址變量是前一個地址和後一個地址的亦或值(XOR of next and previous)
我們將這個變量表示成xnp。
Node A:
npx = 0 XOR add(B) //位運算XOR, 0和地址B的XOR值
Node B:
npx = add(A) XOR add(C) //位運算, 地址A和地址B的XOR值
Node C:
npx = add(B) XOR add(D) //位運算, 地址B和地址D的XOR值
Node D:
npx = add(C) XOR 0 //位運算, 地址C和0的XOR值
訪問XOR雙向鏈表:
我們可以根據簡單的亦或操作,從前往後或者從後往前的訪問這個雙向鏈表,但是訪問的時候要保留前一個節點的地址。
例如當我們在Node C時,接下來要訪問Node D:
需要紀錄Node B的地址add(B),我們知道Node C的npx = add(B) XOR add(D),根據亦或運算,npx(C) XOR add(B) = add(B) XOR add(D) XOR add(B) = add(D), 因此我們得到Node D的地址,可以訪問節點D。
同理,當我們在Node C時,要訪問Node B,我們需要知道上一個節點D的地址add(D)。
add(B) = npx(C) XOR add(D) = add(B) XOR add(D) XOR add(D) = add(B)。
1、從前往後遍歷鏈表(從後向前遍歷同理)
紀錄前一個地址prevadd = 0;
1、訪問Node A,因爲Node A是第一個節點,可以直接訪問得到,visit(A) ,計算下一個節點B的地址,add(B) = prevadd XOR npx(A) = 0 XOR 0 XOR add(B) = add(B), 更新前一個節點地址prevadd = add(A)。
2、訪問Node B,visit(B),計算下一個節點C的地址,add(C) = prevadd XOR npx(B) = add(A) XOR add(C) XOR add(A) = add(C), 更新前一個節點地址prevadd = add(B)。
3、訪問Node C,visit(C) 。計算下一個節點D的地址,add(D) = prevadd XOR npx(C) = add(B) XOR add(B) XOR add(D) = add(D), 更新前一個節點地址prevadd = add(C)。
4、訪問Node D,visit(D) 。計算下一個節點的地址,nextadd = prevadd XOR npx(D) = add(C) XOR add(C) XOR 0 = 0, 下一個節點地址是0,遍歷結束。
對於這樣的一個數據結構,我們簡單給出一個頭插法的鏈表的添加,和從前向後和從後向前的鏈表的遍歷。我們知道對於一種數據結構都需要給出在第i個位置插入和刪除的操作,這裏目前沒有給出這段代碼,但是需要記住的從插入或者刪除的第i個位置,之後的所有節點的npx都要修改,可以參考本部分的遍歷鏈表的代碼。
Code:
/**
*Author: xiaoran
*座右銘:既來之,則安之
*/
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<string>
#include<string.h>
using namespace std;
typedef long long LL;
const int MAXN = 1005;
struct Node{
int data;
struct Node *npx; // XOR of next and previous
};
// return XOR value of node address
struct Node* XOR (struct Node *a, struct Node *b)
{
return (struct Node*) ((unsigned long ) (a) ^ (unsigned long) (b));
}
/**
*使用頭插法插入鏈表,
*每次將新插入的新節點作爲頭節點
*/
void insert(struct Node **head_ref, int data){
//申請新的空間
struct Node * newnode = (struct Node *) malloc(sizeof(struct Node));
newnode->data = data;
//因爲新的鏈表作爲頭結點,所有其npx = NULL XOR 當前的頭節點的地址
newnode->npx = XOR(*head_ref, NULL);
// 如果鏈表非空,需要修改當前頭節點的npx值,當前節點的npx = 新的頭結點和
// 下個節點的地址的XOR值
if(*head_ref != NULL){
// 計算出下個節點的地址
struct Node *next = XOR((*head_ref)->npx, NULL);
(*head_ref)->npx = XOR(newnode,next);
}
*head_ref = newnode;
}
/**
*遍歷鏈表
*/
void printXORList(struct Node *head){
struct Node *cur = head;
struct Node *prev = NULL;
struct Node *next;
printf("Following are the nodes of Linked XORList: \n");
while(cur != NULL){
printf("%d ",cur->data);
// 計算下一個節點的地址
next = XOR(prev, cur->npx);
// 跟新前一個地址和,當前地址
prev = cur;
cur = next;
}
printf("\n");
}
int main()
{
struct Node *head = NULL;
insert(&head, 10);
insert(&head, 20);
insert(&head, 30);
insert(&head, 40);
printXORList(head);
return 0;
}