C Primer+Plus(十七)高级数据表示(一)

第十七章 高级数据表示(一)

一、抽象数据类型

抽象数据类型(abstract data type:ADT)是指由用户依据实际需求所创建的某种数据类型,它可以是C语言中的任何数据类型,甚至是基本类型,或数组,复杂的就会用到结构。

为何说是抽象?是因为ADT并不会是固定某种数据类型,而是依据实际应用需求中提炼出来的某种数据类型的表达方式。

那么,如何定义一种ADT呢?

在C语言里,定义一种数据类型,包括两方面:一方面是数据存储方式的描述(对取值的限定),另外一方面是对该类型的可使用的操作的定义。

比如:定义一个int型数据a,则表示a取值范围是-32768~32767中的整数;同时a可以使用所有对整型数据的操作。

依据上述,创建一个ADT,包括以下两大方面:1、描述该ADT的存储方式;2、定义其可使用的操作。

具体可分为一下两个步骤:

1、定义数据类型和并描述该数据类型可进行的操作。

//假设ADT为typea
typedef something typea; //类型定义

//可使用的操作集定义

//操作1定义
//操作1描述
void f1(typea *a);

//操作2定义
//操作2描述
void f2(typea *a);

....

 

2、定义操作(编写代码实现)。

对于上例即是编写代码实现f1和f2函数功能。

 

二、利用ADT来实现一个关于电影列表的工程

1、数据的定义和描述

为简化代码,设置每个电影项目有两个成员:影片名称和打分。则对于每个项目的定义描述如下:

struct film
{
   char name[20];
   int rating;
};

接下来要做的工作是,如何将所有项目链接组合起来,或者叫“形成一个数据堆”?可以通过数组的方式:

struct film filmpro[100];

数组的实现有其优势:可随机访问,但也有其缺点:先前就限定了数据堆的空间大小,可能造成浪费也可能空间不足(数组空间的分配是在编译时候就进行了),同时若要向数组中间添加新项目,则会使其后面的数组元素都进行调整(除非数组非满且在最后添加)。

因此这里我们选用单链表方式去实现这个“数据堆”,首先描述这个“数据堆”中的每一个元素,成为“节点”:

struct node
{
   struct film filmpro;
   struct node *pnode;
};

接下来描述这个数据堆,该如何描述?本例中数据堆是一个单链表形式,那么要形容这个数据堆,必须描述其起始的位置,即首节点的地址。

struct node *list;

 当然,依据实际需求,也可以增加其他成员,比如增加描述这个数据堆的项目数量:

struct list
{
  struct node *plist;
  int node_number;
};

那么至此,一个完整对该电影工程的抽象数据定义如下:

struct film
{
   char name[20];
   int rating;
};
struct node
{
   struct film filmpro;
   struct node *pnode;
};
struct node *list;

这里要明确理解这三个定义之间的关系,首先film是依据实际应用提炼出的数据的定义表示;再次在这个数据定义表示之后,要用一种方式将所有的数据组合成一个“数据堆”,那要能表示这数据堆中每一个元素,而这时这每一个元素不仅仅是先前的film数据,它要能包含film,同时又能表示其与数据堆中其他元素的关系,即定义一个node数据;最后我要对这整个数据堆进行定义,这里定义描述选择了最简单的一个情况:即描述指向数据堆首节点的地址,因此表示数据堆的数据类型为node *型。

我们可以通过修改第一个定义,依据实际情况改变我们对提炼数据的定义;可以通过修改第二个node定义,修改我们组合数据的方式;可通过修改第三个list修改我们描述这数据堆的方式。

但上面的代码还不算最完善,我们可以使用typedef,同时通过有含义的类型名使其各部分定义更清晰一些。

typedef struct item
{
   char name[20];
   int rating;
}Item;
typedef struct node
{
   struct film filmpro;
   struct node *pnode;
}Node;
typedef Node *List;

 这里一定要清楚:List虽然定义上是指向Node的指针类型,但实质上按我们的需求,List是形容一个“数据堆”的,这里即是指向一个列表的,只不过我们通过指向列表首节点来描述它。

接下来,创建一个依据该ADT定义的电影列表的实例:

List movies;
//简直是简单而清爽啊

 

2、创建完这个电影实例的ADT模型List后,接下来考虑实际我们需要哪些操作?先去描述这些操作,再通过函数去实现他们。而且这些操作对于所有List型数据都应该是通用的。

(1)初始化List:列表通过描述首节点地址来描述整个列表,那么直接使其指向空,即可实现,因为该函数要改变列表,所以用指针传递:

void InitializeList(List *plist)
{
   *plist=NULL;
}

(2)判断列表是否为空:若列表指向首节点地址为空,则列表为空。这里仅是判断,不改变列表,因此不应该传递指针,而直接用列表值传递:

int ListIsEmpty(List list)
{
   if(list==NULL)
      return 1;
   else 
      return 0;
}

这里也可以使用const保护,来通过传递指针实现函数功能:

int ListIsEmpty(const List *plist)
{
    if(*plist==NULL)
       return 1;
    else return 0;
}

(3)判断列表是否为满:这里因为列表List的定义中只有首节点地址,而没有限定列表最大节点数目。因此这里判断列表是否为满,实质是判断系统可否为列表继续分配内存空间来存放其节点。所以这里函数也是不改变列表本身的,甚至是不涉及列表,因此下面代码其实也是可以不传参的。

int ListIsFull(const List *plist)
{
   Node *newnode;
   newnode=(Node*)malloc(sizeof(Node));
   if(newnode==NULL)   //malloc false
        return 1;
   else                //malloc succeed
return 0;
free(newnode); }

(4)计算列表中节点数量

int ListItemCount(const List *plist)
{
   Node *look;
   int n=0;
   look=*plist;
   while(look!=NULL)
   {
      look=look->next;
      n++;
   }
   return n;
}

(5)在列表尾部添加一个项目

int AddItem(Item item,List *plist)
{
   Node *look;
   Node *newnode;
   look=*plist;

  newnode=(Node*)malloc(sizeof(Node));
  if(newnode==NULL)
      return 0;
   while(look!=NULL)
      look=look->next;
   look=newnode;
   look->item=item;
   look->next=NULL;
   return 1;
}

(6)释放列表内存空间

void EmptyList(List *plist)
{
   Node *look;
   while((*plist)!=NULL)
   {
      look=*plist;
      *plist=*plist->next;
      free(look);
    }
}

  

posted @ 2013-07-12 12:51  tsembrace  阅读(523)  评论(0编辑  收藏  举报