抽象數據類型(ADT)
理論基礎
定義新的類型
什麼是類型?類型特指兩類信息:屬性和操作。
假設要定義一個新的數據類型:
- 首先,必須提供存儲數據的方法,例如設計一個結構。
- 其次,必須提供操控數據的方法。
計算機科學領域已開發了一種定義新類型的好方法,用3個步驟完成抽象到具體的過程。
- 提供類型屬性和相關操作的抽象描述。這些描述既不能依賴特定的實現,也不能依賴特定的編程語言。這種正式的抽象描述被稱爲抽象數據類型(ADT)。
- 開發一個實現ADT的編程接口。也就是說,指明如何儲存數據和執行所需操作的函數。例如在C中,可以提供結構定義和操控該結構的函數原型。這些作用於用戶定義類型的函數相當於作用於C基本類型的內置運算符。需要使用該新類型的程序員可以使用這個接口進行編程。
- 編寫代碼實現接口。這一步至關重要,但是使用該新類型的程序員無需瞭解具體的實現細節。
實現過程
建立抽象
以簡單電影項目爲例,所需的是一個鏈表數據類型,每一項包含電影名和評級。所需的操作是把新項添加到鏈表的末尾和顯示鏈表中的內容。
鏈表的屬性:
- 能儲存一系列的項,並按照一定方式排列;
- 鏈表類型應該提供一些有用的操作。
鏈表一些有用的操作:
- 初始化一個空鏈表;
- 在鏈表末尾添加一個新項;
- 確定鏈表是否爲空;
- 確定鏈表是否已滿;
- 確定鏈表的項數;
- 訪問鏈表的每一項執行某些操作,如顯示該項。
對於該電影項目而言,暫時不需要其他操作。但是一般的鏈表還應包含以下操作:
- 在鏈表的任意位置插入一個項;
- 移除鏈表中的一個項;
- 在鏈表中檢索一個項(不改變鏈表);
- 用另一個項替換鏈表中的一個項。
最後該類型總結如下:
建立接口
list.h接口頭文件:
/* list.h -- header file for a simple list type */
#ifndef LIST_H_
#define LIST_H_
#include <stdbool.h>
/* 特定程序的聲明 */
#define TSIZE 45 //儲存電影名的數組大小
struct film
{
char title[TSIZE];
int rating;
};
/* 一般類型定義 */
typedef struct film Item;
typedef struct node
{
Item item;
struct node * next;
} Node;
typedef Node * List; //List是指向Node類型的指針
/* 函數原型 */
void InitializeList(List * plist); //plist是指向List類型的指針
bool ListIsEmpty(const List *plist);
bool ListIsFull(const List *plist);
unsigned int ListItemCount(const List *plist);
bool AddItem(Item item, List * plist);
void Traverse (const List *plist, void (* pfun)(Item item) );//pfun是指向無返回值、參數爲Item類型的函數的指針
void EmptyTheList(List * plist);
#endif
使用接口
使用這個接口編寫程序,但是不必知道具體細節。
僞代碼方案:
創建一個List類型的變量。
創建一個Item類型的變量。
初始化鏈表爲空。
當鏈表未滿且有輸入時:
把輸入讀取到Item類型的變量中。
在鏈表末尾添加項。
訪問鏈表中的每個項並顯示他們。
films.c使用ADT風格的鏈表
#include<stdio.h>
#include<stdlib.h> //提供exit()的原型
#include"list.h" //定義Item、List
void showmovies(Item item);
char * s_gets(char *st, int n);
int main(void)
{
List movies;
int count;
Item temp;
/* 初始化 */
InitializeList(&movies);
if (ListIsFull(&movies))
{
fprintf(stderr, "No memory available!\n");
exit(1);
}
/* 獲取用戶輸入並儲存 */
puts("Enter first moive title");
while (s_gets(temp.title, TSIZE) != NULL && temp.title[0] != '\0')
{
puts("Enter your rating <1-10>:");
scanf("%d", &temp.rating);
while (getchar() != '\n')
continue;
if (AddItem(temp,&movies) == false)
{
fprintf(stderr,"Problem allocating memory.\n");
break;
}
if (ListIsFull(&movies))
{
fprintf(stderr, "The list is now full.\n");
break;
}
puts("Enter the next title:");
}
/* 顯示 */
if (ListIsEmpty(&movies))
printf("No data entered.\n");
else
{
printf("Here is the moive list:\n");
Traverse(&movies, showmovies);
}
printf("You entered %d moives.\n", ListItemCount(&movies));
/* 清理 */
EmptyTheList(&movies);
puts("88");
return 0;
}
void showmovies(Item item)
{
printf("Moive: %s Rating: %d\n",item.title, item.rating);
}
char * s_gets(char *st, int n)
{
char *ret_val;
char *find;
ret_val = fgets(st, n, stdin);
if (ret_val)
{
find = strchr(st,'\n');
if (find)
*find = '\0';
else
while (getchar() != '\n')
continue;
}
return ret_val;
}
實現接口
list.c實現文件
//支持鏈表操作的函數
#include<stdio.h>
#include<stdlib.h>
#include"list.h"
//局部函數原型
static void CopyToNode(Item item, Node * pnode);//內部鏈接,只在文件內生效
//接口函數
//把鏈表設置爲空
void InitializeList(List * plist)//plist是指向List的指針,*plist是指向Node的指針
{
*plist = NULL;
}
bool ListIsEmpty(const List * plist)
{
if (*plist == NULL)
return true;
else
return false;
}
bool ListIsFull(const List * plist)
{
Node * pt;
bool full;
pt = (Node *)malloc(sizeof(Node));
if (pt == NULL)
full = true;
else
full = false;
free(pt);
return full;
}
unsigned int ListItemCount(const List * plist)
{
unsigned int count = 0;
Node * pnode = *plist;
while (pnode != NULL)
{
++count;
pnode = pnode->next;
}
return count;
}
bool AddItem(Item item, List * plist)
{
Node * pnew;
Node * scan = *plist;
pnew = (Node *)malloc(sizeof(Node));
if(pnew == NULL)
return false;
CopyToNode(item, pnew);
pnew->next = NULL;
if (scan == NULL)
*plist = pnew;
else
{
while(scan->next != NULL)
scan = scan->next;
scan->next = pnew;
}
return true;
}
//訪問每個節點並執行pfun指向的函數
void Traverse(const List * plist, void (*pfun)(Item item))
{
Node * pnode = *plist;
while (pnode != NULL)
{
(*pfun)(pnode->item);
pnode = pnode->next;
}
}
void EmptyTheList(List * plist)
{
Node * psave;
while (*plist != NULL)
{
psave = (*plist)->next;
free(*plist);
*plist = psave;
}
}
static void CopyToNode(Item item, Node * pnode)
{
pnode->item = item;
}