目录
一,设计内容
主要完成手机运动信息的管理。主要功能包括:用户信息的管理、运动信息的管理、查看运动排行榜、定制运动路线、数据文件的加载和保存。
二,系统功能模块图
三,主菜单
主菜单及各自菜单代码如下:
#include <iostream>
#include "menu.h"
using namespace std;
void menu1()
{
cout<<"************手机运动信息管理系统************"<<endl;
cout<<endl;
cout<<" 1,用户信息管理 2,运动信息管理 "<<endl;
cout<<" 3,运动排行榜 4,定制运动路线 "<<endl;
cout<<" 5,读取数据文件 0,退出 "<<endl;
cout<<endl;
cout<<"********************************************"<<endl;
}
void menu2()
{
cout<<"**************用户信息管理**************"<<endl;
cout<<endl;
cout<<" 1,添加用户 2,删除用户 "<<endl;
cout<<" 3,查询用户 4,修改用户 "<<endl;
cout<<" 0,返回上一级 "<<endl;
cout<<endl;
cout<<"****************************************"<<endl;
}
void menu3()
{
cout<<"**************运动信息管理**************"<<endl;
cout<<endl;
cout<<" 1,添加用户运动信息 "<<endl;
cout<<" 2,修改用户运动信息 "<<endl;
cout<<" 3,显示所有用户运动信息 "<<endl;
cout<<" 0,返回上一级 "<<endl;
cout<<endl;
cout<<"****************************************"<<endl;
}
void menu4()
{
cout<<"**************运动排行榜*************"<<endl;
cout<<endl;
cout<<" 1,当天排行榜 "<<endl;
cout<<" 2,连续运动排行榜 "<<endl;
cout<<" 3,指定用户的一周排行榜 "<<endl;
cout<<" 0,返回上一级 "<<endl;
cout<<endl;
cout<<"*************************************"<<endl;
}
void menu5()
{
cout<<"**************定制运动路线**************"<<endl;
cout<<endl;
cout<<" 1,绘制运动路线 "<<endl;
cout<<" 2,选择运动路线 "<<endl;
cout<<" 0,返回上一级 "<<endl;
cout<<endl;
cout<<"****************************************"<<endl;
}
void menu6()
{
int n;
cout<<"**************读取数据文件**************"<<endl;
cout<<endl;
cout<<" 1,数据加载 "<<endl;
cout<<" 2,数据保存 "<<endl;
cout<<" 0,返回上一级 "<<endl;
cout<<endl;
cout<<"****************************************"<<endl;
}
四,重点模块实现
1,用户信息,运动信息及运动地点和距离的存储
整体用户信息都使用线性表存储,User结构体里面存储一行信息,分别包括用户的id号,姓名,性别,年龄,最近7天的运动步数,7天运动的平均步数以及运动的天数。
地点图及地点间的距离使用邻接矩阵存储。
存储的结构体代码如下:
#ifndef _LIST_H
#define _LIST_H
typedef int status;
typedef struct {
char id[20];
char name[20];
char sex[10];
int age;
int step[7];
double avgst;
int day;
}User;
typedef struct {
User *elem;
int length;
}SqList;
status InitList(SqList &L);
#define Maxint 32767//表示无穷大
#define MVNum 20//最大顶点数
typedef char VerTexType;//设顶点的数据类型为字符型
typedef double ArcType;//假设边的权值类型为浮点型
typedef struct{
VerTexType vexs[MVNum][50];//顶点表
ArcType arcs[MVNum][MVNum];//邻接矩阵
int vexnum,arcnum;//图的当前点数和边数
}AMGraph;
#endif
2,数据文件的加载和保存
这部分主要是先从txt文件中读入用户信息和运动地点和距离的信息。
附上C++读取和写入txt文件的传送门
加载和保存的代码如下:
status Readtxtlist(SqList &L)
{
int m=0;
int n;
fstream f;
f.open("user.txt",ios::in);
while(f>>L.elem[m].id>>L.elem[m].name>>L.elem[m].sex>>L.elem[m].age)
{
n=0;
while(f>>L.elem[m].step[n])
{
n++;
if(n==7)
break;
}
f>>L.elem[m].avgst;
f>>L.elem[m].day;
m++;
}
L.length=m;
f.close();
for(int i=0;i<L.length;i++)
{
cout<<left<<setw(16)<<L.elem[i].id;
cout<<left<<setw(8)<<L.elem[i].name;
cout<<left<<setw(8)<<L.elem[i].sex;
cout<<left<<setw(8)<<L.elem[i].age;
for(int j=0;j<7;j++)
{
cout<<left<<setw(8)<<L.elem[i].step[j];
}
cout<<left<<setw(16)<<L.elem[i].avgst;
cout<<left<<setw(8)<<L.elem[i].day;
cout<<endl;
}
return OK;
}
status Readtxtgraph(AMGraph &G)
{
int i=0,j,k;
double w;
int s;
char s1[50],s2[50];
fstream f;
f.open("place.txt",ios::in);
while(f>>s>>G.vexs[i])
{
i++;
}
f.close();
G.vexnum=i;
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
{
G.arcs[i][j]=Maxint;//边的权值均设置为无穷大
}
}
i=0;
f.open("distance.txt",ios::in);
while(f>>s1>>s2>>w)
{
k=LocateVex(G,s1);
j=LocateVex(G,s2);//确定v1,v2在G中的位置
G.arcs[k][j]=w;
G.arcs[j][k]=w;
i++;
}
f.close();
G.arcnum=i;
cout<<"运动地点名称:"<<endl;
for(i=0;i<G.vexnum;i++)
{
cout<<G.vexs[i];
cout<<endl;
}
cout<<"各运动地点间距离得出的邻接矩阵:"<<endl;
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
{
if(G.arcs[i][j]==32767)
cout<<left<<setw(4)<<"∞";
else
cout<<left<<setw(4)<<G.arcs[i][j];
}
}
return OK;
}
status LocateVex(AMGraph G,char *u)
{
int o;
for(o=0;o<G.vexnum;o++)
{
if(strcmp(G.vexs[o],u)==0)
return o;
}
return -1;
}
status Writetxt(SqList &L)
{
int i;
fstream f;
f.open("user.txt",ios::out);
for(int i=0;i<L.length;i++)
{
f<<left<<setw(16)<<L.elem[i].id;
f<<left<<setw(8)<<L.elem[i].name;
f<<left<<setw(8)<<L.elem[i].sex;
f<<left<<setw(8)<<L.elem[i].age;
for(int j=0;j<7;j++)
{
f<<left<<setw(8)<<L.elem[i].step[j];
}
f<<left<<setw(16)<<L.elem[i].avgst;
f<<left<<setw(8)<<L.elem[i].day;
f<<endl;
}
f.close();
return OK;
}
3,用户操作,运动操作和排行榜操作
这部分内容主要是对线性表进行增删改查和排序的操作,在这里的排序操作使用的都是冒泡排序。
部分排序代码如下:
status Sortlist(SqList &L)
{
int i,j;
User k;
for(i=0;i<L.length;i++)
{
for(j=0;j<L.length-1-i;j++)
{
if(L.elem[j].step[6]<L.elem[j+1].step[6])
{
k=L.elem[j];
L.elem[j]=L.elem[j+1];
L.elem[j+1]=k;
}
}
}
return OK;
}
4,运动路线
4.1,绘制运动路线
绘制运动路线主要用到了C++语言图像库graphics.h。
附上C++语言图像库graphics.h的安装教程传送门
绘制运动路线功能代码如下:
status Protractgraph(AMGraph G)
{
char s[5];
int i,j=5;
initgraph(1200,600);
circle(100,100,20);
sprintf(s,"%d",12);
outtextxy(90,90,s);
circle(100,175,20);
sprintf(s,"%d",13);
outtextxy(90,165,s);
circle(100,250,20);
sprintf(s,"%d",16);
outtextxy(90,240,s);
circle(240,175,20);
sprintf(s,"%d",17);
outtextxy(230,165,s);
circle(240,250,20);
sprintf(s,"%d",19);
outtextxy(230,240,s);
circle(380,250,20);
sprintf(s,"%d",15);
outtextxy(370,240,s);
circle(520,175,20);
sprintf(s,"%d",18);
outtextxy(510,165,s);
circle(520,250,20);
sprintf(s,"%d",3);
outtextxy(510,240,s);
circle(380,325,20);
sprintf(s,"%d",2);
outtextxy(370,315,s);
circle(660,25,20);
sprintf(s,"%d",14);
outtextxy(650,15,s);
circle(660,100,20);
sprintf(s,"%d",11);
outtextxy(650,90,s);
circle(660,175,20);
sprintf(s,"%d",10);
outtextxy(650,165,s);
circle(660,250,20);
sprintf(s,"%d",0);
outtextxy(650,240,s);
circle(660,325,20);
sprintf(s,"%d",1);
outtextxy(650,315,s);
circle(800,250,20);
sprintf(s,"%d",4);
outtextxy(790,240,s);
circle(800,325,20);
sprintf(s,"%d",5);
outtextxy(790,315,s);
circle(800,400,20);
sprintf(s,"%d",7);
outtextxy(790,390,s);
circle(940,325,20);
sprintf(s,"%d",6);
outtextxy(930,315,s);
circle(940,400,20);
sprintf(s,"%d",8);
outtextxy(930,390,s);
circle(1080,325,20);
sprintf(s,"%d",9);
outtextxy(1070,315,s);
line(120,100,640,25);
sprintf(s,"%.1f",5.1);
outtextxy(380,62.5,s);
line(120,100,640,100);
sprintf(s,"%.1f",1.4);
outtextxy(380,100,s);
line(120,100,640,175);
sprintf(s,"%.1f",1.5);
outtextxy(380,137.5,s);
line(100,120,100,155);
sprintf(s,"%.1f",1.7);
outtextxy(100,137.5,s);
line(100,195,100,230);
sprintf(s,"%.1f",1.9);
outtextxy(100,212.5,s);
line(120,175,220,175);
sprintf(s,"%.1f",3.1);
outtextxy(170,175,s);
line(115,190,225,235);
sprintf(s,"%.2f",0.62);
outtextxy(170,212.5,s);
line(120,250,220,250);
sprintf(s,"%.1f",2.3);
outtextxy(170,250,s);
line(240,195,240,230);
sprintf(s,"%.1f",3.3);
outtextxy(240,212.5,s);
line(225,190,115,235);
sprintf(s,"%.1f",1.8);
outtextxy(190,192.5,s);
line(260,175,500,175);
sprintf(s,"%.1f",3.5);
outtextxy(380,175,s);
line(260,250,360,250);
sprintf(s,"%.2f",0.67);
outtextxy(310,250,s);
line(380,230,500,175);
sprintf(s,"%.1f",1.4);
outtextxy(440,202.5,s);
line(400,250,500,250);
sprintf(s,"%.1f",1.8);
outtextxy(450,250,s);
line(520,195,520,230);
sprintf(s,"%.1f",2.1);
outtextxy(520,212.5,s);
line(540,250,640,250);
sprintf(s,"%.1f",1.4);
outtextxy(590,250,s);
line(660,195,660,230);
sprintf(s,"%.1f",2.8);
outtextxy(660,212.5,s);
line(660,270,660,305);
sprintf(s,"%.2f",0.91);
outtextxy(660,287.5,s);
line(660,45,660,80);
sprintf(s,"%.1f",5.2);
outtextxy(660,62.5,s);
line(660,120,660,155);
sprintf(s,"%.2f",0.43);
outtextxy(660,137.5,s);
line(680,250,780,250);
sprintf(s,"%.1f",2.6);
outtextxy(730,250,s);
line(800,270,800,305);
sprintf(s,"%.1f",3.0);
outtextxy(800,287.5,s);
line(800,345,800,380);
sprintf(s,"%.1f",3.9);
outtextxy(800,362.5,s);
line(820,325,920,325);
sprintf(s,"%.1f",2.4);
outtextxy(870,325,s);
line(820,400,920,400);
sprintf(s,"%.1f",1.8);
outtextxy(870,400,s);
line(940,345,940,380);
sprintf(s,"%.1f",2.7);
outtextxy(940,362.5,s);
line(960,325,1060,325);
sprintf(s,"%.1f",3.8);
outtextxy(1010,325,s);
line(1080,345,960,400);
sprintf(s,"%.1f",2.9);
outtextxy(1020,372.5,s);
line(380,305,240,270);
sprintf(s,"%.1f",1.4);
outtextxy(310,287.5,s);
line(380,305,380,270);
sprintf(s,"%.1f",1.8);
outtextxy(380,287.5,s);
line(380,305,520,270);
sprintf(s,"%.1f",0.9);
outtextxy(450,287.5,s);
line(400,325,640,325);
sprintf(s,"%.2f",0.44);
outtextxy(520,325,s);
line(50,100,100,100);
line(50,100,50,325);
sprintf(s,"%.1f",1.2);
outtextxy(50,212.5,s);
line(50,325,360,325);
for(i=0;i<10;i++)
{
sprintf(s,"%d",i);
outtextxy(10+j,500,s);
outtextxy(25+j,500,G.vexs[i]);
j+=120;
}
j=5;
for(;i<G.vexnum;i++)
{
sprintf(s,"%d",i);
outtextxy(10+j,525,s);
outtextxy(25+j,525,G.vexs[i]);
j+=120;
}
getch();
closegraph();
return OK;
}
绘制运动路线功能展示:
4.2,选择运动路线
选择运动路线功能主要实现输入一个地点,程序会给出此地点到其他所有地点的最短路线及此路线的距离。
选择运动路线功能代码如下:
status Selectgraph(AMGraph G,char *s)
{
int n;
for(n=0;n<G.vexnum;n++)
{
if(strcmp(G.vexs[n],s)==0)
break;
}
if(n==G.vexnum)
return ERROR;
int p[G.vexnum][G.vexnum];
int i,S[G.vexnum],j,k;
int a[G.vexnum];
int num=1,u;
double dist[G.vexnum],wmin;
for(i=0;i<G.vexnum;i++)
{
a[i]=-1;
}
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
{
p[i][j]=-1;
}
}
for(i=0;i<G.vexnum;i++)
{
dist[i]=G.arcs[n][i];
S[i]=0;
}
S[n]=1;
for(i=0;i<G.vexnum;i++)
{
p[i][0]=n;
}
do
{
wmin=Maxint;
u=n;
for(i=0;i<G.vexnum;i++)
{
if(S[i]==0)
{
if(dist[i]<wmin)
{
u=i;
wmin=dist[i];
}
}
}
S[u]=1;
for(i=0;i<G.vexnum;i++)
{
if(p[u][i]==-1)
{
p[u][i]=u;
break;
}
}
for(i=0;i<G.vexnum;i++)
{
if(S[i]==0)
{
if(dist[u]+G.arcs[u][i]<dist[i])
{
k=1;
for(j=0;j<G.vexnum;j++)
{
if(p[i][j]==-1&&a[i]==-1)
a[i]=j;
if(p[i][j]==-1)
{
break;
}
}
j=a[i];
while(p[u][k]!=-1)
{
p[i][j]=p[u][k];
k++;
j++;
}
dist[i]=dist[u]+G.arcs[u][i];
}
if(num==G.vexnum-2)
p[i][j]=i;
}
}
num++;
}while(num!=G.vexnum-1);
for(i=0;i<G.vexnum;i++)
{
j=0;
if(i==n)
continue;
else
{
while(p[i][j]!=-1)
{
cout<<left<<setw(16)<<G.vexs[p[i][j]];
j++;
}
cout<<dist[i]<<endl;
}
}
return OK;
}
选择运动路线功能展示:
这个功能实现较难,小编在这里解释一下,要理解的话还是要花一定功夫。
代码中第一个for循环和if语句是检测输入的地点是否在邻接矩阵中,若不在,则直接退出。
首先看最后一个for循环,可见,我们在经历完算法后,每个此地点到每个地点的最短路径会存储在p这个二维矩阵中,p这个二维矩阵中的每一行就是此地点到各个地点的最短路径,而最短路径的距离会存储在dist这个一维矩阵中。
中间的do循环为本算法的重难点,其基本思路为:先从dist中找到距离输入地点最近的一个地点,并改变p矩阵中它所在的行,然后再比较其他地点有没有因为这个最近的地点的加入而距离输入地点有一个更近的值,若有,则改变此地点的dist和p中的一行。
源码及txt文件请查看:https://github.com/jvyou/Motion-information-management-system
效果演示请查看:https://www.bilibili.com/video/BV1vH4y1e7ik/?spm_id_from=333.999.0.0&vd_source=ea64b940c4e46744da2aa737dca8e183