一、调试成功程序及说明
1、碰撞的小球
题目:
1、 数轴上有一条长度为L(L为偶数)的线段,左端点在原点,右端点在坐标L处。有n个不计体积的小球在线段上,开始时所有的小球都处在偶数坐标上,速度方向向右,速度大小为1单位长度每秒;
2、 当小球到达线段的端点(左端点或右端点)的时候,会立即向相反的方向移动,速度大小仍然为原来大小;
3、 当两个小球撞到一起的时候,两个小球会分别向与自己原来移动的方向相反的方向,以原来的速度大小继续移动。
4、 现在,告诉你线段的长度L,小球数量n,以及n个小球的初始位置,请你计算t秒之后,各个小球的位置。
算法思想:
对于本体,由于涉及到多次的比较,判断,所有我使用的数据结构为顺序表,顺序表在访问第i个元素上面比链表的时间复杂度要低。我用一个顺序表来存储他们的位置,用另一个顺序表来存储他们的速度,再用一个顺序表来进行排序。这样我们就在判断速度方向是否改变的时候,只需要判断自己和左边(或者右边,只用选一边进行判断)的位置进行判断,当然端点要额外判断。这样时间复杂度为O(n2+nt),其中n2来自于对数据的排序(对于选择排序和冒泡排序时间复杂度一样),nt来自与判断位置是否重合(相遇)由于我们开了三个大小为的顺序表,所以我们的空间复杂度为:O(n)。
运行结果:
结果分析:
从结果上来看,答案是正确的,而且有输入的提示信息,方便使用者输入相应的数值。
附源代码:
#include "stdlib.h"
#include "math.h"
#include "stdio.h"
#define N 100
main()
{
int x[N],v[N],pos[N]; //存储小球的位置信息和速度信息
int i,j,temp; //辅助变量,i:用来进行对小球的位置信息和速度信息的初始化,,,,j,k:后面运动过程中进行改变球的位置信息和速度信息
int n,L,t; //小球的个数n,线段的长度L,以及你需要计算t秒之后小球的位置
printf("请输入小球的个数n,线段的长度L,以及你需要计算t秒之后小球的位置: "); //提示输入信息
scanf("%d %d %d",&n,&L,&t);
while(L%2) //判断线段的长度L是否是偶数
{
printf("线段的长度L输入错误,L必须是偶数!\n");
printf("请输入线段的长度L: ");
scanf("%d",&L);
}
for(i = 1;i<=n;i++) //初始化小球的位置信息和速度信息
{
printf("请输入第%d个小球的初始坐标: ",i);
scanf("%d",&x[i]);
v[i] = 1; //初始速度全为1
while(x[i]%2) //判断第i+1个小球的初始坐标是否是偶数
{
printf("第%d个小球的初始坐标输入错误,L必须是偶数!\n",i);
printf("请输入第%d个小球的初始坐标: ",i);
scanf("%d",&x[i]);
}
pos[i] = i;
}
x[0] = 0;x[n+1] = L;v[0] = 0;v[n+1] = 0;pos[0] = 0;pos[n+1] = n+1;
for(i = 1;i<=n;i++)
{
for(j = i+1;j<=n;j++)
{
if(x[pos[i]] > x[pos[j]])
{
temp = pos[i];
pos[i] = pos[j];
pos[j]= temp;
}
}
}
for(j=1;j<=t;j++)//遍历运功过程每秒时间
{
for(i = 1;i<=n;i++)
x[i] = x[i]+v[i];
for(i = 1;i<=n;i++)//遍历每个小球,判断小球是否运动反向
{
if(x[pos[i]] == x[pos[i-1]])
{
v[pos[i]] = -v[pos[i]];
v[pos[i-1]] = -v[pos[i-1]];
}
if(i == n)
if(x[pos[i]] == x[pos[i+1]])
v[pos[i]] = -v[pos[i]];
}
}
printf("%d秒后:\n",t);
for(i = 1;i<=n;i++)
printf("第%d个小球的位置为:%d\n",i,x[i]);
}
2、
题目:稀疏矩阵的快速转置B=A’
算法思想:
按照A. data中三元组的次序进行转置,并将转置后的三元组置人B中恰当的位置。如果能预先确定矩阵B中每-列(即A中每一行)的第一 个非零元在B. data中应有的位置,那么在对A. data中的三元组依次作转置时,便可直接放到B. data中怡当的位置上去。为了确定这些位置.在转置前,应先求得A的每一列中非零元的个数,进而求得每一列的第一个非零元在B. data中应有的位置。在此,需要附设num和cpot两个向量。num[col]表示矩阵 A中第col列中非零元的个数,pot[co]指示A中第col列的第一个非零元在b. data中的恰当位置。显然有:
cpot[1]=1;cpot[col]=cpot[col-1]+num[col-1]
这里我们相当于空间换时间了,我们多用了俩个辅助向量,空间复杂度为:O(n),但是时间复杂度降为:O(nu+tu)
运行结果:
结果分析:
运行结果正确,可以快速算出矩阵的转置;
附源程序:
MA reversema(MA *A)//快速转置
{
MA B; //矩阵B为矩阵A转置以后的矩阵;
int i,col,p; //辅助变量;
int num[A->nu+1],cpot[A->nu+1]; //num记录每一列中非0元素的个数,cpot记录该列第一个元素的存储位置
B.mu = A->nu; //初始化矩阵B
B.nu = A->mu; //初始化矩阵B
B.tu = A->tu; //初始化矩阵B
if(A->tu) //如果矩阵A为零矩阵,那么就直接返回B就行了
{
for(i = 1;i<=A->nu;i++) //初始化num
num[i] = 0;
for(i = 1;i<=A->tu;i++) //得到num
num[A->T[i].j]++;
cpot[1] = 1; //初始化cpot
for(i = 2;i<=A->nu;i++) //得到cpot
cpot[i] = cpot[i-1] + num[i-1];
for(i = 1;i<=A->tu;i++) //将A中的三元组赋值到对应的B中的三元组中
{
col = A->T[i].j;
p = cpot[col];
B.T[p].i = A->T[i].j;
B.T[p].j = A->T[i].i;
B.T[p].data = A->T[i].data;
cpot[col]++;
}
}
return B;
}
3.题目:稀疏矩阵加法
算法思想:
开一个新的矩阵C,用来存放矩阵A+矩阵B的值,当A中某非0元素和矩阵B中某非0元素的行相等时,我们就返回他们的和给C,否则判断行或者列的大小关系,判断应该把谁先给矩阵C,当然要注意,跳出while循环时,可能有一个还有剩余,要将剩下的赋值过去。
运行结果:
结果分析:
结果运行正确,可以正确算出矩阵A和矩阵B的和
附源程序:
MA addition(MA *A,MA *B)
{
MA C; //将两个稀疏矩阵相加,并返回结果
int i,j,k; //辅助变量
i = j = k = 1; //赋初值
if(A->mu != B->mu || A->nu != B->nu) //判断矩阵A和矩阵B的维数,以此来看是否可以相加
{
printf("矩阵A和矩阵B的维数不完全相同,不可以做加法!");
exit(0);
}
C.mu = A->mu; //矩阵C初始化
C.nu = A->nu; //矩阵C初始化
while(i<=A->tu&&j<=B->tu) //循环条件,当i和j都没有把矩阵A和矩阵B中非零元素遍历完
{
if(A->T[i].i == B->T[j].i) //判断行关系
{
if(A->T[i].j == B->T[j].j) //判断列关系
{
C.T[k].i = A->T[i].i;
C.T[k].j = A->T[i].j;
C.T[k].data = A->T[i].data + B->T[j].data;
k++;
i++;
j++;
}
else if(A->T[i].j < B->T[j].j)
{
C.T[k].i = A->T[i].i;
C.T[k].j = A->T[i].j;
C.T[k].data = A->T[i].data;
k++;
i++;
}
else
{
C.T[k].i = B->T[j].i;
C.T[k].j = B->T[j].j;
C.T[k].data = B->T[j].data;
k++;
j++;
}
}
else if(A->T[i].i < B->T[j].i)
{
C.T[k].i = A->T[i].i;
C.T[k].j = A->T[i].j;
C.T[k].data = A->T[i].data;
k++;
i++;
}
else
{
C.T[k].i = B->T[j].i;
C.T[k].j = B->T[j].j;
C.T[k].data = B->T[j].data;
k++;
j++;
}
}
while(i<=A->tu) //如果是j先遍历完了,但是i没有遍历完,那么就将矩阵A中剩余元素赋值给矩阵C
{
C.T[k].i = A->T[i].i;
C.T[k].j = A->T[i].j;
C.T[k].data = A->T[i].data;
k++;
i++;
}
while(j<=B->tu) //如果是i先遍历完了,但是j没有遍历完,那么就将矩阵B中剩余元素赋值给矩阵C
{
C.T[k].i = B->T[j].i;
C.T[k].j = B->T[j].j;
C.T[k].data = B->T[j].data;
k++;
j++;
}
C.tu = k-1; //注意,我这里是先赋值,再k++,所以最后k的值比C中非零元素个数多1,所以要减去。
return C;
}
二、小结
写完这次作业以后,我收获很多,这道题我以前写过类似的,但是没有想到过每个小球只会和周围的两个球发生碰撞,我以前会采用遍历的方式来做,那么时间复杂度就为:,空间复杂度为:相比于学过数据结构以后的我设计出来的算法,以前的算法时间复杂度太大了,所以感觉学了数据结构以后收获颇丰。
矩阵这里,我以前不会用三元组来表示,都是单纯的用二维数组来做的,那样的话,时间复杂度很高,所以学完了三元组感觉可以提升自己的矩阵处理能力了。
(附上以前写的第一题的代码和结果,当然按照这道题的意思改了改原来的代码)
main()
{
int x[N],v[N]; //存储小球的位置信息和速度信息
int i,j,k; //辅助变量,i:用来进行对小球的位置信息和速度信息的初始化,,,,j,k:后面运动过程中进行改变球的位置信息和速度信息
int n,L,t; //小球的个数n,线段的长度L,以及你需要计算t秒之后小球的位置
printf("请输入小球的个数n,线段的长度L,以及你需要计算t秒之后小球的位置: "); //提示输入信息
scanf("%d %d %d",&n,&L,&t);
while(L%2) //判断线段的长度L是否是偶数
{
printf("线段的长度L输入错误,L必须是偶数!\n");
printf("请输入线段的长度L: ");
scanf("%d",&L);
}
for(i = 0;i<n;i++) //初始化小球的位置信息和速度信息
{
printf("请输入第%d个小球的初始坐标: ",i+1);
scanf("%d",&x[i]);
v[i] = 1; //初始速度全为1
while(x[i]%2) //判断第i+1个小球的初始坐标是否是偶数
{
printf("第%d个小球的初始坐标输入错误,L必须是偶数!\n",i+1);
printf("请输入第%d个小球的初始坐标: ",i+1);
scanf("%d",&x[i]);
}
}
for(j = 0;j<t;j++) //不考虑初始位置相同的情况
{
for(i = 0;i<n;i++) //找到下一秒小球的位置,对小球位置进行更新
x[i] = x[i]+v[i];
for(i = 0;i<n;i++)
{
for(k = i+1;k<n;k++)
{
if(x[i] == x[k])
{
v[i] = -v[i];
v[k] = -v[k];
continue; //因为所有小球的初始位置都为偶数,而且线段的长度为偶数,可以证明,不会有三个小球同时相撞
}
}
if(x[i] == 0||x[i] == L)
{
v[i] = -v[i];
}
}
}
printf("%d秒后小球的位置坐标为:\n",t);
for(i = 0;i < n;i++)
printf("第%d个小球的位置为:%d\n",i+1,x[i]);
}