您的位置:首页 > 其它

仅用一个指针域,实现双向循环链表

2012-10-12 00:00 302 查看
原题出自"Introduction to Algorithms"中的练习题,10.2.8. 今天恰巧看到,觉得挺有意思,就写一写。

问题:

10.2-8
Explain how to implement doubly linked lists using only one pointer value np[x]
per item instead of the usual two (next and prev). Assume that all pointer
values can be interpreted as k-bit integers, and define np[x] to be np[x] =
next[x] XOR prev[x], the k-bit “exclusive-or” of next[x] and prev[x]. (The value
NIL is represented by 0.) Be sure to describe what information is needed to access
the head of the list. Show how to implement the S EARCH, I NSERT, and D ELETE
operations on such a list. Also show how to reverse such a list in O(1) time.

期望的调用方法

search(list, key)

insert(list, node)

delete(list, node)

reverse(list)

代码

#include <stdio.h>
#include <stdlib.h>

/**
* circular doubly linked list with only one pointer value
* SENTINEL <-> ELE1 <-> ELE2 <...> ELEn
* ELEn <-> SENTINEL
*
*/

/**
* Operations
* search
* insert
* delete
* traverse
* reverse
*
*/

struct node {
int id;
unsigned long np;
};

struct list {
struct node SENTINEL;
struct node *HEAD;
struct node *HEAD_PREV;
struct node *prevNode;
struct node *tmpNode;
};

/* global variable, just for demonstration purpose */
static int ID = 0;

/* auxiliary function */
unsigned long toUlong(struct node *pNode) {
return (unsigned long)(void*)pNode;
}

struct node *toPNode(unsigned long ul) {
return (struct node*)(void*)ul;
}

/* operations */

/* init list */
void INIT_LIST(struct list* pList) {
pList->SENTINEL.id = -1; pList->SENTINEL.np = 0;
pList->HEAD_PREV = pList->HEAD = &(pList->SENTINEL);
pList->prevNode = pList->tmpNode = NULL;
}

/* allocate a new Node */
int spawn(struct node **ppNode) {
*ppNode = (struct node*)malloc(sizeof(struct node));
if (*ppNode == NULL) {
perror("spawn node failure");
exit(1);
}
(*ppNode)->id = ID;
(*ppNode)->np = 0;
ID += 1;
return ID-1;
}

/* insert pNode into the list */
void insert(struct list *pList, struct node *pNode) {
if (pList->HEAD_PREV == pList->HEAD) {
pList->HEAD_PREV = pNode;
}
else {
struct node *pNext = toPNode(pList->HEAD->np ^ toUlong(pList->HEAD_PREV));
pList->HEAD->np = toUlong(pList->HEAD_PREV) ^ toUlong(pNode);
pNode->np = toUlong(pList->HEAD) ^ toUlong(pNext);
pNext->np = toUlong(pList->HEAD) ^ pNext->np ^ toUlong(pNode);
}
}

/* iterate over the list */
#define ITER_LIST(pList, pNode)						\
for (pList->prevNode = pList->HEAD, pList->tmpNode = NULL, \
pNode = toPNode(toUlong(pList->HEAD_PREV) ^ pList->HEAD->np);	   \
pNode != pList->HEAD;						\
pList->tmpNode = pNode, pNode = toPNode(toUlong(pList->prevNode) ^ pNode->np), \
pList->prevNode = pList->tmpNode)

/* reverse list */
void reverse(struct list *pList) {
pList->HEAD_PREV = toPNode(toUlong(pList->HEAD_PREV) ^ pList->HEAD->np);
}

/* search item in list */
struct node* search(struct list *pList, int key) {
struct node *pNode;
ITER_LIST(pList, pNode) {
if (pNode->id == key)
return pNode;
}
return NULL;
}

/* delete item from list */
void delete(struct list *pList, struct node *pNode) {
struct node *current, *next;
ITER_LIST(pList, current) {
if (current->id == pNode->id && current->np == pNode->np) {
break;
}
}
if (current != pList->HEAD) {
/* node found, delete node */
next = toPNode(toUlong(pList->prevNode) ^ current->np);
pList->prevNode->np = pList->prevNode->np ^ toUlong(current) ^ toUlong(next);
next->np = next->np ^ toUlong(current) ^ toUlong(pList->prevNode);
free(current);
}
}

/* test */
int main() {
int ret, i;
struct node *ptmpNode;
struct list L;
struct list *pL = &L;
INIT_LIST(&L);
/* test1 - basic */
printf("======================Test Basic======================\n");
printf("SENTINEL->id = %d \n", pL->HEAD->id);
/* test2 - spawn */
printf("======================Test spawn======================\n");
for (i=0; i<10; i++) {
ret = spawn(&ptmpNode);
printf("id = %d \t np = %lu \t ret = %d\n",
ptmpNode->id, ptmpNode->np, ret);
free(ptmpNode);
}
/* test3 - insert */
printf("======================Test insert======================\n");
for (i=0; i<10; i++) {
ret = spawn(&ptmpNode);
insert(&L, ptmpNode);
printf("INSERT: id = %d \t np = %lu \n",
ptmpNode->id, ptmpNode->np);
}
/* test4 - traverse */
printf("======================Test traverse======================\n");
ITER_LIST(pL, ptmpNode) {
printf("id = %d \t np = %lu \n",
ptmpNode->id, ptmpNode->np);
}
/* test5 - reverse */
printf("======================Test reverse======================\n");
reverse(&L);
ITER_LIST(pL, ptmpNode) {
printf("id = %d \t np = %lu \n",
ptmpNode->id, ptmpNode->np);
}
/* test6 - search */
printf("======================Test search======================\n");
for (i=0; i<20; i++) {
ptmpNode = search(&L, i);
if (ptmpNode) {
printf("SEARCH: id = %d, np = %lu \n",
ptmpNode->id, ptmpNode->np);
}
else {
printf("SEARCH: id = %d, NOT FOUND \n", i);
}
}
/* test7 - delete */
printf("======================Test delete======================\n");
ptmpNode = search(&L, 15);
if (ptmpNode) {
delete(pL, ptmpNode);
}
ITER_LIST(pL, ptmpNode) {
printf("id = %d \t np = %lu \n",
ptmpNode->id, ptmpNode->np);
}
return 0;
}


遗留问题

1. 是否可以不用到sentinel?

2. delete操作是否有可能在O(1)内完成?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  双向循环链表