线性表的应用(算法与数据结构课程设计)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
线性表的应用
一、问题描述
线性表有两种不同的存储结构,分别是顺序存储结构和链式存储结构,在实际中应用十分广泛。
本设计要求分别利用线性表的两种存储结构,设计算法完成对大数的阶乘、加法、乘法的求解。
二、基本要求
1、选择合适的存储结构实现大数存储;
2、设计算法,采用顺序存储结构完成大数的阶乘运算;
3、设计算法,采用链式存储结构完成大数的加法运算;
4、设计算法,选择合适的存储结构完成大数的乘法运算;
5、其中某一算法采用两种存储结构实现。
三、测试数据
1、阶乘运算的测试数据:63!
2、加法运算的测试数据: 9876876787+89678967559999
3、乘法运算的测试数据:9876876787×89678967559999
四、算法思想
1、阶乘运算的算法思想:一个数的阶乘,利用一个顺序表来存储结果,首先令L.elem[0]=1,其他全部赋值为零,再用for循环,从1至i完成阶乘运算,其中由于位数越乘越多,故将其按位存储在顺序表中,防止数据范围溢出,在逐位相乘中,利用for循环位数,如若有进位问题,每次运算时,此位保留的数位,t=L.elem[j]*i+jw; L.elem[j]=t%10;jw=t/10;如果满足j>=top && jw==0;程序跳出,进行下一步i运算,此处top位保留上一位的位数,如此运算下去,输出顺序表。
2、加法运算的算法思想:本运算分别采用了两种存储结构,链式和栈存储结构。
加法是两个数位数对齐,从低位向高位加的运算,如果在哪位有进位,则后一位,进行加法还要另加上前面的进位,由此将输入的字符大数,存入链表中,且改为整形存入,此时是的链表是倒序的,定义一个变量表示每次的进位jw=0,建立一个链表,让他存储结果,如此两链表的数相加,每次还要加上上次留下的进位,此为保留的数位:new->data =(p->data +q->data +jw)%10; new->next =NULL;jw =(p->data+q->data+jw)/10;当两个数是一场一短时,自然当相等的长度加完后在执行下面的判断,保留住剩下的数同时每次加上jw,最后就是当最后一位有进位时将最后一个链表值赋jw,由于现在此链表存储的结果是反序的,故将其压入栈中,让后再输出栈元素,就是想加的结果。
3、加法运算的算法思想:主要采用顺序存储结构,先从低位算起,只须要对应的位相加,再加上前一位的进位,使用变量jw存储,每次运算时加上jw运算,再去判断是否本位
是否有进位,有则将jw的值赋成本位进位数;没有进位,则给进位赋值0。
其中,若两个加数中那一个数的位数长,以位数长的作为循环变量;结束循环时,不仅仅是最后一位加完就停止,还应加入如果有进位,也要再循环一次。
如最后一位是9,进位是1,则相加时进位,要加上进位这一位值。
4、乘法运算的算法思想:传入的乘数和被乘数是以字符串形式放入的,为了要让指针指向最后一位,自己写了个函数StrNum2倒着赋值,同时因为传入和保存的都是字符,所以计算时要将字符转化为数字;从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果,之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果。
当然我们可以直接用这种方法,但要用多个数据来保存计算出的分结果,之后结果再相加得到最后结果,但是这样会浪费很多空间,所以我们可以再优化一下,就是只用一个顺序表来表示结果,先把第一位乘数与被乘数的结果保存在链表中,之后把存储结果的和第二次如此算的结果加在第一次的基础上,二者使用同一个顺序表来表示,以此类推,直到结束,这样就可以用一个顺序表来存储相乘后的结果。
另外在运算时乘和加都有进位时要处理,就和大数加法中进位采取方法一样,当前的值加上进位的值再看本位数字是否又有进位就行。
五、模块划分
1、 void factorial(),利用顺序存储结构完成大数阶乘运算;
2、int StrToNum1(SqList L,char *a),将字符串转换为整形数组,正向赋值;
3、void addition(),利用顺序存储结构完成大数的加法;
4、 void addition1(),将字符串转换为整形数组,反向传入链表中赋值,并利用链式存储结构完成大数的加法运算,最后用栈来输出结果;
5、 void StrToNum2(SqList *a,char *s),将字符串转换为整形数组,反向赋值;
6、void multiply(),利用顺序存储结构完成大数的乘法;
7、int main(),主函数。
六、数据结构//(ADT)
1、顺序表的顺序存储结构:
typedef struct {
ElemType *elem;
int length; } SqList;
2、顺序表的链式存储结构:
typedef struct LNode{
ElemType data;
struct LNode *next; } LNode,*LinkList;
3、链栈的存储结构:
typedef struct SNode{
ElemType data;
struct SNode *next;
} SNode, *LinkStack;
七、源程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
typedef int ElemType;
/* 1.顺序表的存储结构 */
typedef struct {
ElemType *elem;
int length; } SqList;
/* 2.链表的存储结构 */
typedef struct LNode{
ElemType data;
struct LNode *next; } LNode,*LinkList;
/* 3.链栈的存储结构 */
typedef struct SNode
{ ElemType data;
struct SNode *next;
} SNode, *LinkStack;
/* 4.初始化顺序表*/
void InitList(SqList *L)
{ L->elem=(ElemType*)malloc((2*N)*sizeof(ElemType)); L->length=0; }
/* 5.初始化链表 */
void InitLink(LinkList *L)
{
*L=(LinkList)malloc(sizeof(LNode));
(*L)->next=NULL; }
/*6.初始化链栈(没有头结点) */
void InitStack(LinkStack *top)
{ *top=NULL; }
/*7.销毁顺序表*/
void DestroyList(SqList *L)
{ free(L->elem); }
/*8.清空顺序表 */
void ClearList(SqList *L)
{ L->length=0; }
/*9.顺序表长度 */
int ListLength(SqList L)
{ return L.length; }
/*10.遍历顺序表并输出(数组下标法) */
void ListTravarsa(SqList L)
{ int i;
printf("\nList:\t");
for(i=0; i<L.length; i++)
printf("%d\t",L.elem[i]); }
/*11.入链栈 */
void Push(LinkStack *top, ElemType e)
{ LinkStack new;
new=(LinkStack)malloc(sizeof(SNode));
new->data=e;
new->next=*top; *top=new; }
/*12.遍历链栈并输出: 栈顶向栈底方向输出 */ void StackTraverse(LinkStack top)
{ LinkStack p;
p=top;
while (p)
{ printf("%d",p->data);
p=p->next; }
}
/*13.后接法建立链表 */
void CreateList2(LinkList *L, char *a)
{ LinkList p,new; int i; int len=strlen(a); p=*L;
for(i=0;a[i]!='\0';i++)
{
new=(LinkList)malloc(sizeof(LNode)); new->data=a[len-i-1]-48;
p->next=new; p=p->next; }
p->next=NULL;
}
/*14.实现阶乘的运算 */
void factorial()
{ SqList L; int n,i,j,t,jw,top;
InitList(&L);
printf("\n输入求阶乘的数:");
scanf("%d",&n);
L.elem[0]=1; top=0;
for(i=1;i<N;i++)
L.elem[i]=0;
for(i=1; i<=n; i++)
{ jw=0;
for(j=0; j<100; j++)
{ t=L.elem[j]*i+jw;
L.elem[j]=t%10;
jw=t/10;
if (j>=top && jw==0)
{top=j; break;}
}
}
printf("%d!=",n);
for(i=top; i>=0; i--)
printf("%d",L.elem[i]);
}
/*15.(链表和栈)实现加法的运算 */
void addition1()
{ int jw=0;//jw表示进位
char a[N],b[N];LinkStack top;
LinkList L1,L2,sum,p,q,r,new;
printf("\n(链表和栈方法)输入大数加法的第一个数:");
scanf("%s",a);
printf("\n(链表和栈方法)输入大数加法的第二个数:");
scanf("%s",b);
InitLink(&L1);InitLink(&L2);InitLink(&sum);InitStack(&top); CreateList2(&L1,a);
CreateList2(&L2,b);
p=L1->next;q=L2->next;
r=sum;
while(p&&q)
{
new=(LinkList)malloc(sizeof(LNode));
new->data=(p->data+q->data+jw)%10;
new->next=NULL;
jw=(p->data+q->data+jw)/10;
p=p->next;q=q->next;
r->next=new;r=r->next;
}
while(p)
{
new=(LinkList)malloc(sizeof(LNode));
new->data=(p->data+jw)%10;
new->next=NULL;
jw=(p->data+jw)/10;
p=p->next;
r->next=new;r=r->next;
}
while(q)
{
new=(LinkList)malloc(sizeof(LNode));
new->data=(q->data+jw)%10;
new->next=NULL;
jw=(q->data+jw)/10;
q=q->next;
r->next=new;r=r->next;
}
if(jw!=0)
{
new=(LinkList)malloc(sizeof(LNode));
new->data=jw;//解决最高位的进位问题
new->next=NULL;
r->next=new;r=r->next;
}
printf("%s+%s=",a,b);
r=sum->next;
while(r)
{
Push(&top,r->data);
r=r->next;
}
StackTraverse(top);
}
/*16.(顺序表)实现大数的加法运算 */
int StrToNum1(SqList L,char *a)
{int i;
for(i=0;a[i]!='\0';i++) //把字符串转化成整形数组 {
L.elem[i]=a[i]-48;
}
i--;//i回退1,则i对应的最后一个字符就不是'\0' return i;
}
void addition()
{ int i,j,k,m,jw=0;//jw表示进位
char a[N],b[N];
SqList L1,L2,sum;
printf("\n\n输入大数加法的第一个数:");
scanf("%s",a);
printf("输入大数加法的第二个数:");
scanf("%s",b);
InitList(&L1);InitList(&L2); InitList(&sum);
i=StrToNum1(L1,a);
j=StrToNum1(L2,b);
if(i>j)//把位数较大的下标记下来
k=i;
else
k=j;
m=k+1;
for(k=k+1;i>=0 && j>=0;k--,i--,j--)//求和
{
sum.elem[k]=(L1.elem[i]+L2.elem[j]+jw)%10;
jw=(L1.elem[i]+L2.elem[j]+jw)/10;
}
if(j<0)
{ for(;i>=0;i--,k--)
{ sum.elem[k]=(L1.elem[i]+jw)%10;
jw=(L1.elem[i]+jw)/10;
}
}
if(i<0)
{ for(;j>=0;j--,k--)
{ sum.elem[k]=(L2.elem[j]+jw)%10;
jw=(L2.elem[j]+jw)/10;
}
}
sum.elem[k]=jw;//解决最高位的进位问题
printf("%s+%s=",a,b);
for(k=0;k<=m;k++)
{ if(sum.elem[0]==0 && k==0)
continue;
else
printf("%d",sum.elem[k]);
}
}
/*17.顺序表赋值*/
void StrToNum2(SqList *a,char *s)
{ int i;
int len = strlen(s);
//对数组初始化
for(i = 0; i < N; ++i)
(*a).elem[i]=0;
for(i = 0; i < len; ++i)
(*a).elem[len-1-i]=*(s+i)-'0';//给*a赋值
}
/*18.实现大数的乘法运算*/
void multiply()//将数组a与数组b逐位相乘以后存入数组c
{ SqList a,b,c;
char s1[N],s2[N];
int i,j;
int k = 2*N-1;
InitList(&a);InitList(&b); InitList(&c);
printf("\n\n输入大数乘法的第一个数:");
scanf("%s",s1);
printf("输入大数乘法的第二个数:");
scanf("%s",s2);
StrToNum2(&a,s1);
StrToNum2(&b,s2);
for(i = 0; i < 2 * N; ++i)
c.elem[i]=0;////数组初始化
for(i = 0; i < N; ++i)
for(j = 0; j < N; ++j)
c.elem[i+j]+=a.elem[i]*b.elem[j];//每次相乘的加上上次的运算结果 for(i = 0; i < 2 * N - 1; ++i)//用来移位和进位
{ c.elem[i+1]+=c.elem[i]/10;//将十位上的数向前进位,并加上原来这个位上的数 c.elem[i]=c.elem[i]%10;//将剩余的数存原来的位置上
}
printf("%s*%s=",s1,s2);
while(c.elem[k]==0)
k--;
for(i = k;i >= 0; --i)
printf("%d",c.elem[i]);
}
void Help()
{ printf("***************************************************\n");
printf(" * 大数运算 * \n");
printf("---------------------------------------------------\n");
printf("| * * * * * * * * * * * * * * * * * * * * * |\n");
printf("| * 1.大数的阶乘运算 * |\n");
printf("| * 2.大数的加法运算 * |\n");
printf("| * 3.大数的加法运算 * |\n");
printf("| * 4.大数的乘法运算 * |\n");
printf("| * 5.帮助 * |\n");
printf("| * 0.退出 * |\n");
printf("| * * * * * * * * * * * * * * * * * * * * * |\n");
printf("---------------------------------------------------\n");
}
main()
{ int choice=0;
Help();
do{ printf("\n**************************************************\n");
printf("Pleasa select(0,1,2,3,4,5):");
scanf("%d",&choice);
if(choice<0||choice>5) continue;
printf("\n**************************************************\n");
switch(choice)
{case 1:factorial();
break;//阶乘
case 2:addition1();
break;//(链表和栈实现的加法)
case 3:addition();
break;//(顺序表实现的加法)
case 4:multiply();
break;//乘法
case 5:Help();
break;
case 0: exit(0);
}
printf("\n");
}while(1);
return 0;
}
八、测试情况
程序的测试结果如下:
1、阶乘运算:
2、(链式和栈存储结构)加法运算
3、(顺序表)加法运算
4、乘法运算
九、参考文献
1、严蔚敏,《数据结构 C语言》,清华大学出版社。
2、谭浩强,《c语言程序设计》,清华大学出版社。
小结
经过几个月的数据结构的学习和各种实践,我对数据结构这门课有了颇深的了解,数据结构是一门研究非数值计算的程序设计问题中的操作对象以及它们之间的关系的操作的学科,在本次课程设计中,定义存储结构均采用了数据结构中的抽象数据类型,而抽象数据类型是指一个数据模型以及定义在改模型上的一组操作,抽象数据类型的定义仅仅取决于它的一组逻辑特性,而与计算机内部如何表示和实现无关,即不论其内部结构如何变化,只要它的数学特性不变,都不影响其外部的使用。
下面谈谈这次的实验程序设计。
本次课程设计我们选择的题目是:选择合适存储结构实现大数运算。
首先需要先解释的是这里大数计算的因数和结果精度一般是少则数十位,多则几万位。
在C语言中定义的类型中精度最多只有二十多位,因而在此我们采取用线性表的顺序和链表存储结构的方式来存放大数。
在程序编写之初,就想好了算法,但当真正来写的时候又困难重重,这又不免验证了那句“口头容易,实做难。
”最终小组合作还是勉强编出了一个完整的程序,但随之而来的一个问题又来了,虽然我们程序算法完全正确,但实际运行存在问题:第一在顺序存储结构中我们采用单个存储大数,大数每运算一次,就分配空间保存一次,这样的算法导致整个程序的效率性极低(特别在乘法运算中);第二在链式存储结构中为了使代码简短,高效,首先每个链表节点的元素所存的数据小的可怜,只存储了一位数据,但这是最容易想到的,模仿十进制那样进位,但这并没考虑机器的计算的情况,在我们常用的32位计算中,CPU 中加减乘除的一次运算是32位的值,也就是说2的32次方的一个值,这就是说1加上1
的CPU工作量和小于2的32的两位数相加是用的相同的周期。
为了避免运算结果大于2
的32次,因为大于的话又会造成一次CPU周期运算,但这样当然也可以做。
但建议用下面方法,考虑乘法的原因,两个2的16次方相乘就是2的32次方的情况,所以我们定义了本程序算法,结点数巨减,主要是开内存方面,有了极大的减少。
经过此次课程设计,让我们了解到一个好的代码,在于它的算法和数据结构,即程序=算法+数据结构,在写一个好的程序之前我们首先必须确定我们应该选用哪种数据结构,然后实现设计要求,同时也应多考虑哪种更高效而且时间复杂度和空间复杂度比较小的算法。
在程序结束的时候,自己在网上查看了一些关于大数的运算应用,原来此问题在现实生活中实用性很强,诸如密码学特别是RSA加密方面、物理研究学、生物学、化学中有特殊应用。
同时这些日子里小组同学之间互相学习,探讨算法,才得出此完善的程序,深刻感受到团队合作的力量,当大数运算顺利运行时,我们一个个都感到深深的欣慰,也从实践中学到很多知识。
wilyes11收集博客(与学习无关):/u/1810231802。