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

原题出自"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)内完成?

 

你可能感兴趣的:(双向循环链表)