數據結構:XOR鏈表--- 一個空間高效的雙向鏈表

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;
}
發佈了258 篇原創文章 · 獲贊 135 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章