您好,欢迎来到化拓教育网。
搜索
您的当前位置:首页数据结构课程设计

数据结构课程设计

来源:化拓教育网
目 录

课题一joseph环 ………………………………………………1

1.1 问题的提出 …………………………………………………………… 1 1. 2概要设计 ………………………………………………………………… 1 1.3流程图 …………………………………………………………………… 2 1.4 源代码 …………………………………………………………………… 3 1.5 结果与分析 ……………………………………………………………… 6

课题二 拓扑排序 ……………………………………………… 7

2.1 问题的提出 …………………………………………………………… 7 2. 2 概要设计 ……………………………………………………………… 7 2.3 流程图 ………………………………………………………………… 8 2.4 源代码 ………………………………………………………………… 9 2.5 结果与分析 …………………………………………………………… 13

课题三 纸牌游戏 …………………………………………… 15

3.1 问题的提出 …………………………………………………………… 15 3. 2 概要设计 ……………………………………………………………… 15 3.3流程图 ………………………………………………………………… 16 3.4 源代码 ………………………………………………………………… 17 3.5 结果与分析 …………………………………………………………… 18

课程设计总结 ………………………………………………… 20 参考文献 ……………………………………………………… 21

课题一 joseph环

1.1 问题的提出1.1.问题的提出

1.任务:编号是1,2,……,n的n个人按照顺时针方向围坐一圈,每

个人只有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个仍开始顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。设计一个程序来求出出列顺序。

2.测试数据:输入(范围:整型数据)

总成员数:7

各成员密码:3 1 7 2 4 7 4 初始值m:6

输出(范围:大于等于1,小于等于n的整型数据) 出列成员序号:6 7 4 1 5 3 2

1.2 概要设计 2.1算法思想

利用单向循环链表存储结构模拟此过程,因为循环链表最后一个结点的指针域指向头结点,整个链表形成一人环,刚好和题中的“n个人按照顺时针方向围坐一圈,每个人只有一个密码(正整数)”内容要求一致,而且,循环链表中任一结点出发均可找到表中其他结点,利用这一优点可较容易地找出报数的人及下一个报数的人,最后按照出列的顺序用一个for语句实现。

joseph环的组成成员由密码(password)和序号(No)组成,循环链表的存储结构如下:

typedef struct LNode {

int password; //密码 int No; //序号

struct LNode *next; //下一成员指针 }member; //组成成员结构体

1.3流程图 根据算法思想,画程序流程图如下:

开始 输入m、n m>0且n>0的整数 建立含n个结点的链表且用head指向第一个元素,结点数据域包含password、No、以及指向下一结点的指head=>p n≥2 (m%n)==0?n:m%n=>1=>i 输出p→No 结束 ip i++ 输出p→No p→password=>m 删除p所指向结点 n-- 1.4 源代码1.3.详细设计

typedef struct LNode {

int password; //密码 int No; //序号

struct LNode *next; //下一成员指针 }member; //组成成员结构体

typedef int status; #define OVERFLOW -2 #define OK 1 #define ERROR 0

#include #include

status CreateList_Circle(member **,int); status DeleteNode(member **);

status main() { int n,m;

member *head=NULL,*p=NULL; //头指针即首成员地址,遍历指针p printf (\"Please enter number of people:\\n\"); scanf (\"%d\总成员数 while (n<=0) {

printf (\"n must be positive, please enter again:\\n\"); scanf (\"%d\ }

if(!CreateList_Circle(&head,n)) //创建循环链表,返回头指针head return OVERFLOW;

printf (\"Please enter initial m:\\n\");

scanf (\"%d\初始值m while (m<=0) {

printf (\"m must be positive, please enter again:\\n\"); scanf (\"%d\ }

printf (\"\\nThe order is:\\n\"); p=head;

while (n>=2) //寻找出列成员 { int i;

m=(m%n==0)?n:m%n; //化简m值 for (i=1;ip=p->next; //p指向出列成员

printf (\"%d\\n\输出出列成员序号 m=p->password; //修改m

DeleteNode(&p); //删除链表中的出列成员 n--; //成员数自减 }

printf (\"%d\\n\输出最后一个成员序号 return OK; }

status CreateList_Circle(member **p_head,int n) {

//此算法创建一个无头结点的循环链表,结点数n,*p_head返回链表头指针即首结点地址 int i;

member *tail,*p;

*p_head=(member *)malloc(sizeof(member)); if (!(*p_head)) return OVERFLOW; (*p_head)->No=1; //储存成员一序号

printf (\"Please enter password of No. 1:\\n\"); scanf (\"%d\储存成员一密码

tail=*p_head; tail->next=NULL; for (i=2;ip=(member *)malloc(sizeof(member)); if (!p) return OVERFLOW; p->No=i; //储存成员序号

printf (\"Please enter password of No. %d:\\n\ scanf(\"%d\储存成员密码 tail->next=p; tail=p; }

tail->next=*p_head; return OK; }

status DeleteNode(member **pp) {

//此算法删除链表中的结点*pp,操作实质是将*pp下一结点复制到*pp后将其free

member *temp;

(*pp)->password=((*pp)->next)->password; (*pp)->No=((*pp)->next)->No; temp=(*pp)->next;

(*pp)->next=(*pp)->next->next; free(temp); return OK; }

1.5 结果与分析.4 测试及性能分析

1.此程序的时间复杂度是O(m*n)。

2.本次设计主要用到了循环链表的相关知识,求joseph环的问题。调试过程中,一开始没有想到“指向指针数据的指针变量”,使得问题一直没有明朗。

3.是否需要化简m值的语句:m=(m%n==0)?n:m%n;可根据m与总成员数的值的大小来判断。

当m〈=n时,则此步是可以删除的;

当m>n时,则此步最好不删除,特别是当输入的m值很大,则化简m值的操作是很必要。

4. 遇到的问题主要是:指针的指向的边界问题,但根据运行程序时的出错提

示,很快也就解决了。 5.测试的结果如下:

课题二 拓扑排序

2.1 问题的提出2.1 问题的提出

任务:编写函数实现图的拓扑排序。

程序所实现的功能: 建立对应的邻接表,对该图进行拓扑排序,并显示排序

结果。 输入:顶点数, 边数及各顶点信息(数据格式为整形) 输出:拓扑排序结果。

2. 2 概要设计

1.拓扑排序是指由某个集合上的一个偏序得到该集合上的一个全序。更直观地讲,一个偏序是自反的、反对称的,用图表示时每个点都有环且只有单向边。拓扑排序的任务是在这个偏序上得到一个全序,即得到一个完成整个项目的各步骤的序列。

2.解决拓扑排序的方法如下:

(1)在有向图中选一个没有前驱的顶点且输出之。 (2)从图中删除该顶点和所有以它为尾的弧。

重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况则说明有向图中存在环。具体的算法实现参照源程序。 3.构造邻接表图:typedef struct{

AdjList vertices; int vexnum,arcnum;

}Graph;//邻接表图

4. 为了避免重复检测入度为零的顶点,源程序中设了一个栈,暂存所有入度为零的顶点:typedef struct stack{

int *base;

int *top; int stacksize;

}sqstack;//栈的结构,存储图的顶点序号

2.3 流程图2.根据算法思想,画流程图如下:

开始 建立一个栈,存储图的 顶点的序号 设辅助数组indegree记录图的各顶点的入度值,并 将indegree数组各变量赋初值。 输入图的顶点数、边数 用邻接表法建图,并计算出 indegree数组中各变量值 根据indegree数组将入度 为0的顶点入栈 count对输出顶点计数 0=>count 栈不空 删除栈顶元素,赋给i 输出第i个顶点值 输出“拓扑 排序成功” count++ 将与第i个顶点链接的各顶点入度减1 顶点序号入栈 顶点入度为0 count2.4 源代码

//采用尾插法创的邻接图 #include using namespace std; const int MAX=20;

const int STACK_INIT_SIZE=100; const int ERROR=0;

typedef struct stack{

int *base;

typedef struct lnode {

typedef struct node2 {

typedef struct{

void Initstack(sqstack &s) {

s.base=new int; AdjList vertices; int vexnum,arcnum; char data; ArcNode *fristarc; int adjvex; struct lnode *next;

int *top; int stacksize;

}sqstack;//栈的结构,存储图的顶点序号

}ArcNode;//弧结点

}VNode,AdjList[MAX];//顶点数组,fristarc指向与顶点邻接的第一条弧

}Graph;//邻接表图

}

if(!s.base)

exit(0); s.top=s.base;

s.stacksize= STACK_INIT_SIZE;

void Push(sqstack &s,int &e) { }

int Emptystack(sqstack &s) { }

int Pop(sqstack &s,int &e) { }

void CreatGraph(Graph &G,int *indegree) {

cout<<\"请输入图的顶点数和弧数(且顶点数不能超过\"<>G.vexnum>>G.arcnum;

cout<<\"请输入顶点值:\"<for(int i=0;icin>>G.vertices[i].data; G.vertices[i].fristarc=NULL; if(s.base==s.top)

return ERROR; e=*--s.top; if(s.base==s.top)

return 1; return 0; else

*s.top++=e;

}

}

indegree[i]=0;

for(i=0;iint m,n; ArcNode *p;

cout<<\"请输入第\"<>m>>n; p=new ArcNode; if(!p)

exit(0);

indegree[n-1]++;//求每个顶点的入度值 p->adjvex=n-1;

p->next=G.vertices[m-1].fristarc; G.vertices[m-1].fristarc=p;

int Toposort(Graph &G,int *indegree) {

sqstack S; Initstack(S);

for(int i=0;iint count=0;

while(!Emptystack(S)) {

Pop(S,i);

cout<for(ArcNode *p=G.vertices[i].fristarc;p;p=p->next)//把与顶点 if(!indegree[i])

Push(S,i);

}

{ }

//相邻接的顶点的入度

int k=p->adjvex; if(!(--indegree[k]))

Push(S,k);

if(countreturn 0; return 1; else

} {

int main() Graph G; }

int *indegree; indegree=new int; if(!Toposort(G,indegree)) { } else { }

return 0;

cout<cout<<\"拓扑排序成功!\"<cout<<\"拓扑排序不成功!\"<CreatGraph(G,indegree);

2.5 结果与分析2.4 测试及性能分析 1.它的时间复杂度是O(G.vexnum+G.arcnum)。

2. 整个程序的关键就是采用尾插法创的邻接表图,运用栈来实现各个数值

的输入输出及存储。

3.注意*的使用,并不是什么情况下都用*,它有时候会造成数据破坏,利

用破坏的值进行运算,结果可想而知,所以,如果没返回值时,一般不要用。

4.为了避免重复检测入度为零的顶点,源程序中设了一个栈,暂存所有入

度为零的顶点,此程序段书写如下: InitStack(S); 栈

}//for }//while

5.测试的数据如下: 第一组结果(有向无环图):

for (i=0;icount = 0; //对输出顶点计数 while (!StackEmpty(S)){

Pop(S,i);printf(i,G.vertices[i].data);++count; //输出i号顶点

for (p=G.vertices[i].firstarc;p;p=p->next){

k=p->adj; //对i号顶点的每个邻接点的if(!(--indegree[k])) Push(S,k); //一旦入度为0,则入

并计数

入度减1

第二组结果(有向有环图):

第一组对应的图如下:

1 2 3 5 4

第二组对应的图如下:

1 2 3 5 4 课题三 纸牌游戏

3.1 问题的提出.1 问题的提出

任务:编号为1-52张牌,正面向上,从第2张开始,以2为基数,是2的倍数的牌翻一次,直到最后一张牌;然后,从第3张开始,以3为基数,是3的倍数的牌翻一次,直到最后一张牌;然后…从第4张开始,以4为基数,是4的倍数的牌翻一次, 直到最后一张牌;...再依次5的倍数的牌翻一次,6的,7的 直到 以52为基数的 翻过,输出:这时正面向上的牌有哪些?

输出的纸牌号为:

1 4 9 16 25 36 49

3. 2 概要设计

1.当每个号码每次遇到是某个数的倍数时,都会相应的翻一次,这样,每张牌会翻的次数就各不一样,可能很多次,也可能只有一两次,结果就只是要输出在经过各个不同次数的翻牌后,正面向上的牌都有哪几个。举例说明一下,比如24,第一次它是2的倍数时要从正面翻到背面,当进行到3时,就又要从背面翻回来,而到4时还要在翻,同理呢,到6.8.12„它都要来回的翻。如果它在多次的翻牌后,正面还向上了,那么它就是要输出的结果之一。

2.用#define OPPOSITE(i) i = i?0:1这个宏将牌的状态标志求反,也即为翻牌操作。

将所有的牌建立一个数组,运用for的循环嵌套执行以下操作:把52张牌初始化成正面朝上、控制基数和翻牌次数,判断最终的纸牌朝向并打印出结果,具体实现算法参看详细设计。

3.3流程图 根据算法思想,画流程图如下:

开始 设一个一维数组card[52],并将所有变量赋初值为0,表示牌正面朝上 2=>j j≤52 j=>k k≤52 j++ k%j=0 翻牌,如果card[k-1]为0,则变为1;如果为1,则变为0 k++ 输出card数组中正面朝上的牌的序号 结束

3.4 源代码

#include

#define OBVERSE 0 //正面朝上 #define ADVERSE 1 //背面朝上

#define OPPOSITE(i) i = i?0:1 //这个宏是将牌的状态标志求反,也即为翻牌操作

void main() {

int card[52];//52张牌 for (int i = 0; i < 52; i++) {

card[i] = OBVERSE; //将52张牌初始化成正面朝上 }

for (int j=2; j<=52; j++) //此层循环是控制基数的 {

for (int k = j; k <= 52 ; k++)//此层循环是控制从第几张牌开始 {

if (k%j == 0)//判断第k张牌除以基数j后的余数是否为0,如为0就是能整除 {

OPPOSITE(card[k-1]);//翻牌 } } }

for (int h = 0; h < 52; h++)//开始打印 {

if (card[h] == OBVERSE)//判断牌的状态是否为正面朝上 {

printf(\"第%d张牌正面朝上\\n\} } }

3.5 结果与分析

1.这题的时间复杂度是O(52)。

2.虽然本次程序的题目难度与其他问题相比不是很高,但仍有很多问题我们

是很容易忽视的,其一:在理解题目的要求时,注意翻牌的次数可能有多次;其二:for循环的嵌套使用在书写时很容易漏掉大括弧。

3.运用更多的基础算法,使得程序和算法思想得到更好的表现,为增强算法

的可读性,则算法改进如下: #include void main() {

int i,j,card[52];

for(i=0;i<52;i++)//52张牌所有状态均为1,即均为正面 card[i]=1;

for(j=2;j<=52;j++) //对52张牌(序号放在i里)对2,3...52(放在j里)按i+1是否是j的倍数进行状态翻转。 for(i=0;i<52;i++) if((i+1)%j==0) card[i]=card[i]?0:1; printf(\"positive card are:\");

for(i=0;i<52;i++)//对翻转处理后状态仍然是正面的(card保持为1)的将其编号输出。 { if(card[i]) printf(\"%d \ } }

4.测试的结果如下:

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo9.cn 版权所有 赣ICP备2023008801号-1

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务