c數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)案例
本文關(guān)鍵詞:數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì),由筆耕文化傳播整理發(fā)布。
我們?cè)O(shè)計(jì)的程序有三個(gè),分別是:航空訂票系統(tǒng)、24點(diǎn)游戲、旅游交通查詢系統(tǒng),為了用戶的方便和更能體現(xiàn)C語言的模塊化理念,我們把三個(gè)程序放到一個(gè)系統(tǒng)中去實(shí)現(xiàn)了。
1、需求分析:
在完成課程設(shè)計(jì)的過程中,我們組合作為主,歐陽錦林主要負(fù)責(zé)程序設(shè)計(jì)與調(diào)試,王峰和段靜緣主要負(fù)責(zé)資料收集與文檔輸入。設(shè)計(jì)完成后交流了各人收獲與體會(huì)。
(1)、航空訂票系統(tǒng):
通過此系統(tǒng)可以實(shí)現(xiàn)如下功能:
1) 錄入航線信息
每條航線信息包括航班號(hào)、飛機(jī)號(hào)、目的地、訂票數(shù)、余票數(shù)共5項(xiàng)。假設(shè)現(xiàn)在有3條航線, 目的地分別是北京, 上海, 廣州, 飛機(jī)上可乘坐100人( 即初始訂票數(shù)為0, 余票數(shù)為100) , 將這3條航線信息存入文件“airline.dat” 中。
2) 訂票業(yè)務(wù)
客戶信息包括姓名, 航班號(hào), 座位號(hào)(初始為0), 假設(shè)已有3個(gè)客戶信息存入文件“customer.dat”中。
有新客戶訂票時(shí), 先輸入客戶的姓名和他提出的航班號(hào), 查詢?cè)摵骄的訂票情況, 若有余票, 則為客戶辦理訂票手續(xù), 分配給客戶一個(gè)座位號(hào), 然后將新客戶的信息添加到文件“customer.dat”中, 并修改文件“airline.dat”中該航線的訂票數(shù)和余票數(shù)。若無余票, 則輸出客滿信息。進(jìn)一步可實(shí)現(xiàn)如果該航班已經(jīng)無票,可以提供相關(guān)可選擇航班信息。
3) 退票業(yè)務(wù)
根據(jù)客戶提出的航班號(hào), 辦理退票, 從文件“customer.dat”中刪除該客戶的信息, 并修改文件“airline.dat”中相應(yīng)航線的訂票數(shù)和余票數(shù)。
4) 修改航班信息:當(dāng)航班信息改變可以修改航班數(shù)據(jù)文件。
5) 輸出全部航線信息和全部客戶信息。
6) 退出系統(tǒng)。
。2)、24點(diǎn)游戲:
基本要求及步驟:
1) 隨機(jī)產(chǎn)生四個(gè)1-13的數(shù),分別代表13張牌。
2) 提示玩家輸入算式。
3) 判斷玩家輸入的表達(dá)式是否合法,其中算式中的四個(gè)數(shù)字只能是程序所給的四個(gè)數(shù)字,非法則回到1)。
4) 如果玩家認(rèn)為這四張牌算不出24點(diǎn)(如:1,1,1,1),可只輸入?,程序?qū)⑴袛噙@四張牌是否能得出24點(diǎn),如果能,則程序?qū)⒔o出算式,如果不能,說明不能,并回到1)。
5) 當(dāng)用戶正確輸入算式后,用“堆棧來求表達(dá)式的值”的原理 求出結(jié)果并判斷是否為24,得出用戶是輸是贏的結(jié)果。
6) 詢問用戶是否繼續(xù),是則回到1),否則結(jié)束程序。
。3)、旅游交通查詢系統(tǒng):
實(shí)現(xiàn)功能:火車信息查詢、最短路徑查詢、火車信息編輯、讀入修改信息、查看火車信息、查看城市信息。每個(gè)功能中又有一些小功能,如火車信息查詢中有:按車次查詢、按出發(fā)地與目的地查詢(其中又有最快、最省錢、全部選擇)中轉(zhuǎn)站查詢、查看火車信息,火車信息編輯又包括:添加火車信息、刪除火車信息、查看火車信息、保存火車信息功能。
2、概要設(shè)計(jì):
(1)、航空訂票系統(tǒng):
1)、抽象數(shù)據(jù)類型定義如下(C語言下的):
typedef struct airline{
char line_num[8];//航班號(hào)
char plane_num[8];//飛機(jī)號(hào)
char end_place[20];//目的的
int total;//座位總數(shù)
int left;//剩余座位
struct airline *next;//下一個(gè)結(jié)點(diǎn)
}airline;
typedef struct customer{
char name[9];//顧客名
char line_num[8];//航班號(hào)
int seat_num;//座位號(hào)
struct customer *next;//下一個(gè)結(jié)點(diǎn)
}customer;
/******************鏈表操作模塊***********/
airline *init_airline();
//初始化鏈表
customer * init_customer();
//初始化鏈表
status insert_airline(airline **p,char *line_num,char *plane_num,char *end_place,int total,int left);
//airline鏈表插入操作
//插入航班
status insert_customer(customer **p,char *name,char *line_num,int seat);
//customer鏈表插入操作
status creat_airline(airline **l);
//創(chuàng)建airline單鏈表
status creat_customer(customer **l);
//創(chuàng)建customer單鏈表
/******************鏈表操作模塊********************/
2)、其它模塊的實(shí)現(xiàn)函數(shù)聲明如下:
//**********************信息修改****************/
airline *modefy_airline(airline *l,char *line_num);
//修改airline鏈表中的數(shù)據(jù)
status delete_airline(airline *h,char *line_num);
//刪除航班
status delete_customer(customer *h,char *line_num);
//刪除顧客
status delete_cus(customer *h,airline *l,char *name);
//顧客退票
status increase_air(airline *l,char *line_num,char *plane_num,char *end_place,int total);
//增加航線
//**********************信息修改*************/
//*********************文件操作模塊**************/
status save_airline(airline const *l);
//保存airline.dat
status save_customer(customer const *l);
//保存顧客信息 customer.dat
//*********************文件操作模塊**************/
int changStrInt(char *ch);
//把字符串轉(zhuǎn)化為整型
status book(airline const *l,char *line_num,customer *c,char *name);
//訂票
status print_airline(airline const *l);
//打印航線信息
status print_customer(customer const *l);
//打印顧客信息
status air_main();
//執(zhí)行函數(shù)
(2)、24點(diǎn)游戲:
1)抽象數(shù)據(jù)類型定義如下(C語言下的):
int number[2][4];
enum
{
eNumber = 0, //操作數(shù)
eOperator = 1 //算子
};
typedef struct sqlist{
int bol;//bol is 0 when num_ch is a number;bol is 1 when the num_ch is a oprater
int num_ch;
struct sqlist *next;
}sqlist;
typedef struct sqstack {
int *base;
int *top;
int stacksize;
}sqstack;
/***************鏈表操作模塊****************/
status init_sq(sqlist *l);//初始化鏈表
status insert_sq(sqlist **p,int e,int bl);
//鏈表插入操作
//由于這里要求修改外部指針,因此要用指向指針的指針
//將插入到鏈表的末尾
/***************鏈表操作模塊**************/
2)、其它模塊的實(shí)現(xiàn)函數(shù)聲明如下:
/*******用棧進(jìn)行表達(dá)式計(jì)算模塊***********/
int check(sqlist l);
//保證輸入的數(shù)字是給出的四個(gè)數(shù)字
int chang(char *s,sqlist *l);
//將用戶的輸入轉(zhuǎn)化為單鏈表
int Operate(int a,int theta, int b);
//計(jì)算a theta b
int ReturnOpOrd(char op,char* TestOp);
//被char precede(char Aop, char Bop)所調(diào)用來求優(yōu)先級(jí)
char precede(char Aop, char Bop);
//返回優(yōu)先級(jí)
status initstack(sqstack *s);
//棧初始化
int gettop(sqstack *s);
//取棧頂元素
status push(sqstack *s,int e);
//把 e 壓棧
status pop(sqstack *s,int *e);
//出棧,用e 保存
int EvaluateExpression(char* MyExpression);
/***************鏈表操作模塊******************/
int randomm();//產(chǎn)生四個(gè)隨機(jī)數(shù)
/****************計(jì)算機(jī)計(jì)算模塊****************/
int CalcOneExpress(int expression[][2]);
int Equal24(int n);
int CalcArray1(int iNumInput[2][4]);
// a * b * c * d //7 number
int CalcArray2(int iNumInput[2][4]);
// (a * b) * c * d //9 number
int CalcArray3(int iNumInput[2][4]);
// (a * b * c) * d //9 number
int CalcArray4(int iNumInput[2][4]);
// (a * b * c) * d //9 number
int CalcArray5(int iNumInput[2][4]);
// (a * b) * (c * d) //11 number
int CalcArray6(int iNumInput[2][4]);
// ((a * b) * c) * d //11 number
int CalcArray7(int iNumInput[2][4]);
// (a * b * c) * d //9 number
int Calc24(int number[2][4]);
//括號(hào)的幾種情況
//1 無括號(hào)
//2 (a b) c d 同a b (c d), 下省略
//3 (a b c) d
//4 a (b c) d
//5 (a b) (c d)
//6 ((a b) c) d
//7 (a (b c)) d
/****************計(jì)算機(jī)計(jì)算模塊*****************/
void game_24_main();
//24點(diǎn)游戲執(zhí)行函數(shù)
。3)、旅游交通查詢系統(tǒng):
1)所需抽象數(shù)據(jù)類型定義如下(C語言下的):
typedef struct {
int day;//天數(shù)
int hour;//小時(shí)數(shù)
int minute;//分鐘數(shù)
}time_train;
typedef struct town{
char name[9];//城鎮(zhèn)名稱
time_train arrive;//火車到達(dá)時(shí)間
time_train leave;//火車離站時(shí)間
struct town *next_town;//下一個(gè)城鎮(zhèn)
}town;
typedef struct train{
char train_num[8];//火車序列號(hào)
char start_place[9];//始發(fā)地
char end_place[9];//終點(diǎn)站
int fare;//費(fèi)用
int hour;//時(shí)間(用小時(shí)計(jì)算)
struct train *next_train;//指向下一輛列車
struct town *next_town;//指向下一個(gè)城鎮(zhèn)
}train;
typedef struct{
int length;//長(zhǎng)度
// int hour;//時(shí)間
// int fare;//費(fèi)用
int ivex,jvex;//邊的兩端頂點(diǎn)號(hào)
}path,*path_p;//路徑類型
typedef struct path_node{//邊結(jié)點(diǎn)
path pa;//路徑類型
struct path_node *i_link,*j_link;
//i_link,j_link分別指向邊結(jié)點(diǎn)的兩頂點(diǎn)的地址
}path_node,*path_node_p;
typedef struct {
char city_name[9];//城市名
path_node_p firsh_path;//指向第一條依附該頂點(diǎn)的邊
}city_node,*city_p;//圖結(jié)點(diǎn)
typedef struct {
city_node adj_list[MAXSIZE];//鄰接多重
int city_num,path_num;//圖的頂點(diǎn)數(shù)和邊數(shù)
}graph_country;//圖儲(chǔ)存結(jié)構(gòu)
//鄰接多重表儲(chǔ)存圖
//路徑類型
typedef struct {//
int vx,vy;
}Edge;
typedef struct {
Edge path[100];//路徑中邊的序列
int len;//路徑中邊的數(shù)目
}path_city;
typedef struct {
char citys[MAXSIZE][9];//路徑中城市的序列
int num;//城市數(shù)目
}p_city;//
/**************火車的鏈表操作模塊**************/
train *init_train();
//初始化鏈表
town *init_town();
//初始化鏈表
status insert_train(train *l, char *train_num,char *start_place,char *end_place,int fare,int hour);
//火車鏈表的插入操作
status insert_town(train *l,char *name,time_train arrive,time_train leave);
//城市的插入操作
status find_town(train *l,char *town);
////查找中轉(zhuǎn)站
status find_train_num(train *l,char *train_num);
//用火車序列號(hào)來查詢
status find_place(train *l,char *start_place,char *end_place,char choice);
//用始發(fā)始發(fā)站和終點(diǎn)站來查詢
//status creat_train(train *l);
//內(nèi)置數(shù)據(jù)來創(chuàng)建火車鏈表
status creat_train_f(train *l);
//用文件來創(chuàng)建火車鏈表
status creat_train_save(train *l);
//用修改后的儲(chǔ)存文件train_save.dat來創(chuàng)建火車鏈表
status print_train(train const *l);
//打印火車信息,包括中轉(zhuǎn)站
status save_train(train l);
//儲(chǔ)存修改后的到train_save.dat文件中
status delete_train(train *l,char *train_num);
//刪除列車
/******************火車的鏈表存儲(chǔ)操作模塊*******/
/*******火車的圖(多重鄰接表)操作模塊***********/
status insert_path(graph_country *l,path pa);
//在圖l中插入邊pa
status insert_city(graph_country *l,char *city_name,int i);
//在圖l的i位置插入一城市city_name
status init_graph(graph_country *l);
//初始化圖l
status creat_graph(graph_country *l);
//創(chuàng)建圖l,從文件中讀取信息
void get_city(graph_country l,int i, char *city_name);
//以city_name返回鄰接多重表中序號(hào)為i頂點(diǎn)的城市名
path_node *first_path(graph_country l,int vi);
//返回圖中依附于頂點(diǎn)的第一條這的指針:l.adj_list [vi].firsh_path
path_node * next_path(graph_country g,int vi,path_node p,int *vj,path_node *q);
//以vj返回圖g中依附于頂點(diǎn)vi的一條邊(由指針p所指)的另一端點(diǎn);
//以q返回圖中依附于頂點(diǎn)vi且相對(duì)于指針p所指邊的下一條邊
status print_graph(graph_country g);
//打印城市圖的信息
/********火車的圖(多重鄰接表)操作模塊*********/
2)、其它模塊的實(shí)現(xiàn)函數(shù)聲明如下:
/**********迪杰斯特拉算法實(shí)現(xiàn)模塊**********/
void init_p(path_city *pa);
//初始化為一條空路徑
void init_set(p_city *p);
//初始化生成最短路徑結(jié)點(diǎn)的集合
void copy_path(path_city *pa1,path_city *pa2);
//復(fù)制路徑
void insert_p(path_city *pa,int v,int w);
//在pa中插入一條邊(v,w)
int path_length(path_city *pa);
//返回路徑長(zhǎng)度
void out_path(graph_country l,path_city pa,p_city *citys,int nd);
//將路徑轉(zhuǎn)換為城市名稱的序列
void putin_set(char *city_name,p_city *p,int st);
//把city_name(序號(hào)為st的結(jié)點(diǎn))放入
void shortest_path(graph_country g,int st,int nd,int *pathlength,p_city *path_info);
//利用迪杰斯特拉算法的基本思想求圖g中從頂點(diǎn)st到頂點(diǎn)nd的一條最短路徑
//最短路徑path_info及其路徑長(zhǎng)度path_lenth
int minnal(int *dist,p_city ss);
//求dist[]中的最小邊
/********迪杰斯特拉算法實(shí)現(xiàn)模塊************/
void train_main();
//全國旅游交通查詢的執(zhí)行函數(shù)
int city_name_int(graph_country l,char *name);
//在城市與計(jì)算機(jī)儲(chǔ)存序號(hào)之間建立一一映射
3、詳細(xì)設(shè)計(jì):
所需抽象數(shù)據(jù)類型定義和一些函數(shù)的聲明見設(shè)計(jì)概要。
這里只解釋一下執(zhí)行函數(shù)和一些比較復(fù)雜一點(diǎn)的算法。
。1)、航空訂票系統(tǒng):
1)、執(zhí)行函數(shù):
見void air_main();
2)、定票函數(shù):
status book(airline const *l,char *line_num,customer *c,char *name)//訂票
{
//訂票
airline *p=l;
customer *q=c->next ;
p=l->next ;
for(;q->next !=NULL;q=q->next){}
// PR("%s\n",q->name );
for(;p!=NULL;p=p->next )
{
if(strcmp(line_num,p->line_num )==0)
{
if(p->left >0)
{
PR("恭喜您!訂票成功!\n");
PR("你的座位號(hào)是: %d\n",(p->total -p->left+1));
insert_customer(&q,name,line_num,p->total-p->left +1);
p->left --;
return OK;
}
else PR("對(duì)不起,座位已滿!\n");
return 0;
}
}
PR("對(duì)不起,沒有這個(gè)航班號(hào)!\n");
return ERROR;
}
定票的同時(shí)也同時(shí)了修改航線信息,并在修改后把修改后的信息儲(chǔ)存到文件中去。
3)、退票函數(shù):
status delete_cus(customer *h,airline *l,char *name)
{
//顧客退票
customer *p,*pr;
char line_num[8];
// qr=h;
pr=h;
p=pr->next ;
// PR("開始刪除\n");
while(p!=NULL)
{
if(strcmp(name,p->name )==0)
{
strcpy(line_num,p->line_num );
l=modefy_airline(l,line_num);
pr->next =p->next ;
free(p);
PR("顧客 %s 退票成功!\n",p->name );
return OK;
}
pr=pr->next ;
p=pr->next ;
}
PR("無此顧客,無法退票!\n");
return ERROR;
}
在此函數(shù)執(zhí)行后執(zhí)行顧客信息修改和航線信息修改。從而實(shí)現(xiàn)了退票功能。
(2)、24點(diǎn)游戲:
這個(gè)系統(tǒng)中主要有兩個(gè)功能:人算24點(diǎn)和計(jì)算機(jī)算24點(diǎn),前者是在單鏈表和用棧來表達(dá)式求值的模塊中實(shí)現(xiàn)的,后者是在機(jī)算模塊(也有用棧求值)中實(shí)現(xiàn)的。
int CalcOneExpress(int expression[][2]);是實(shí)現(xiàn)計(jì)算機(jī)計(jì)算功能。
int Equal24(int n);是判斷是否為24。
這里我主要講一下計(jì)算機(jī)計(jì)算24點(diǎn)的算法:
要計(jì)算機(jī)判斷是否能計(jì)算24,就必須考慮所有的計(jì)算情況才能得出結(jié)論,如何把所有的情況都考慮進(jìn)去呢?我想了一個(gè)星期,如果把四個(gè)計(jì)算數(shù)的數(shù)的地位看成一樣的,把+-*/四種運(yùn)算符也看成同等地位(這可用兩個(gè)四重for循環(huán)來實(shí)現(xiàn)),如此一來繁亂復(fù)雜的很多種情況都?xì)w于七種情況,分另用一個(gè)函數(shù)去計(jì)算這種“特殊”的情況,那就是:(*號(hào)表示操作符,abcd分別表示四個(gè)數(shù)字)
int CalcArray1(int iNumInput[2][4]);
// a * b * c * d //7 number
int CalcArray2(int iNumInput[2][4]);
// (a * b) * c * d //9 number
int CalcArray3(int iNumInput[2][4]);
// (a * b * c) * d //9 number
int CalcArray4(int iNumInput[2][4]);
// (a * b * c) * d //9 number
int CalcArray5(int iNumInput[2][4]);
// (a * b) * (c * d) //11 number
int CalcArray6(int iNumInput[2][4]);
// ((a * b) * c) * d //11 number
int CalcArray7(int iNumInput[2][4]);
// (a * b * c) * d //9 number
(3)、旅游交通查詢系統(tǒng):
這個(gè)系統(tǒng)中主要是有三個(gè)模塊來完成所有功能,其中有關(guān)火車信息查詢與編輯功能都在一個(gè)火車模塊中實(shí)現(xiàn)的。而最小路徑的算法實(shí)現(xiàn)都在圖模塊和最小路徑模塊實(shí)現(xiàn)的。
這里分析一下status creat_train_f(train *l);函數(shù),我這里用從文件中讀入信息的方式創(chuàng)建火車信息鏈表。為了能編輯它,我提供了兩種方式:一是直接修改文件,但這不太安全,因?yàn)槟惚仨氁远x的文件格式編輯文件,否則錯(cuò)誤不可預(yù)料。二是用我這里提供的編輯方式,再把修改信息保存下來(有這個(gè)選項(xiàng))。
編輯火車信息的一些關(guān)鍵語句如下:
PR("---課程設(shè)計(jì)選擇菜單------*\n");
PR("*---旅游交通查詢系統(tǒng)選擇菜單------------*\n");
PR("*----火車信息編輯菜單-------------------*\n");
PR("* 添加火車信息------------0 *\n");
PR("* 刪除火車信息------------1 *\n");
PR("* 查看火車信息------------2 *\n");
PR("* 保存火車信息------------3 *\n");
PR("* 退出火車信息編輯--------4 *\n");
PR("*---------------------------------------*\n");
PR("請(qǐng)選擇: ");
ch3 = getch();
PR("%c\n",ch3);
if(ch3=='0')
{
PR("請(qǐng)輸入你要添加的火車的列車號(hào) :");
scanf("%s",train_num);
PR("請(qǐng)輸入始發(fā)站: ");
scanf("%s",start_place);
PR("請(qǐng)輸入終點(diǎn)站: ");
scanf("%s",end_place);
PR("請(qǐng)輸入所需費(fèi)用: ");
scanf("%d",&fare);
PR("請(qǐng)輸入所需時(shí)間: ");
scanf("%d",&hour);
insert_train(tr,train_num,start_place,end_place,fare,hour);
PR("成功添加%s號(hào)火車\n",train_num);
while(t4==1)
{
PR("*-----------------------------*\n");
PR("---課程設(shè)計(jì)選擇菜單---------*\n");
PR("*---旅游交通查詢系統(tǒng)選擇菜單----*\n");
PR("*----火車信息編輯菜單---------*\n");
PR("*-----火車中轉(zhuǎn)站編輯菜單-----*\n");
PR("* 添加中轉(zhuǎn)站----0 *\n");
// PR("* 刪除中轉(zhuǎn)站-------1 *\n");
PR("* 查看中轉(zhuǎn)站信息------1 *\n");
PR("* 中轉(zhuǎn)站編輯完畢----2 *\n");
PR("*--------------------------*\n");
PR("請(qǐng)選擇: ");
ch4= getch();
PR("%c\n",ch4);
if(ch4=='0')
{
PR("!!中轉(zhuǎn)站也包括始發(fā)站和終點(diǎn)站\n!!");
PR("!!這兒途經(jīng)各站的順序與實(shí)際剛好相反!!\n");
PR("請(qǐng)輸入你要添加的中轉(zhuǎn)站名 :");
scanf("%s",town_name);
PR("請(qǐng)輸入第幾天到達(dá): ");
scanf("%d",&arrive.day );
PR("請(qǐng)輸入到達(dá)的時(shí)數(shù): ");
scanf("%d",&arrive.hour );
PR("請(qǐng)輸入到達(dá)的分鐘數(shù): ");
scanf("%d",&arrive.minute);
PR("請(qǐng)輸入第幾天出發(fā): ");
scanf("%d",&leave.day );
PR("請(qǐng)輸入的時(shí)數(shù): ");
scanf("%d",&leave.hour );
PR("請(qǐng)輸入出發(fā)的分鐘數(shù): ");
scanf("%d",&leave.minute);
if(tr->next_train ==NULL)
{
insert_town(tr,town_name,arrive,leave);
}
else insert_town(tr->next_train,town_name,arrive,leave);
}
最后分析一下利用迪杰斯特拉算法來求最短路徑的基本思想與過程,集合ss始終包含已求得的最短路徑的結(jié)點(diǎn),再算其它結(jié)點(diǎn)與已求集合的最短距離,再把最短距離的結(jié)點(diǎn)加入到ss 集合,重復(fù)上棕過程。C程序如下:
void shortest_path(graph_country g,int st,int nd,int *pathlength,p_city *path_info)
{
//利用迪杰斯特拉算法的基本思想求圖g中從頂點(diǎn)st到頂點(diǎn)nd的一條最短路徑
//最短路徑path_info及其路徑長(zhǎng)度path_lenth
int dist[MAXSIZE];
int i=0,v,w;
path_node q;
int found,min;
p_city ss;//ss為已求得最短路徑的頂點(diǎn)的集合
int adjvex;
path_node *p,*qq;
path_city path[MAXSIZE];
for (i=0;i
{
}
p=first_path(g,st);
while(p!=NULL)//初始化dist數(shù)組,檢測(cè)依附于起始邊的每一條邊
{
qq=next_path(g,st,*p,&adjvex,&q);
dist[adjvex]=p->pa.length ;
insert_p(&path[adjvex],st,adjvex);
p=qq;
}
found =FALSE;
init_set(&ss);
putin_set(g.adj_list[st].city_name ,&ss,st);
while(!found)
{
min=minnal(dist,ss);
//在所有尚未求得最短路徑的頂點(diǎn)中求使dist取小值的i值
if(min==nd) found= TRUE;
else
{
v=min;
putin_set(g.adj_list[v].city_name,&ss,v);
p=first_path(g,v);
while(p!=NULL)//檢測(cè)依附于v的每一條尚未訪問的邊
{
qq=next_path(g,v,*p,&w,qq);
if((ss.citys [w][0]=='\0'/*w不在ss中*/)&&((dist[v]+p->pa .length )
{
dist[w]=dist[v]+p->pa.length ;
copy_path(&path[w],&path[v]);
insert_p(&path[w],v,w);
}
p=qq;
}//while(p!=NULL)
}//else
}//while(!found)
pathlength=&dist[nd];
PR("從 %s 到 %s 的最短路徑是:\n",g.adj_list [st].city_name ,g.adj_list [nd].city_name );
out_path(g,path[nd],&ss,nd);
PR("總路徑長(zhǎng)度 :%d 公里\n",dist[nd]);
}
4、調(diào)試分析:
調(diào)試是程序設(shè)計(jì)中最重要的一環(huán),他幾乎決定了程序優(yōu)劣,F(xiàn)將我調(diào)試時(shí)遇到的一些問題及其解決的方法的記錄陳列如下以供學(xué)習(xí)與交流:
我將調(diào)試時(shí)遇到的一些問題分為兩大類:
(1)、語法錯(cuò)誤:
語法錯(cuò)誤相對(duì)來說要好調(diào)試一些的,但有兩點(diǎn)需要特別指出:一是應(yīng)該用規(guī)范化的格式輸入源程序,我推薦的格式是:函數(shù)體內(nèi)、循環(huán)體內(nèi)等都應(yīng)該縮進(jìn)一個(gè)TAB位,相應(yīng)的塊語句的兩個(gè)大括號(hào)都應(yīng)保持在同一列上,函數(shù)體之間、模塊之間都應(yīng)用空行隔開,這就解決了各種匹配的問題,更重要的是它極大的增強(qiáng)的程序的可讀性。二是應(yīng)該注意函數(shù)的實(shí)參與形參的傳遞問題,要盡量保持兩者類型的匹配,(當(dāng)不匹配又可通過編譯時(shí)會(huì)發(fā)生數(shù)據(jù)類型的隱式轉(zhuǎn)換,這樣會(huì)產(chǎn)生很多不安全且又很難找到的錯(cuò)誤)當(dāng)不需要改變形參時(shí),只需傳入變量,如果你想在函數(shù)體內(nèi)改變函數(shù)的外部變量,則傳入指針。
我的調(diào)試一個(gè)打開文件進(jìn)行寫入操作時(shí)遇到這樣一個(gè)問題,char filename[]=”C:\\airline.dat”,fp=fopen “filename”,”wb”);調(diào)試了我一個(gè)半小時(shí),還是沒有找到問題的所在,最后發(fā)現(xiàn)是在filename上多了一對(duì)引號(hào)!。
。2)、邏輯錯(cuò)誤:
在編譯錯(cuò)誤為0的情況下,不要高興的太早,這只是你調(diào)試程序的第一步,此時(shí)也要關(guān)注一下警告warning,每一個(gè)warning都有他一定的道理。當(dāng)你修改的只剩下一些無關(guān)緊要的時(shí),你才可以連接運(yùn)行你的程序了。這其中出現(xiàn)的一些邏輯錯(cuò)誤才是調(diào)試的難點(diǎn)所在。在連接程序時(shí)可能出現(xiàn)的問題可能是,庫連接不上、標(biāo)志符有問題(如函數(shù)名不應(yīng)該以數(shù)字開頭命名,你定義的標(biāo)志符與編譯器內(nèi)部或庫內(nèi)部定義的標(biāo)識(shí)符相沖突。如我在定一個(gè)火車的時(shí)間的結(jié)構(gòu)體時(shí)用到了time作為它的名稱,與time.h內(nèi)部定義的變量名相沖突了)此外還應(yīng)該注意幾點(diǎn):
1)、溢出的問題,其中包括數(shù)組的下標(biāo)上溢或下溢,for語句的控制語句超出數(shù)據(jù)范圍(如在訪問單鏈表時(shí)應(yīng)該是:for p=l;p!=NULL;p=p->next))。
2)、指針問題,記住,聲明一個(gè)指針只是在內(nèi)存中開辟了一個(gè)這種數(shù)據(jù)類型的空間,并讓這個(gè)指針指向它,由于它還沒有指向任何變量,因此不能引用其指向的任何成員(編譯器會(huì)警告你說你的指針沒有初始化卻用了它,如我在初始化單鏈表時(shí)這樣寫的:init_sq sqlist *l){l->base= *elemtype)malloc sizeof elemtype));}而在調(diào)用時(shí)用語句:sqlist *l;init_sq );這是不允許的,因?yàn)樗鼉H僅是一個(gè)地址,而不是一個(gè)變量,這個(gè)程序有幾種改法:)(又如:char *name;再將name做為一個(gè)指針傳到函數(shù)中,你的本意可能是想通過函數(shù)改變你的字符串,但這里你忽略了一個(gè)問題,你沒有初始化你的指針卻用了它,這樣很不安全,雖然你有時(shí)可以運(yùn)行,卻有了不安定的因素。你可以這樣定義:char name[20];這里的name是一個(gè)常量地址,也是一個(gè)數(shù)組名,因此不用擔(dān)心它沒有被初始化。字符數(shù)組與字符串的區(qū)別是前者不用在最末位加一個(gè)’\0’,但你如果把它當(dāng)做字符串用時(shí)系統(tǒng)會(huì)自動(dòng)給你加上的,因此在定義字符數(shù)組時(shí)盡量多定義一位)
3)、何時(shí)用指向指針的指針(二級(jí)指針)?
我的經(jīng)驗(yàn)是:當(dāng)你在函數(shù)體內(nèi)改變了一個(gè)形參的地址且你又想同時(shí)影響函數(shù)體外部的指針,則需要一個(gè)二級(jí)指針。如我在24點(diǎn)游戲中的status insert_sq sqlist **p,int e,int bl)函數(shù)中,我為了每次都在鏈表末尾加一個(gè)結(jié)點(diǎn),我就使用了二級(jí)指針p,使其始終指向最后一人結(jié)點(diǎn)。當(dāng)然這也可以用返回指針的方式完成同樣的功能。但當(dāng)你必須返回一個(gè)別的變量時(shí)這個(gè)方法就不行了。
4)、養(yǎng)成打開了文件(fopen)就要在某個(gè)時(shí)候關(guān)閉(fclose)的習(xí)慣,否則會(huì)有安全隱患。
5)、C語言本身的一些安全問題:
a)、字符串函數(shù)的不安全性:
如scanf();由于它是基于獲取單詞而不是獲取字符串,
它遇到空格或換行符就結(jié)束(誰先滿足執(zhí)行誰),當(dāng)提前遇到空格或輸入的字符個(gè)數(shù)大于你所容納的個(gè)數(shù)時(shí),系統(tǒng)會(huì)把其它的字符溢出到相鄰的內(nèi)存中去,產(chǎn)生一些不安全的因素。Gets(); strcpy();等字符串函數(shù)都是這樣。因此建議你嚴(yán)格控制用戶的輸入或用另外的一些相應(yīng)的函數(shù)來代替,如用:strncpy();代替strcpy();
b)、常量定義時(shí)的一些不安全因素:
用#define 定義常量編譯器不會(huì)去檢查其數(shù)據(jù)類型,引發(fā)安全隱患。解決的方法是用關(guān)鍵字:const去定義常量,這樣可以要編譯器給你進(jìn)行類型匹配檢查。
c)、名字沖突的不安全性:
名字,特別是函數(shù)名、數(shù)據(jù)類型名經(jīng)常與自己定義的名字或系統(tǒng)的,包括庫里面的名字相同,解決的方法是盡量用能表達(dá)意思且系統(tǒng)不太可能出現(xiàn)的名字命名。(僅在當(dāng)兩個(gè)相同的標(biāo)識(shí)符在同一個(gè)作用域時(shí)才會(huì)出現(xiàn)此問題。
d)、內(nèi)存管理的不安全性:
內(nèi)存泄漏,野指針等內(nèi)存問題,應(yīng)該好好的考慮一下,我的經(jīng)驗(yàn)是每刪除一人用malloc()申請(qǐng)的結(jié)點(diǎn)時(shí),都應(yīng)該用free();語句釋放它;并且在這個(gè)作用域內(nèi)不再使用此指針去指向其它的地址。
e)、移植的安全性問題
雖然C語言具有很強(qiáng)的可移植性,但在不同機(jī)器上運(yùn)行(如32位機(jī)),不同的編譯器上(如tc 上整型是2個(gè)字節(jié),而在VC6.0中卻是4位。)解決的方法是在申請(qǐng)空間等涉及到數(shù)據(jù)類型長(zhǎng)度時(shí)都采有sizeof(elemtpye);的形式,又如我在定義maxint時(shí)用了:#define maxint (sizeof(int)>2?MAXINT_2:MAXINT_1)這樣考慮不用管 int型是四位的,還是二位的。
5、遺留問題分析:
(1)、在航空訂票系統(tǒng)中的密碼輸入時(shí),由于我用了for 語句和getch );putchar ‘*’);來實(shí)現(xiàn)的,而getch );不分區(qū)另ENTER和BACKSPACE等特殊鍵,不好控制它的結(jié)束。因此我只有避過問題強(qiáng)行規(guī)定密碼必須是8位的,但在輸入密碼時(shí)仍然不允許用戶輸入ENTER和BACKSPACE等特殊鍵。
。2)、24點(diǎn)中的問題:
相對(duì)于一個(gè)完整的二十四點(diǎn)游戲本程序尚存在兩個(gè)缺陷:
I.時(shí)間限制:由于本程序不是重在娛樂,而重在學(xué)習(xí)與交流,再加上我也沒找到C語言中的適當(dāng)?shù)臅r(shí)間函數(shù),(要是用Visnal C++6.0的可視化編程加一個(gè)時(shí)鐘計(jì)十分方便,我也正在把此游戲改為c++語言,主要是想熟悉一下)故而來把此功能加入進(jìn)去,如果玩家有心,可以自行加入。
II.由于我在算a/b時(shí),a,b和其結(jié)果卻設(shè)為整型,導(dǎo)致計(jì)算機(jī)可能得出2/5=0的結(jié)果,因此我又改了一下,在計(jì)算a/b之前先判斷,if((a%b)!=0),我返回了一個(gè)-2000,目的是阻止這種運(yùn)算,現(xiàn)實(shí)中是卻存在一些數(shù)字非得這樣計(jì)算才能得出結(jié)果(如:5,,5,5,1,只能由((5-1/5)*5=24來算)。導(dǎo)致一些問題的產(chǎn)生。要解這些問題有兩個(gè)方法:其一是修改四個(gè)隨機(jī)數(shù)的產(chǎn)生函數(shù):randomn(),控制諸如此等的隨機(jī)數(shù),當(dāng)然這雖然不先為一個(gè)辦法但只是避開問題本身;其二是把一些相關(guān)的函數(shù)和數(shù)據(jù)類型,如:?jiǎn)捂湵淼膇nt num_ch改為float num_ch;把入棧、出棧等等一些函數(shù)的以前接的int型的改為接的和處理float型,再把int equal_24(int num)改為int equal_24(float num)用if(fabs(num_24-24)<0.000001)來判斷,這樣雖然改動(dòng)極大,但此法可完全解決此問題,可以一試。(其實(shí)這個(gè)問題在其他的數(shù)據(jù)處理算法中也存在)
。3)、交通旅游系統(tǒng)中:
1)、中轉(zhuǎn)站的反輸入問題:
由于我在插入操作時(shí)為了節(jié)約時(shí)間,都是從前部插入,造成了實(shí)際站點(diǎn)順序與程序中儲(chǔ)存的順序剛好相反,本來可以通過加一個(gè)語名for(q=l;q->next!=NULL;q=q->next)來解決,但我覺得這里沒有這個(gè)必要,反正我在編輯文件時(shí)反過來放了。
2)、在求最小路徑時(shí),沒有同時(shí)實(shí)現(xiàn)最省錢,最省時(shí)的最佳路徑。
但我已把解決他們的接口都留下了。便于以后把此功能加上。
3)、終點(diǎn)站本無出站時(shí)間的,但我們?yōu)榱斯教幚硭袉栴},我都把其設(shè)置為000,這個(gè)問題可通過增加控制輸出語句來解決,但我覺得沒有必要,因?yàn)槊總(gè)人都知道哪里是終點(diǎn)站吧。(我們?cè)趓eadme.txt中也有提示)。
4)、數(shù)據(jù)文件,密碼的安全性問題。
由于我把用戶修改后的密碼儲(chǔ)存在pass.dat里,卻又不能把它設(shè)置為只讀,用戶可以隨便改變,也許會(huì)導(dǎo)致異常的產(chǎn)生。數(shù)據(jù)文件也存在這個(gè)問題。
6、使用說明及測(cè)試結(jié)果:
(1)航空訂票系統(tǒng):
航空訂票系統(tǒng)的界面:
*---------------------------------------*
*---課程設(shè)計(jì)選擇菜單--------------------*
*-----航空訂票系統(tǒng)選擇菜單--------------*
* 訂票------------------0 *
* 退票------------------1 *
* 查看所有信息----------2 *
* 修改航線(需密碼)------3 *
* 退出航空訂票系統(tǒng)------4 *
*---------------------------------------*
進(jìn)入修改航線(需密碼)------3時(shí)的原始密碼是:weiji024
經(jīng)測(cè)試,此中第個(gè)功能都實(shí)現(xiàn)了。
。2)、24點(diǎn)游戲:
24點(diǎn)游戲的界面:你想采用什么模式?\n");
************************************************
*----課程設(shè)計(jì)選擇菜單--------------————----*
*-------24點(diǎn)游戲菜單--------------------------*
*計(jì)算機(jī)給出四個(gè)數(shù)字,你來算直到你想退出: 0 *
*自己給出四個(gè)數(shù)字,要計(jì)算機(jī)來算,直到想退出: 1 *
* 退出24點(diǎn)游戲: 2 *
************************************************
經(jīng)測(cè)試,此中每個(gè)功能都實(shí)現(xiàn)了。
。3)、旅游交通查詢系統(tǒng):
旅游交通查詢系統(tǒng)界面:
*---------------------------------------*
*---課程設(shè)計(jì)選擇菜單--------------------*
*-----旅游交通查詢系統(tǒng)選擇菜單----------*
* 火車信息查詢---------0 *
* 最短路徑查詢---------1 *
* 火車信息編輯---------2 *
* 讀入修改信息---------3 *
* 查看火車信息---------4 *
* 查看城市信息---------5 *
* 退出-----------------6 *
*---------------------------------------*
經(jīng)測(cè)試,此中每個(gè)功能都實(shí)現(xiàn)了。其中最短路徑查詢的原始數(shù)據(jù)為《數(shù)據(jù)結(jié)構(gòu)》第187面的圖7.33, 經(jīng)測(cè)試,最小路徑全部符合實(shí)際。
7、源程序:
我用了9個(gè)文件:分別是:all.h train.c
24.h rainGraph.c
train.h 24game.c
airplane.h main.c t
航空訂票系統(tǒng).c
四個(gè)需要讀入的數(shù)據(jù)文件:pass.dat ,train.dat ,trainGraph.dat
train_save.dat
兩個(gè)生成文件:airline.dat ,customer.dat
所有源程序都在VC6.0中調(diào)試通過。
三、設(shè)計(jì)收獲及心得體會(huì):
這幾天編程發(fā)現(xiàn)了幾個(gè)有趣的結(jié)論:
1、編程的真正吸引力可與玩游戲相媲美
考完英語之后的兩天,除了吃飯外沒有一刻不是在電腦前的,晚上睡覺想著它,早上6點(diǎn)起床擺弄它。尤其是在調(diào)試時(shí)找一個(gè)錯(cuò)誤用了幾個(gè)小時(shí)仍未找到卻忽然醒悟或找的那一刻的感覺實(shí)在太好了。這種執(zhí)著與感覺我只有在玩《天龍八部》、《新仙劍一》、《新仙劍二》觸發(fā)了找了好久的劇情或走出了困了好久的迷宮的感覺。天地為之開闊……
2、真正的程序不只是“運(yùn)行”了!
真正可稱得上“好程序”是要滿足一大堆的條件的。可讀性、健壯性、可維護(hù)性、高效性等等等等條件。其實(shí)大部分功能我早就已經(jīng)實(shí)現(xiàn)了,(只用了兩天),但其后的測(cè)試、修改、完善、注釋、潤色和現(xiàn)在的編寫系統(tǒng)文檔也用了不少的時(shí)間。
3、成功的感覺真好!
當(dāng)你看著自己把功能一個(gè)個(gè)實(shí)現(xiàn),把錯(cuò)誤一個(gè)調(diào)試出來,那種感覺給了自己某種安慰,還有自信。
4、幾句廢話:要提高自己的編程能力,你必須親自去體驗(yàn)、去設(shè)計(jì)、編輯、編譯、調(diào)試、運(yùn)行。在此之前,我也以為自己對(duì)C語言已經(jīng)比較懂了,可還是遇到了一系列問題,也學(xué)到很多東西。每一個(gè)程序員都是在失敗、嘗試、失敗、嘗試與收獲中成長(zhǎng)起來的。作為一個(gè)團(tuán)團(tuán)的計(jì)算機(jī)學(xué)院,大部分學(xué)生對(duì)計(jì)算機(jī)竟不大感興趣,導(dǎo)致學(xué)院人才凋零。我本學(xué)識(shí)尚淺,無權(quán)談?wù)撨@些,只是希望能對(duì)大家有所警醒,編程之道漫漫無邊,吾將上下而求索.最后以林銳先生的話來作為自己的追求目標(biāo)和最后的結(jié)束語:以振興民族軟件產(chǎn)業(yè)為己任,作真實(shí)、正值、優(yōu)秀的科技人員!
四、自己的特色和申請(qǐng)的成績(jī)
1、可讀性較強(qiáng):
我們的程序可讀性比較好,主要體現(xiàn)在以下幾個(gè)方面:
(1)、結(jié)構(gòu)嚴(yán)謹(jǐn),都采用模塊化設(shè)計(jì),我們采用了多文件結(jié)構(gòu),不同的文件實(shí)現(xiàn)了不同的功能,并將每個(gè)模塊的函數(shù)都在相應(yīng)的頭文件中聲明并帶有功能注解。
(2)、較詳細(xì)的注釋 使得程序更容易閱讀。我們?cè)诿總(gè)函數(shù)、每種數(shù)據(jù)類型的定義、每條關(guān)鍵語句都不得有相應(yīng)的注釋。
(3)、書寫規(guī)范:在輸入編輯源程序時(shí)我們力爭(zhēng)使程序語句更規(guī)范(用上面提到的格式規(guī)范)。
。4)標(biāo)識(shí)符(如函數(shù)名、變量名、數(shù)據(jù)類型名)命名有講究:我們大都采用了操作_對(duì)象的命名規(guī)則,如creat_train );表示創(chuàng)建火車鏈表之意,盡量使標(biāo)識(shí)符“詞能達(dá)意”,增強(qiáng)可讀性。
2、容易維護(hù):
我在編程時(shí)有個(gè)習(xí)慣,就是先把執(zhí)行函數(shù)寫好,如main )函數(shù),同時(shí)設(shè)置留下每個(gè)函數(shù)的接口,再編寫相應(yīng)的函數(shù)來實(shí)現(xiàn)它。這樣做的好處是讓我樣很清楚自己需要什么樣的函數(shù),有什么樣的接口,當(dāng)然就更容易維護(hù)了。
3、很強(qiáng)的健壯性:
我在編寫程序時(shí)盡量多的考慮了各種情況的發(fā)生,如用戶的各種非法輸入,數(shù)據(jù)文件的的丟失或者錯(cuò)誤的更改,并做出了相應(yīng)處理,使得程序仍可運(yùn)行。由于C語言的異常機(jī)制本身就不是很強(qiáng),我也只能做到這一步了。
4、支持?jǐn)?shù)據(jù)更新:
我在交通旅游系統(tǒng)中,數(shù)據(jù)都是從文件中讀取的,只要你按照readme.txt文件中所定義的文件格式編輯文件,即可得到新的.exe文件。
5、功能比較完善:
完善的功能主要體現(xiàn)在交通旅游系統(tǒng)中,我們基本實(shí)現(xiàn)了火車查詢的所有功能,如:始發(fā)地與目的地的查詢、車次的查詢、最快、最省錢查詢、中轉(zhuǎn)站查詢等等,與火車查詢的同類軟件相比還似乎更勝一籌,因?yàn)槲覀冞提供的“最短路徑”的查詢。
6、比較友好的界面:
由于各種原因,如時(shí)間,語言等原因,我們沒有做形如windows那么友好的界面,但我也在界面上下了一些功夫:我采用了逐級(jí)菜單,提示用戶的輸入與注意事項(xiàng),并把三個(gè)系統(tǒng)融入到一個(gè)系統(tǒng)中去,結(jié)構(gòu)謹(jǐn)然,界面也比較友好。
7、涉及的數(shù)據(jù)結(jié)構(gòu)知識(shí)廣泛:
課和設(shè)計(jì)本來就是為了提高自己的編程能力,我們?yōu)榱吮M可能的多的讓自己學(xué)得更多,在系統(tǒng)中我們定義了14個(gè)數(shù)據(jù)類型,70多個(gè)函數(shù),操作涉及到單鏈表、多重鏈表、棧、圖、隊(duì)列、多重鄰接表等等數(shù)據(jù)結(jié)構(gòu)的多種操作。
8、24點(diǎn)中的幾個(gè)特色:
1)在接受到用戶輸入的字符串后,程序判斷其為數(shù)字還是字符,并將其信息包括是數(shù)字還是字符的標(biāo)識(shí)(用int BOL,0為數(shù)字,1為字符)儲(chǔ)存到單鏈表中。從而使得其區(qū)別和求值非常簡(jiǎn)單,大大簡(jiǎn)化了程序的復(fù)雜度。
2)當(dāng)玩家認(rèn)為程序隨機(jī)產(chǎn)生的四個(gè)數(shù)字不能算出24時(shí)要求程序給出答案,這其實(shí)是一個(gè)復(fù)雜的問題,因?yàn)閿?shù)字是不定的,而算式出是不定的,要判斷所有的組合才能下結(jié)論。但經(jīng)我研究發(fā)現(xiàn)如果把四個(gè)數(shù)字和加減乘除不加以區(qū)別,加上括號(hào)一共只有7種組合,見程序的源程序中的
int CalcArray1(int iNumInput[2][4])//處理第一種情況,即無括號(hào)的一種,這樣把本是很復(fù)雜的問題大大簡(jiǎn)化了。
9、算法時(shí)間空間復(fù)雜度分析:
。1)、在參數(shù)傳遞時(shí),我大多數(shù)都采用了地址的傳入,因?yàn)檫@樣可以提高程序工作效率。同時(shí)考慮到安全的因素,為了防止傳入的指針意外的改變函數(shù)體外面信息,在不需要改變此地址時(shí),我們都有了const 修飾符。做到了效率與安全兼得。
。2)、當(dāng)需要?jiǎng)h除某個(gè)鏈結(jié)點(diǎn)時(shí),我使用了一種比較高較的一種算法:即用指針來實(shí)現(xiàn),避免兩次重復(fù)遍歷。其代碼請(qǐng)看源程序中的delete_函數(shù)。
。3)、24點(diǎn)機(jī)算算法分析:
由于我采用和如詳細(xì)所述的算法進(jìn)行計(jì)算,最壞的情況要執(zhí)行:4*4*4*3*2*1*7= 10752次,最好時(shí)執(zhí)行1次,平均次數(shù)是5376次。(對(duì)于現(xiàn)在的計(jì)算機(jī)來說即使是最壞的情況也不過是幾毫秒的事)這里程序比較簡(jiǎn)潔易懂,效率也比較高。
本文關(guān)鍵詞:數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì),由筆耕文化傳播整理發(fā)布。
本文編號(hào):249327
本文鏈接:http://sikaile.net/wenshubaike/kcsz/249327.html