c语言 数据结构 list、queue、tree抽象数据类型的定义与实现 详尽代码和注释
本文使用c語言定義并實(shí)現(xiàn)list、queue、tree抽象數(shù)據(jù)類型,代碼有詳盡注釋,可以通過代碼熟悉原理并運(yùn)用數(shù)據(jù)結(jié)構(gòu)。
0.ADT基礎(chǔ)知識(shí)
類型包括兩類信息,屬性和操作。在編程時(shí),根據(jù)編程問題匹配合適的數(shù)據(jù)類型。
定義一個(gè)新的數(shù)據(jù)類型,有三個(gè)步驟:
1.list(鏈表)的實(shí)現(xiàn)
根據(jù)三個(gè)步驟,定義一個(gè)list:
1.建立抽象:
- 類型名:簡單鏈表。
- 類型屬性:可存儲(chǔ)一系列項(xiàng)。
- 類型操作:初始化鏈表為空、確定鏈表為空、確定鏈表已滿、確定鏈表中的項(xiàng)數(shù)、在鏈表末尾添加項(xiàng)、遍歷鏈表,處理鏈表中的項(xiàng)、清空鏈表。
2.建立接口:見list.h
建立接口后,可以使用這個(gè)接口編寫程序,而不必知道具體實(shí)現(xiàn)細(xì)節(jié),見film3.c
3.實(shí)現(xiàn)接口:見list.c
1.1 list.h
/* list.h -- header file for a simple list type */ #ifndef LIST_H_ #define LIST_H_ #include <stdbool.h> /* C99 feature *//* 特定程序的聲明 */#define TSIZE 45 /* 存儲(chǔ)電影名的數(shù)組大小 */ struct film {char title[TSIZE];int rating; };/* 一般類型定義 */typedef struct film Item; /* 如果以后需要其他數(shù)據(jù)形式的鏈表,*/ /* 可以重新定義Item類型,不必更改其余的接口定義 */typedef struct node {Item item;struct node * next; } Node;/* 每一個(gè)鏈節(jié)叫做節(jié)點(diǎn)(Node) */typedef Node * List; /* 指向鏈表開始處的指針,List作為該類型的指針名*//* 函數(shù)原型 */ /* 前提條件:調(diào)用該函數(shù)前應(yīng)具備的條件 */ /* 后置條件:執(zhí)行完該函數(shù)后的情況 *//* 操作: 初始化一個(gè)鏈表 */ /* 前提條件: plist 指向一個(gè)鏈表 */ /* 后置條件: 鏈表初始化為空 */ void InitializeList(List * plist); /* 該函數(shù)的參數(shù)是指向鏈表的指針,*/ /* 如果函數(shù)想要修改一個(gè)參數(shù),那么該參數(shù)的類型應(yīng)該是指向相應(yīng)類型的指針 */ /* 調(diào)用時(shí)InitializeList(&movies) *//* operation: 確定鏈表是否為空 */ /* plist 指向一個(gè)已初始化的鏈表 */ /* postconditions: 如果鏈表為空,函數(shù)返回true */ /* 否則返回false */ bool ListIsEmpty(const List *plist);/* operation: 確定鏈表是否已滿 */ /* plist points to an initialized list */ /* postconditions: function returns True if list is full */ /* and returns False otherwise */ bool ListIsFull(const List *plist);/* operation: 確定鏈表中的項(xiàng)數(shù) */ /* plist points to an initialized list */ /* postconditions: 函數(shù)返回鏈表中的項(xiàng)數(shù) */ unsigned int ListItemCount(const List *plist);/* operation: 在鏈表的末尾添加項(xiàng) */ /* preconditions: item 是一個(gè)待添加至鏈表的項(xiàng) */ /* plist 指向一個(gè)已初始化的鏈表 */ /* postconditions: 如果可以, 函數(shù)在鏈表末尾添加一個(gè)項(xiàng) */ /* 且返回True; 否則返回False */ bool AddItem(Item item, List * plist);/* operation: 把函數(shù)作用于鏈表中的每一項(xiàng) */ /* plist 指向一個(gè)已初始化的鏈表 */ /* pfun 指向一個(gè)函數(shù),該函數(shù)接受一個(gè) */ /* Item類型的參數(shù),且無返回值 */ /* postcondition: pfun指向的函數(shù)作用于鏈表中的每個(gè)項(xiàng)一次 */ void Traverse (const List *plist, void (* pfun)(Item item) );/* operation: 釋放已分配的內(nèi)存(如果有的話) */ /* plist points to an initialized list */ /* postconditions: 釋放了為鏈表分配的所有內(nèi)存 */ /* 鏈表設(shè)置為空 */ void EmptyTheList(List * plist);#endif1.2 list.c
/* list.c -- 支持鏈表操作的函數(shù) */ #include <stdio.h> #include <stdlib.h> #include "list.h"/* 局部函數(shù)原型 */ /* 該函數(shù)是實(shí)現(xiàn)的一部分,不是接口的一部分,*/ /* 使用static存儲(chǔ)類別說明符將其隱藏在list.c文件夾中 */ static void CopyToNode(Item item, Node * pnode);/* 接口函數(shù) */ /* 把鏈表設(shè)置為空 */ void InitializeList(List * plist) {* plist = NULL; }/* 如果鏈表為空,返回true */ bool ListIsEmpty(const List * plist) {if (*plist == NULL)return true;elsereturn false; }/* 如果鏈表已滿,返回True */ bool ListIsFull(const List * plist) {Node * pt;bool full;/* 添加一個(gè)節(jié)點(diǎn),分配一次內(nèi)存 */pt = (Node *) malloc(sizeof(Node));if (pt == NULL)/* 系統(tǒng)把內(nèi)存分配完了,此時(shí)表示鏈表滿了 */full = true;elsefull = false;free(pt);return full; }/* 返回節(jié)點(diǎn)的數(shù)量 */ unsigned int ListItemCount(const List * plist) {unsigned int count = 0;Node * pnode = *plist; /*設(shè)置鏈表的開始 */while (pnode != NULL){++count;pnode = pnode->next; /* 設(shè)置下一個(gè)節(jié)點(diǎn) */}return count; }/* 創(chuàng)建存儲(chǔ)項(xiàng)的節(jié)點(diǎn) */ /* 并將其添加至由plist指向的鏈表末尾 */ bool AddItem(Item item, List * plist) {Node * pnew;Node * scan = *plist;pnew = (Node *) malloc(sizeof(Node));if (pnew == NULL)return false; /* 失敗時(shí)退出函數(shù) */CopyToNode(item, pnew);pnew->next = NULL;if (scan == NULL) /* 空鏈表, 所以把 */*plist = pnew; /* pnew 放在鏈表的開頭 */else{while (scan->next != NULL)scan = scan->next; /* 找到鏈表的末尾 */scan->next = pnew; /* 把pnew添加到鏈表的末尾 */}return true; }/* 訪問每個(gè)節(jié)點(diǎn)并執(zhí)行pfun指向的函數(shù) */ void Traverse (const List * plist, void (* pfun)(Item item) ) {Node * pnode = *plist; /* 設(shè)置鏈表的開始 */while (pnode != NULL){(*pfun)(pnode->item); /* 把函數(shù)應(yīng)用于鏈表中的項(xiàng) */pnode = pnode->next; /* 前進(jìn)到下一項(xiàng) */} }/* 釋放由malloc()分配的內(nèi)存 */ /* 設(shè)置鏈表指針為 NULL */ void EmptyTheList(List * plist) {Node * psave;while (*plist != NULL){psave = (*plist)->next; /* 保存下一個(gè)節(jié)點(diǎn)的地址 */free(*plist); /* 釋放當(dāng)前節(jié)點(diǎn) */*plist = psave; /* 前進(jìn)至下一個(gè)節(jié)點(diǎn) */} }/* 局部函數(shù)定義 */ /* 把一個(gè)項(xiàng)拷貝到節(jié)點(diǎn)中 */ static void CopyToNode(Item item, Node * pnode) {pnode->item = item; /* 拷貝結(jié)構(gòu) */ }1.3 film3.c
使用接口編寫程序,但是不必知道具體的實(shí)現(xiàn)細(xì)節(jié)。
/* films3.c -- using an ADT-style linked list */ /* compile with list.c */ #include <stdio.h> #include <stdlib.h> /* prototype for exit() */ #include "list.h" /* defines List, Item */ void showmovies(Item item); char * s_gets(char * st, int n); int main(void) {List movies;Item temp;/* 初始化 */InitializeList(&movies);if (ListIsFull(&movies)){fprintf(stderr,"No memory available! Bye!\n");exit(1);}/* 獲取用戶輸入并存儲(chǔ) */puts("Enter first movie title:");while (s_gets(temp.title, TSIZE) != NULL && temp.title[0] != '\0'){puts("Enter your rating <0-10>:");scanf("%d", &temp.rating);while(getchar() != '\n')continue;if (AddItem(temp, &movies)==false){fprintf(stderr,"Problem allocating memory\n");break;}if (ListIsFull(&movies)){puts("The list is now full.");break;}puts("Enter next movie title (empty line to stop):");}/* 顯示 */if (ListIsEmpty(&movies))printf("No data entered. ");else{printf ("Here is the movie list:\n");Traverse(&movies, showmovies);}printf("You entered %d movies.\n", ListItemCount(&movies));/* 清理 */EmptyTheList(&movies);printf("Bye!\n");return 0; }void showmovies(Item item) {printf("Movie: %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) // 如果地址不是NULL,*find = '\0'; // 在此處放置一個(gè)空字符elsewhile (getchar() != '\n')continue; // 處理輸入行的剩余內(nèi)容}return ret_val; }2.queue(隊(duì)列)的實(shí)現(xiàn)
隊(duì)列是具有兩個(gè)特殊屬性的鏈表,1.新項(xiàng)只能添加到鏈表的末尾,2.只能從鏈表的開頭移除項(xiàng)。 先進(jìn)先出的數(shù)據(jù)形式。
根據(jù)三個(gè)步驟,定義一個(gè)list:
1.建立抽象:
- 類型名:隊(duì)列。
- 類型屬性:可存儲(chǔ)一系列項(xiàng)。
- 類型操作:初始化隊(duì)列為空、確定隊(duì)列為空、確定隊(duì)列已滿、確定隊(duì)列中的項(xiàng)數(shù)、在隊(duì)列末尾添加項(xiàng)、在隊(duì)列開頭刪除或恢復(fù)項(xiàng)、清空隊(duì)列。
2.建立接口:見queue.h
3.實(shí)現(xiàn)接口:見queue.c
4.測(cè)試接口:見use_q.c
5.使用隊(duì)列:見mall.c
2.1 queue.h
/* queue.h -- interface for a queue */ #ifndef _QUEUE_H_ #define _QUEUE_H_ #include <stdbool.h>// 在這里插入Item類型的定義 // FOR EXAMPLE, //typedef int Item; // for use_q.c // OR typedef struct item {int gumption; int charisma;} Item; // OR (for mall.c) /**/typedef struct item{long arrive; // 一位顧客加入隊(duì)列的時(shí)間int processtime; // 該顧客咨詢時(shí)花費(fèi)的時(shí)間} Item; /**/#define MAXQUEUE 10typedef struct node {Item item;struct node * next; } Node;typedef struct queue {Node * front; /* 指向隊(duì)列首項(xiàng)的指針 */Node * rear; /* 指向隊(duì)列尾項(xiàng)的指針 */int items; /* 隊(duì)列中的項(xiàng)數(shù) */ } Queue;/* operation: 初始化隊(duì)列 */ /* 前提條件: pq 指向一個(gè)隊(duì)列 */ /* 后置條件: 隊(duì)列被初始化為空 */ void InitializeQueue(Queue * pq);/* operation: 檢查隊(duì)列是否已滿 */ /* precondition: pq 指向之前被初始化的隊(duì)列 */ /* postcondition: 如果隊(duì)列已滿則返回true,否則返回false */ bool QueueIsFull(const Queue * pq);/* operation: 檢查隊(duì)列是否為空 */ /* precondition: pq 指向之前被初始化的隊(duì)列 */ /* postcondition: 如果隊(duì)列為空則返回true,否則返回false */ bool QueueIsEmpty(const Queue *pq);/* operation: 確定隊(duì)列中的項(xiàng)數(shù) */ /* precondition: pq points to previously initialized queue */ /* postcondition: 返回隊(duì)列中的項(xiàng)數(shù) */ int QueueItemCount(const Queue * pq);/* operation: 在隊(duì)列末尾添加項(xiàng) */ /* precondition: pq 指向之前被初始化的隊(duì)列 */ /* item是要被添加在隊(duì)列末尾的項(xiàng) */ /* postcondition: 如果隊(duì)列不為空,item將被添加在隊(duì)列的末尾, */ /* 函數(shù)返回true; */ bool EnQueue(Item item, Queue * pq);/* operation: 從隊(duì)列的開頭刪除項(xiàng) */ /* precondition: pq 指向之前被初始化的隊(duì)列 */ /* postcondition: 如果隊(duì)列不為空, */ /* 隊(duì)列首端的item將被拷貝到*pitem中, */ /* 并被刪除,且函數(shù)返回True */ /* 如果該操作使隊(duì)列為空, */ /* 則重置隊(duì)列為空 */ /* 如果隊(duì)列在操作前為空, */ /* 該函數(shù)返回false */ bool DeQueue(Item *pitem, Queue * pq);/* operation: 清空隊(duì)列 */ /* precondition: pq指向之前被初始化的隊(duì)列 */ /* postconditions: 隊(duì)列被清空 */ void EmptyTheQueue(Queue * pq);#endif2.2 queue.c
/* queue.c -- the Queue type implementation*/ #include <stdio.h> #include <stdlib.h> #include "queue.h"/* local functions */ static void CopyToNode(Item item, Node * pn); static void CopyToItem(Node * pn, Item * pi);void InitializeQueue(Queue * pq) {pq->front = pq->rear = NULL;pq->items = 0; }bool QueueIsFull(const Queue * pq) {return pq->items == MAXQUEUE; }bool QueueIsEmpty(const Queue * pq) {return pq->items == 0; }int QueueItemCount(const Queue * pq) {return pq->items; }bool EnQueue(Item item, Queue * pq) {Node * pnew;if (QueueIsFull(pq))return false;pnew = (Node *) malloc( sizeof(Node));if (pnew == NULL)/*如果函數(shù)不能為節(jié)點(diǎn)分配內(nèi)存,程序運(yùn)行內(nèi)存不足*/{fprintf(stderr,"Unable to allocate memory!\n");exit(1);}CopyToNode(item, pnew);pnew->next = NULL;if (QueueIsEmpty(pq))pq->front = pnew; /* 項(xiàng)位于隊(duì)列首端 */elsepq->rear->next = pnew; /* 鏈接到隊(duì)列尾端 */pq->rear = pnew; /* 記錄隊(duì)列尾端位置 */pq->items++; /* 隊(duì)列項(xiàng)數(shù)加1 */return true; }bool DeQueue(Item * pitem, Queue * pq) {Node * pt;if (QueueIsEmpty(pq))return false;CopyToItem(pq->front, pitem);pt = pq->front;pq->front = pq->front->next;/*重置首指針指向隊(duì)列中的下一個(gè)項(xiàng)*/free(pt);/*釋放空出的節(jié)點(diǎn)使用的內(nèi)存空間*/pq->items--;if (pq->items == 0)//如果刪除的是最后一項(xiàng)pq->rear = NULL;//首指針和尾指針都置為NULLreturn true; }/* 清空隊(duì)列 */ void EmptyTheQueue(Queue * pq) {Item dummy;while (!QueueIsEmpty(pq))DeQueue(&dummy, pq); }/* Local functions */static void CopyToNode(Item item, Node * pn) {pn->item = item; }static void CopyToItem(Node * pn, Item * pi) {*pi = pn->item; }2.3 use_q.c
/* use_q.c -- driver testing the Queue interface */ /* compile with queue.c */ #include <stdio.h> #include "queue.h" /* defines Queue, Item */int main(void) {Queue line;Item temp;char ch;InitializeQueue(&line);puts("Testing the Queue interface. Type a to add a value,");puts("type d to delete a value, and type q to quit.");while ((ch = getchar()) != 'q'){if (ch != 'a' && ch != 'd') /* ignore other input */continue;if ( ch == 'a'){printf("Integer to add: ");scanf("%d", &temp);if (!QueueIsFull(&line)){printf("Putting %d into queue\n", temp);EnQueue(temp,&line);}elseputs("Queue is full!");}else{if (QueueIsEmpty(&line))puts("Nothing to delete!");else{DeQueue(&temp,&line);printf("Removing %d from queue\n", temp);}}printf("%d items in queue\n", QueueItemCount(&line));puts("Type a to add, d to delete, q to quit:");}EmptyTheQueue(&line);puts("Bye!");return 0; }2.4 mall.c
有一個(gè)提供快樂的攤位,顧客可以購買1分鐘、2分鐘或3分鐘的快樂。為確保交通順暢,每個(gè)攤位前排隊(duì)等待的顧客最多為10人。假設(shè)顧客隨機(jī)出現(xiàn),并且他們花費(fèi)的時(shí)間也是隨機(jī)選擇的(1\2\3分鐘),那么該攤位平均每小時(shí)接待多少名顧客?每位顧客平均花多長時(shí)間?排隊(duì)等待的顧客平均有幾人?
// mall.c -- use the Queue interface // compile with queue.c #include <stdio.h> #include <stdlib.h> // for rand() and srand() #include <time.h> // for time() #include "queue.h" // change Item typedef #define MIN_PER_HR 60.0bool newcustomer(double x); // 是否有新顧客來? Item customertime(long when); // 設(shè)置顧客參數(shù)int main(void) {Queue line;Item temp; // 新的顧客數(shù)據(jù)int hours; // 模擬的小時(shí)數(shù)int perhour; // 每小時(shí)平均多少位顧客long cycle, cyclelimit; // 循環(huán)計(jì)數(shù)器、 計(jì)數(shù)器的上限long turnaways = 0; // 因隊(duì)列已滿被拒的顧客數(shù)量long customers = 0; // 加入隊(duì)列的顧客數(shù)量long served = 0; // 在模擬期間到訪過攤位的顧客數(shù)量long sum_line = 0; // 累計(jì)的隊(duì)列總長int wait_time = 0; // 從當(dāng)前到攤位空閑所需時(shí)間double min_per_cust; // 顧客到來的平均時(shí)間long line_wait = 0; // 隊(duì)列累計(jì)的等待時(shí)間 InitializeQueue(&line);srand((unsigned int) time(0)); // rand()隨機(jī)初始化puts("Case Study: Sigmund Lander's Advice Booth");puts("Enter the number of simulation hours:");scanf("%d", &hours);cyclelimit = MIN_PER_HR * hours;puts("Enter the average number of customers per hour:");scanf("%d", &perhour);min_per_cust = MIN_PER_HR / perhour;for (cycle = 0; cycle < cyclelimit; cycle++)//每次迭代對(duì)應(yīng)一分鐘的行為{if (newcustomer(min_per_cust))//如果1分鐘內(nèi)有顧客到來{if (QueueIsFull(&line))turnaways++;//被拒的顧客數(shù)量else{customers++;temp = customertime(cycle);/*設(shè)置temp結(jié)構(gòu)中的arrive和processtime成員*/EnQueue(temp, &line);}}if (wait_time <= 0 && !QueueIsEmpty(&line)){//如果隊(duì)列不為空且前面的顧客沒有咨詢,則刪除隊(duì)列首端的項(xiàng)DeQueue (&temp, &line);wait_time = temp.processtime;//該顧客咨詢時(shí)花費(fèi)的時(shí)間line_wait += cycle - temp.arrive;//到目前為止,隊(duì)列中所有顧客的等待總時(shí)間served++;//咨詢過的顧客數(shù)量}if (wait_time > 0)//完成當(dāng)前顧客的需求所需的時(shí)間大于0wait_time--;sum_line += QueueItemCount(&line);//目前為止統(tǒng)計(jì)的隊(duì)列長度}if (customers > 0){printf("customers accepted: %ld\n", customers);printf(" customers served: %ld\n", served);printf(" turnaways: %ld\n", turnaways);printf("average queue size: %.2f\n",(double) sum_line / cyclelimit);printf(" average wait time: %.2f minutes\n",(double) line_wait / served);}elseputs("No customers!");EmptyTheQueue(&line);puts("Bye!");return 0; }// x = 顧客到來的平均時(shí)間(單位:分鐘) // 如果1分鐘內(nèi)有顧客到來,則返回true bool newcustomer(double x) {if (rand() * x / RAND_MAX < 1)return true;elsereturn false; }// when是顧客到來的時(shí)間 // 該函數(shù)返回一個(gè)Item結(jié)構(gòu),該顧客到達(dá)的時(shí)間設(shè)置成when // 咨詢時(shí)間設(shè)置為1 - 3的隨機(jī)值 Item customertime(long when) {Item cust;cust.processtime = rand() % 3 + 1;cust.arrive = when;return cust; }3.tree(二叉查找樹)的實(shí)現(xiàn)
訪問元素有兩種,隨機(jī)訪問:使用數(shù)組下標(biāo)直接訪問該數(shù)組中的任意元素;順序訪問:對(duì)于鏈表而言,必須從鏈表首節(jié)點(diǎn)開始,逐個(gè)節(jié)點(diǎn)移動(dòng)到要訪問的節(jié)點(diǎn)。
用數(shù)組可以實(shí)現(xiàn)二分查找,因?yàn)榭梢允褂脭?shù)組下標(biāo)確定數(shù)組中任意部分的中點(diǎn)。但是鏈表只支持順序訪問,不提供跳至中間節(jié)點(diǎn)的方法,所以鏈表中不能使用二分查找。
有一種數(shù)據(jù)形式的需求,既支持頻繁插入和刪除項(xiàng),又支持頻繁查找。此時(shí)需要二叉查找樹。
根據(jù)三個(gè)步驟,定義一個(gè)二叉樹:
該定義假設(shè)二叉樹不包含相同的項(xiàng)。
1.建立抽象:
類型名:二叉查找樹。
類型屬性:
- 二叉樹要么是空節(jié)點(diǎn)的集合(空樹),要么是有一個(gè)根節(jié)點(diǎn)的節(jié)點(diǎn)集合。
- 每個(gè)節(jié)點(diǎn)都有兩個(gè)子樹,叫做左子樹和右子樹
- 每個(gè)子樹本身也是一個(gè)二叉樹,也可能是空樹
- 二叉查找樹是一個(gè)有序的二叉樹,每個(gè)節(jié)點(diǎn)包含一個(gè)項(xiàng)。
- 左子樹的所有項(xiàng)都在根節(jié)點(diǎn)項(xiàng)的前面,右子樹所有項(xiàng)都在根節(jié)點(diǎn)項(xiàng)的后面
類型操作:初始化樹為空、確定樹是否為空、確定樹是否已滿、確定樹中的項(xiàng)數(shù)、在樹中添加一個(gè)項(xiàng)、在樹中刪除一個(gè)項(xiàng)、在樹中查找一個(gè)項(xiàng)、在樹種訪問一個(gè)項(xiàng)、清空樹。
2.建立接口:見tree.h
3.實(shí)現(xiàn)接口:見tree.c
4.使用二叉樹:見petclub.c
3.1 tree.h
/* tree.h -- binary search tree 二叉查找樹 */ /* 樹中不允許有重復(fù)的項(xiàng) */ #ifndef _TREE_H_ #define _TREE_H_ #include <stdbool.h>/* 根據(jù)情況重新定義Item */ #define SLEN 20 typedef struct item {char petname[SLEN];char petkind[SLEN]; } Item;#define MAXITEMS 10typedef struct trnode {Item item;struct trnode * left; /* 指向左分支的指針 */struct trnode * right; /* 指向右分支的指針 */ } Trnode;typedef struct tree {Trnode * root; /* 指向根節(jié)點(diǎn)的指針 */int size; /* 樹的項(xiàng)數(shù) */ } Tree;/* 函數(shù)原型 *//* 操作: 把樹初始化為空 */ /* 前提條件: ptree 指向一個(gè)樹 */ /* 后置條件: 樹被初始化為空 */ void InitializeTree(Tree * ptree);/* operation: 確定樹是否為空 */ /* preconditions: ptree points to a tree */ /* postconditions: 如果樹為空,函數(shù)返回true */ /* 否則返回false */ bool TreeIsEmpty(const Tree * ptree);/* operation: 確定樹是否已滿 */ /* preconditions: ptree points to a tree */ /* postconditions: function returns true if tree is */ /* full and returns false otherwise */ bool TreeIsFull(const Tree * ptree);/* operation: 確定樹的項(xiàng)數(shù) */ /* preconditions: ptree points to a tree */ /* postconditions: 返回樹的項(xiàng)數(shù) */ int TreeItemCount(const Tree * ptree);/* operation: 在樹中添加一個(gè)項(xiàng) */ /* preconditions: pi 是待添加項(xiàng)的地址 */ /* ptree 指向一個(gè)已初始化的樹 */ /* postconditions: 如果可以添加,該函數(shù)將在樹中添加一個(gè)項(xiàng) */ /* 并返回true,否則返回false */ bool AddItem(const Item * pi, Tree * ptree);/* operation: 在樹中查找一個(gè)項(xiàng) */ /* preconditions: pi指向一個(gè)項(xiàng) */ /* ptree 指向一個(gè)已初始化的樹 */ /* postconditions: 如果在樹中找到指定項(xiàng),該函數(shù)返回true */ /* 否則返回false */ bool InTree(const Item * pi, const Tree * ptree);/* operation: 從樹中刪除一個(gè)項(xiàng) */ /* preconditions: pi 是刪除項(xiàng)的地址 */ /* ptree 指向一個(gè)已初始化的樹 */ /* postconditions: 如果在樹中成功刪除一個(gè)項(xiàng),該函數(shù)返回true */ /* 否則返回false*/ bool DeleteItem(const Item * pi, Tree * ptree);/* operation: 把函數(shù)應(yīng)用于樹中的每一項(xiàng) */ /* preconditions: ptree 指向一個(gè)樹 */ /* pfun 指向一個(gè)函數(shù) */ /* 該函數(shù)接受一個(gè)Item類型的參數(shù),并無返回值 */ /* postcondition: pfun指向的這個(gè)函數(shù)為樹中的每一項(xiàng)執(zhí)行一次 */ void Traverse (const Tree * ptree, void (* pfun)(Item item));/* operation: 刪除樹中的所有內(nèi)容 */ /* preconditions: ptree指向一個(gè)已初始化的樹 */ /* postconditions: 樹為空 */ void DeleteAll(Tree * ptree);#endif3.2 tree.c
/* tree.c -- tree support functions */ #include <string.h> #include <stdio.h> #include <stdlib.h> #include "tree.h"/* 局部數(shù)據(jù)類型 */ typedef struct pair {Trnode * parent;Trnode * child; } Pair;/* 局部函數(shù)的原型 */ static Trnode * MakeNode(const Item * pi); static bool ToLeft(const Item * i1, const Item * i2); static bool ToRight(const Item * i1, const Item * i2); static void AddNode (Trnode * new_node, Trnode * root); static void InOrder(const Trnode * root, void (* pfun)(Item item)); static Pair SeekItem(const Item * pi, const Tree * ptree); static void DeleteNode(Trnode **ptr); static void DeleteAllNodes(Trnode * ptr);/* 函數(shù)定義 */ void InitializeTree(Tree * ptree) {ptree->root = NULL;ptree->size = 0; }bool TreeIsEmpty(const Tree * ptree) {if (ptree->root == NULL)return true;elsereturn false; }bool TreeIsFull(const Tree * ptree) {if (ptree->size == MAXITEMS)return true;elsereturn false; }int TreeItemCount(const Tree * ptree) {return ptree->size; }bool AddItem(const Item * pi, Tree * ptree)/*在樹中添加一個(gè)項(xiàng)*/ {Trnode * new_node;if (TreeIsFull(ptree))/*首先檢查該樹是否有空間放下一個(gè)項(xiàng)*/{fprintf(stderr,"Tree is full\n");return false; /* 提前返回 */}if (SeekItem(pi, ptree).child != NULL){/*檢查該樹是否有該項(xiàng),因?yàn)橐蠖鏄渲械捻?xiàng)不能重復(fù)*/fprintf(stderr, "Attempted to add duplicate item\n");return false; /* 提前返回 */}new_node = MakeNode(pi); /* 指向新節(jié)點(diǎn) */if (new_node == NULL){fprintf(stderr, "Couldn't create node\n");return false; /* 提前返回 */}/* 成功創(chuàng)建了一個(gè)新節(jié)點(diǎn) */ptree->size++;/*找出應(yīng)該把這個(gè)新節(jié)點(diǎn)放在樹中哪個(gè)位置*/if (ptree->root == NULL) /* 情況 1: 樹為空 */ptree->root = new_node; /* 新節(jié)點(diǎn)為樹的根節(jié)點(diǎn) */else /* case 2: 樹不為空 */AddNode(new_node,ptree->root); /* 在樹中添加新節(jié)點(diǎn) */return true; /* 成功返回 */ }/*在樹中查找一個(gè)項(xiàng)*/ bool InTree(const Item * pi, const Tree * ptree) {/*SeekItem函數(shù)返回一個(gè)結(jié)構(gòu),該函數(shù)可以與結(jié)構(gòu)成員運(yùn)算符一起使用*/return (SeekItem(pi, ptree).child == NULL) ? false : true; }bool DeleteItem(const Item * pi, Tree * ptree) {Pair look;/*SeekItem返回一個(gè)結(jié)構(gòu),*//*內(nèi)含兩個(gè)指針,一個(gè)指針look.parent指向父節(jié)點(diǎn),*//*一個(gè)指針look.child指向包含特定項(xiàng)的節(jié)點(diǎn)*/look = SeekItem(pi, ptree);if (look.child == NULL)/*表明未找到指定項(xiàng)*/return false;/*找到了指定項(xiàng),分三種情況處理*//*第一種情況,該項(xiàng)在根節(jié)點(diǎn)中*/if (look.parent == NULL) /* 刪除根節(jié)點(diǎn)項(xiàng) */DeleteNode(&ptree->root);/*第二種情況,待刪除節(jié)點(diǎn)是父節(jié)點(diǎn)的左子節(jié)點(diǎn)*/else if (look.parent->left == look.child)DeleteNode(&look.parent->left);/*第三種情況,待刪除節(jié)點(diǎn)是父節(jié)點(diǎn)的右子節(jié)點(diǎn)*/elseDeleteNode(&look.parent->right);ptree->size--;return true; }/*遍歷樹*/ void Traverse (const Tree * ptree, void (* pfun)(Item item)) {if (ptree != NULL)InOrder(ptree->root, pfun); }void DeleteAll(Tree * ptree) {if (ptree != NULL)DeleteAllNodes(ptree->root);/*釋放內(nèi)存*//*處理Tree類型的結(jié)構(gòu),重置Tree類型結(jié)構(gòu)成員,表明該樹為空*/ptree->root = NULL;ptree->size = 0; }/* local functions */ /*可以有不同的處理順序*/ static void InOrder(const Trnode * root, void (* pfun)(Item item)) {if (root != NULL){/*處理左子樹、處理項(xiàng)、處理右子樹*/InOrder(root->left, pfun);(*pfun)(root->item);InOrder(root->right, pfun);} }static void DeleteAllNodes(Trnode * root) {Trnode * pright;if (root != NULL){pright = root->right;/*存儲(chǔ)root->right,使其在釋放根節(jié)點(diǎn)后仍然可用*/DeleteAllNodes(root->left);free(root);DeleteAllNodes(pright);} }/*確定新節(jié)點(diǎn)的位置,添加新節(jié)點(diǎn)*/ static void AddNode (Trnode * new_node, Trnode * root) {/*如果新項(xiàng)應(yīng)放在左子樹中,ToLeft()函數(shù)返回true*/if (ToLeft(&new_node->item, &root->item)){if (root->left == NULL) /* 空子樹 */root->left = new_node; /* 把節(jié)點(diǎn)添加到此處 */else/*把新項(xiàng)和左子節(jié)點(diǎn)中的項(xiàng)作比較,*//*以確定新項(xiàng)該放在該子節(jié)點(diǎn)的左子樹還是右子樹*/AddNode(new_node, root->left);/* 否則處理該子樹*/}/*如果新項(xiàng)應(yīng)放在右子樹中,ToRight()函數(shù)返回true*/else if (ToRight(&new_node->item, &root->item)){if (root->right == NULL)root->right = new_node;elseAddNode(new_node, root->right);}else /* 不允許有重復(fù)項(xiàng) */{fprintf(stderr,"location error in AddNode()\n");exit(1);} }/*ToLeft和ToRight函數(shù)依賴于Item類型的性質(zhì)。*/ /*成員名按字母排序,如果名字相同,按其種類排序*/ /*如果種類相同,屬于重復(fù)項(xiàng)*/ static bool ToLeft(const Item * i1, const Item * i2) {int comp1;/*第一個(gè)字符串在第二個(gè)字符串前面,strcmp返回負(fù)數(shù)*//*兩個(gè)字符串相同,strcmp返回0*/if ((comp1 = strcmp(i1->petname, i2->petname)) < 0)return true;else if (comp1 == 0 &&strcmp(i1->petkind, i2->petkind) < 0 )return true;elsereturn false; }static bool ToRight(const Item * i1, const Item * i2) {int comp1;if ((comp1 = strcmp(i1->petname, i2->petname)) > 0)return true;else if (comp1 == 0 &&strcmp(i1->petkind, i2->petkind) > 0 )return true;elsereturn false; }/*處理動(dòng)態(tài)內(nèi)存分配和初始化節(jié)點(diǎn)*/ /*參數(shù)是指向新項(xiàng)的指針,返回值是指向新節(jié)點(diǎn)的指針*/ static Trnode * MakeNode(const Item * pi) {Trnode * new_node;new_node = (Trnode *) malloc(sizeof(Trnode));if (new_node != NULL){new_node->item = *pi;new_node->left = NULL;new_node->right = NULL;}return new_node; }/*查找特定項(xiàng)*/ /*返回的結(jié)構(gòu)包含兩個(gè)指針,*/ /*一個(gè)指針指向包含項(xiàng)的節(jié)點(diǎn),如果未找到指定項(xiàng)則為NULL*/ /*一個(gè)指針指向父節(jié)點(diǎn)(如果該節(jié)點(diǎn)為根節(jié)點(diǎn),沒有父節(jié)點(diǎn),則為NULL)*/ static Pair SeekItem(const Item * pi, const Tree * ptree) {Pair look;look.parent = NULL;/*開始時(shí),設(shè)置look.child指向樹的根節(jié)點(diǎn)*/look.child = ptree->root;if (look.child == NULL)return look; /* 提前返回 */while (look.child != NULL){if (ToLeft(pi, &(look.child->item))){look.parent = look.child;look.child = look.child->left;//如果沒有找到匹配的項(xiàng),look.child將被設(shè)置為NULL}else if (ToRight(pi, &(look.child->item))){look.parent = look.child;look.child = look.child->right;}else /* 如果前兩種情況都不滿足,則必定是相等的情況 */break; /* look.child 目標(biāo)項(xiàng)的節(jié)點(diǎn) */}return look; /* 成功返回 */ }static void DeleteNode(Trnode **ptr) /* ptr是指向待刪除節(jié)點(diǎn)指針的地址 */ /*那么*ptr就是把ptr地址里面的值取出來,就是指向待刪除節(jié)點(diǎn)的指針*/ /*要修改的指針本身是Trnode*類型,指向Trnode的指針*/ /*該函數(shù)的參數(shù)是該指針的地址,所以參數(shù)的類型是Trnode** */ {Trnode * temp;/*待刪除節(jié)點(diǎn)是沒有左子節(jié)點(diǎn)的節(jié)點(diǎn)*/if ( (*ptr)->left == NULL){temp = *ptr;*ptr = (*ptr)->right;free(temp);}/*沒有右子節(jié)點(diǎn)的節(jié)點(diǎn)*/else if ( (*ptr)->right == NULL){temp = *ptr;*ptr = (*ptr)->left;free(temp);}else /* 被刪除的節(jié)點(diǎn)有兩個(gè)子節(jié)點(diǎn) */{/* 找到重新連接右子樹的位置 *//*沿左子樹的右支查找到第1個(gè)空位*/for (temp = (*ptr)->left; temp->right != NULL;temp = temp->right)continue;/*把待刪除節(jié)點(diǎn)的右子樹與該空位連接*/temp->right = (*ptr)->right;/*處理完之后重置待刪除節(jié)點(diǎn)的地址*//*重置之前先將*ptr存儲(chǔ)在temp中,釋放待刪除節(jié)點(diǎn)的內(nèi)存*/temp = *ptr;*ptr =(*ptr)->left;free(temp); } }3.3 petclub.c
/* petclub.c -- use a binary search tree */ #include <stdio.h> #include <string.h> #include <ctype.h> #include "tree.h"char menu(void); void addpet(Tree * pt); void droppet(Tree * pt); void showpets(const Tree * pt); void findpet(const Tree * pt); void printitem(Item item); void uppercase(char * str); char * s_gets(char * st, int n);int main(void) {Tree pets;char choice;InitializeTree(&pets);while ((choice = menu()) != 'q'){switch (choice){case 'a' : addpet(&pets);break;case 'l' : showpets(&pets);break;case 'f' : findpet(&pets);break;case 'n' : printf("%d pets in club\n",TreeItemCount(&pets));break;case 'd' : droppet(&pets);break;default : puts("Switching error");}}DeleteAll(&pets);puts("Bye.");return 0; }char menu(void) {int ch;puts("Nerfville Pet Club Membership Program");puts("Enter the letter corresponding to your choice:");puts("a) add a pet l) show list of pets");puts("n) number of pets f) find pets");puts("d) delete a pet q) quit");while ((ch = getchar()) != EOF){while (getchar() != '\n') /* 處理輸入行的剩余內(nèi)容 */continue;ch = tolower(ch);if (strchr("alrfndq",ch) == NULL)puts("Please enter an a, l, f, n, d, or q:");elsebreak;}if (ch == EOF) /* 使程序退出 */ch = 'q';return ch; }void addpet(Tree * pt) {Item temp;if (TreeIsFull(pt))puts("No room in the club!");else{puts("Please enter name of pet:");s_gets(temp.petname,SLEN);puts("Please enter pet kind:");s_gets(temp.petkind,SLEN);uppercase(temp.petname);uppercase(temp.petkind);AddItem(&temp, pt);} }void showpets(const Tree * pt) {if (TreeIsEmpty(pt))puts("No entries!");elseTraverse(pt, printitem); }void printitem(Item item) {printf("Pet: %-19s Kind: %-19s\n", item.petname,item.petkind); }void findpet(const Tree * pt) {Item temp;if (TreeIsEmpty(pt)){puts("No entries!");return; /* quit function if tree is empty */}puts("Please enter name of pet you wish to find:");s_gets(temp.petname, SLEN);puts("Please enter pet kind:");s_gets(temp.petkind, SLEN);uppercase(temp.petname);uppercase(temp.petkind);printf("%s the %s ", temp.petname, temp.petkind);if (InTree(&temp, pt))printf("is a member.\n");elseprintf("is not a member.\n"); }void droppet(Tree * pt) {Item temp;if (TreeIsEmpty(pt)){puts("No entries!");return; /* quit function if tree is empty */}puts("Please enter name of pet you wish to delete:");s_gets(temp.petname, SLEN);puts("Please enter pet kind:");s_gets(temp.petkind, SLEN);uppercase(temp.petname);uppercase(temp.petkind);printf("%s the %s ", temp.petname, temp.petkind);if (DeleteItem(&temp, pt))printf("is dropped from the club.\n");elseprintf("is not a member.\n"); }void uppercase(char * str) {while (*str){*str = toupper(*str);str++;} } 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) // 如果地址不是 NULL,*find = '\0'; // 在此處放置一個(gè)空字符elsewhile (getchar() != '\n')continue; // 處理輸入行的剩余內(nèi)容}return ret_val; } 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的c语言 数据结构 list、queue、tree抽象数据类型的定义与实现 详尽代码和注释的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: freertos内核 任务定义与切换 原
- 下一篇: xshell6 不更新无法使用_世纪金花