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