zoukankan      html  css  js  c++  java
  • 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);
        }
    }
    

      

  • 相关阅读:
    Trie Tree和Radix Tree
    DataNode Layout升级解决Du操作引发的性能问题
    Write-Ahead Log(WAL)的工作原理
    YARN的共享存储服务
    AWS S3存储基于Hadoop之上的一致性保证
    简单聊聊HDFS RBF第二阶段工作近期的一些进展
    基于 Confluence 6 数据中心的 SAML 单点登录设置你的身份提供者
    基于 Confluence 6 数据中心的 SAML 单点登录设置 SSL/TLS
    Confluence 6 基于 Confluence 数据中心的 SAML 单点登录
    Confluence 6 用自带的用户管理
  • 原文地址:https://www.cnblogs.com/tsembrace/p/3185892.html
Copyright © 2011-2022 走看看