下面是使用数组和循环双向链表实现List
抽象的代码
/* list .h */
/*
List is an ordered collection of homogenuous type elements(unique or duplicate).
List is not designed to have collection of heterogenuous type elements
All elements in a List are related.
List is mutable
Each element has a position.
If an element is deleted, then still the remaining elements sit in new order.
Array implementation of List
*/
#include<stddef.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
typedef enum{false, true}bool;
typedef enum {CREATE_NEW_LIST, DOUBLE_THE_LIST, HALF_THE_LIST}Op;
#if defined(ARRAY)
typedef struct List{
int *array;
int lastItemPosition;
int size;
}List;
#define INITIAL_LIST_SIZE 50
#elif defined(LINKED_LIST)
typedef struct DListNode{
int item;
struct DListNode *next;
struct DListNode *prev;
}DListNode;
/*
Reason to introduce 'List' type:
Problem 1:
Say, user code has 'x' and 'y' pointing to the same list that is built using 'Node' type.
Some part of user code update list with new item using 'x'
'y' is not in sync with this updation
Node *x = someCreatedList;
Node *y = x;
Node *z = malloc(sizeof(Node));
z->next = x;
x = z; //y misses that reference.
Solution:
Maintain a List type, whose job is to point to head(first node) of the list.
User code will go via reference of List type
Problem 2:
It make sense to have references of 'Node' type pointing to NULL
Applying operation[insertItem()] on NULL pointer will cause runtime errors
Solution:
Run operations over List type because it does not make sense to have reference of SList type pointing to NULL.
To solve problem1 & problem2, here is 'List' type
*/
typedef struct List{ //Circular
DListNode *head;
int size; //size attribute is not part of list definition, but quick way to help user code
}List;
#define SENTINEL_ITEM -1
#else
#error "Wrong list implementation macro name !!!"
#endif
void insertItem(List *, int, int);
void deleteItem(List *, int);
List* createList(List *, Op);
/* linkedListImpl.c */
#if defined(LINKED_LIST)
#include"list.h"
DListNode* createNode(int value){
DListNode *newNode= malloc(sizeof(DListNode));
newNode->next = newNode;
newNode->prev = newNode;
newNode->item = value;
return newNode;
}
List *createList(List *list, Op opType ){
List *lptr = (List *)malloc(sizeof(List));
if(opType == CREATE_NEW_LIST){
/*
Amidst performing operations on 'List', you need to check special cases
for List with no item
To reduce the number of special cases, we designate one node as 'SENTINEL'
After using sentinel, there will be no NULL assignments/check in code.
*/
DListNode *sentinel = createNode(SENTINEL_ITEM);
lptr->head = sentinel;
lptr->size = 0;
return lptr;
}else{
fprintf(stderr, "Invalid flag passed to createList() \n");
return (List *)NULL;
}
}
void insertItem(List *linkedList, int newItem, int index){ //O(n)
DListNode *positionElem = linkedList->head;
bool isFirstElement = true; //assume
if(index + 1 <= linkedList->size){
fprintf(stderr, "Position already filled \n");
return;
}else{
DListNode *newNode = createNode(newItem);
while(index--){
positionElem = positionElem->next;
isFirstElement = false;
}
if(isFirstElement){
newNode->next = positionElem;
positionElem->prev = newNode;
}else{
newNode->next = positionElem->next;
newNode->next->prev = positionElem->next;
}
newNode->prev = positionElem;
positionElem->next = newNode;
}
return;
}
void deleteItem(List *linkedList, int index){
//delete element given index
return;
}
#endif
/* arrayImpl.c */
#if defined(ARRAY)
#include"list.h"
List *createList(List *list, Op opType){
List *lptr = (List *)malloc(sizeof(List));
if(opType == CREATE_NEW_LIST){
lptr->array = malloc(INITIAL_LIST_SIZE*sizeof(int));
lptr->array = memset(lptr->array, -1, INITIAL_LIST_SIZE*sizeof(int));
lptr->lastItemPosition = -1;
lptr->size = INITIAL_LIST_SIZE;
}else if(opType == DOUBLE_THE_LIST){
lptr->array = malloc(2*(list->size)*sizeof(int));
lptr->array = memcpy(lptr->array, list->array, list->size*sizeof(int));
lptr->lastItemPosition = list->lastItemPosition;;
lptr->size = 2*(list->size);
}else if(opType == HALF_THE_LIST){
lptr->array = malloc(((list->size)/2)*sizeof(int));
lptr->array = memcpy(lptr->array, list->array, (list->size/2)*sizeof(int));
lptr->lastItemPosition = list->lastItemPosition;;
lptr->size = (list->size)/2;
}
return lptr;
}
void insertItem(List *arrayList, int newItem, int index){
/* House keeping */
if(arrayList->lastItemPosition + 1 == arrayList->size){
arrayList = createList(arrayList, DOUBLE_THE_LIST);
}
/* Insert the element */
arrayList->array[index] = newItem;
if(index > arrayList->lastItemPosition){
arrayList->lastItemPosition = index;
}
return;
}
void deleteItem(List *arrayList, int index){
arrayList->array[index] = -1;
if(index == arrayList->lastItemPosition){
arrayList->lastItemPosition--;
}
/* House keeping */
if(arrayList->size > INITIAL_LIST_SIZE){
if(arrayList->lastItemPosition == ((arrayList->size)/2)){
arrayList = createList(arrayList, HALF_THE_LIST);
}
}
}
#endif
/* main.c */
#include"list.h"
int main(void){
List *linkedList = (List *)NULL;
linkedList = createList((List *)NULL, CREATE_NEW_LIST);
//List *arrayList = createList(CREATE_NEW_LIST);
if (linkedList == (List *)NULL){
fprintf(stderr, "Unable to createList() \n");
exit(1); //Nothing else to do without linkedList
}
insertItem(linkedList, 1, 0);
}
编译为
> gcc -DLINKED_LIST main.c arrayImpl.c linkedListImpl.c
封装 是关于维护ADT的不变量
List
以下不变量适用于
List
sentinel
。(1)对于任何
List d
,d.head != NULL
。 (总有一个哨兵。)(2)对于任何
DListNode x
,x.next != NULL
。(3)对于任何
DListNode x
,x.prev != NULL
。(4)对于任何
DListNode x
,如果x.next == y
,则为y.prev == x
。(5)对于任何
DListNode x
,如果x.prev == y
,则为y.next == x
。(6)
List
的{{1}}成员是size
的数量,不计算DListNode
(由&#34指出; head&#34;),可以通过一系列`下一个引用从sentinel
访问。抽象 是表示和使用之间的障碍(用户界面)
问题:
通过sentinel
/ O(1)
用户界面向用户(DListNode
)提供insertItem()
节点访问权限,可以进行{p> deleteItem()
插入和删除。但是,由于用户在main.c
类型节点上出现故障,封装将被破坏。如何使用链接列表强制执行DListNode
?