列表 - 封装&表现 - 权衡

时间:2016-11-19 19:16:18

标签: c performance list linked-list encapsulation

下面是使用数组和循环双向链表实现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 dd.head != NULL。 (总有一个哨兵。)

         

(2)对于任何DListNode xx.next != NULL

         

(3)对于任何DListNode xx.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

0 个答案:

没有答案