数据结构与算法-模拟手机通信录和最小生成树

一、总体要求

1.模拟手机通信录管理系统

主要功能:

  • 查看功能:选择此功能,列出手机通信录的分类,如A同事、B家人、C朋友、D同学等分类,但选中某分类时,显示出此类所有数据中的姓名和电话号码;
  • 增加功能:录入新数据(包括姓名、电话号码、分类),当录入重复的姓名和电话号码时,提示结果错误并取消录入。
  • 修改功能:对选择(选择的方式自定)的联系人,修改其通信录的相关信息,但如果修改姓名,不能跟其他已有的联系人的姓名不能重复。
  • 删除功能:选择某个联系人的姓名,可对此人的相应数据进行删除。
  • 其他功能:自行分析设计有特色效果的功能。(作为加分点)
  • 要求:设计合理的数据结构,实现合理美观的操作界面。

2.最小生成树算法

设计算法实现给定网的最小生成树,如下网的最小生成树。

  • 环境配置

Visual studio 2010

二、实验步骤

1. 模拟手机通信录管理系统

     在设计模拟手机通信录管理系统时,要考虑数据结构的选取,还要注意对重复数据的处理。

(1)实验思想

      a)整体代码通过四个数组实现;四种类型通过switch选择。

      b)功能菜单如下:录入、输出、删除、修改、退出系统。

      c)  ①定义结构体phone_contacts//手机通讯录;

           ②定义四个结构体数组存储不同类型的人员信息;

           ③定义4个全局变量记录不同类型人员的数量。

      d)  ①信息录入时,如若发现姓名或者号码重复,则重新录入;

           ②输出时,通过switch可选择不同的方式,可以单独输出某一类型人员,也可以全部输出;

           ③删除时,通过数组中该元素后面元素依次向前移动的方式;

           ④ 修改时,通过访问数组进行元素替换。

(2)代码

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#define N 100
using namespace std;


struct phone_contacts//手机通讯录
{
	char name[10];//姓名
	int phone;//电话号码
};
struct phone_contacts AA[N],BB[N],CC[N],DD[N];//结构体对象数组:存不同类型人员的信息
int a=0,b=0,c=0,d=0;//全局变量记录不同类型人员的数量
int input()  //信息录入:通过比对注意信息是否重复,若重复i--,重新循环录入
{
	int i=0,j=0,y=0,m=0,k=0;
	char x;
	cout<<"请输入要增加的总人数:"<<endl;
	cin>>j;
	for(i=0;i<j;i++)
	{	int n=1;
		cout<<"输入第"<<i+1<<"个人员的信息:"<<endl;
		cout<<"请选择新增人员的类型:A.同事 B.家人 C.朋友 D.同学:"<<endl;
		cin>>x;
		cout<<"请依次输入其姓名、电话号码:"<<endl;
		switch(x)
		{
			case 'A':
				{	n=1;
					cin>>AA[a].name>>AA[a].phone;a++;
					//cout<<"n1="<<n<<"i="<<i<<endl;
					//cout<<"AA[a].name"<<AA[a].name<<endl;
					if(n)
					{	k=i;
						for(m=0;m<i;m++)
						{	//cout<<"AA[a].phone="<<AA[a-1].phone<<"AA[m].phone="<<AA[m].phone<<endl;
							if(strcmp(AA[a-1].name,AA[m].name)==0)
							{	cout<<"录入姓名重复,请重新输入。"<<endl;i--;a--;n=1;}
							else if(AA[a-1].phone==AA[m].phone)
							{	cout<<"录入电话号码重复,请重新输入。"<<endl;i--;a--;n=1;}
							else
								n=0;	
						}
					}
				//	cout<<"n2="<<n<<"i="<<i<<endl;
					if(n==0||k==0)
					{	y=1;cout<<"增加成功!"<<endl<<endl;}
					break;
				}
			case 'B':
				{	n=1;
					cin>>BB[b].name>>BB[b].phone;b++;
					if(n)
					{	k=i;
						for(m=0;m<i;m++)
						{	if(strcmp(BB[i].name,BB[m].name)==0)
							{	cout<<"录入姓名重复,请重新输入。"<<endl;i--;b--;n=1;}
							else if(BB[i].phone==BB[m].phone)
							{	cout<<"录入电话号码重复,请重新输入。"<<endl;i--;b--;n=1;}
							else
								n=0;	
						}
					}
					if(n==0||k==0)
					{	y=2;cout<<"增加成功!"<<endl<<endl;}
					break;
				}
			case 'C':
				{	n=1;
					cin>>CC[c].name>>CC[c].phone;c++;
					if(n)
					{	k=i;
						for(m=0;m<i;m++)
						{	if(strcmp(CC[i].name,CC[m].name)==0)
							{	cout<<"录入姓名重复,请重新输入。"<<endl;i--;c--;n=1;}
							else if(AA[i].phone==AA[m].phone)
							{	cout<<"录入电话号码重复,请重新输入。"<<endl;i--;c--;n=1;}
							else
								n=0;	
						}
					}
					if(n==0||k==0)
					{	y=3;cout<<"增加成功!"<<endl<<endl;}
					break;
				}
			case 'D':
				{	n=1;
					cin>>DD[d].name>>DD[d].phone;d++;
					if(n)
					{	k=i;
						for(m=0;m<i;m++)
						{	if(strcmp(DD[i].name,DD[m].name)==0)
							{	cout<<"录入姓名重复,请重新输入。"<<endl;i--;d--;n=1;}
							else if(AA[i].phone==AA[m].phone)
							{	cout<<"录入电话号码重复,请重新输入。"<<endl;i--;d--;n=1;}
							else
								n=0;	
						}
					}
					if(n==0||k==0)
					{	y=4;cout<<"增加成功!"<<endl<<endl;}
					break;
				}
		}	
	}
	return j;
}

void display()  //输出信息:可选择输出一种类型,也可以选择输出全部
{
	int i=0;
	char x;
	cout<<"请选择要显示的类型:A.同事 B.家人 C.朋友 D.同学 F.全部:"<<endl;
	cin>>x;
	switch(x)
	{	case 'A':
				{	for(i=0;i<a;i++)
					{	cout<<"显示第"<<i+1<<"个同事信息"<<endl;
						cout<<"姓名为:"<<AA[i].name<<"  电话号码为:"<<AA[i].phone<<endl;
					}
					break;
				}
		case 'B':
				{	for(i=0;i<b;i++)
					{	cout<<"显示第"<<i+1<<"个家人信息"<<endl;
						cout<<"姓名为:"<<BB[i].name<<"  电话号码为:"<<BB[i].phone<<endl;
					}
					break;
				}
		case 'C':
				{	for(i=0;i<c;i++)
					{	cout<<"显示第"<<i+1<<"个朋友信息"<<endl;
						cout<<"姓名为:"<<CC[i].name<<"  电话号码为:"<<CC[i].phone<<endl;
					}
					break;
				}
		case 'D':
				{	for(i=0;i<d;i++)
					{	cout<<"显示第"<<i+1<<"个同学信息"<<endl;
						cout<<"姓名为:"<<DD[i].name<<"  电话号码为:"<<DD[i].phone<<endl;
					}
					break;
				}
		case 'F':
				{	for(i=0;i<a;i++)
					{	cout<<"A.同事   "<<"姓名为:"<<AA[i].name<<"  电话号码为:"<<AA[i].phone<<endl;}
					for(i=0;i<b;i++)
					{	cout<<"B.家人   "<<"姓名为:"<<BB[i].name<<"  电话号码为:"<<BB[i].phone<<endl;}
					for(i=0;i<c;i++)
					{	cout<<"C.朋友   "<<"姓名为:"<<CC[i].name<<"  电话号码为:"<<CC[i].phone<<endl;}
					for(i=0;i<d;i++)
					{	cout<<"D.同学   "<<"姓名为:"<<DD[i].name<<"  电话号码为:"<<DD[i].phone<<endl;}
					break;
				}
	}
}

void delet()  //信息删除:通过姓名比对在数组中找到相应位置删除元素,并将后面的元素依次向前移动
{
	int i,k;
	char x;char names[10];
	cout<<"请输入要删除人员的类型:A.同事 B.家人 C.朋友 D.同学:"<<endl;
	cin>>x;
	cout<<"请输入要删除的人的姓名:"<<endl;
	scanf("%s",names);
	switch(x)
	{
		case 'A':
				{	for(i=0;i<a;i++)
					{	if(strcmp(names,AA[i].name)==0)
						{	for(k=i;k<a;k++)
							{	strcpy(AA[k].name,AA[k+1].name);
								AA[k].phone=AA[k+1].phone;}//i后元素依次往前移动
						}
					}
					cout<<"删除成功!"<<endl;
					a--;break;
				}
		case 'B':
				{	for(i=0;i<b;i++)
					{	if(strcmp(names,BB[i].name)==0)
						{	for(k=i;k<b;k++)
								BB[k]=BB[k+1];//i后元素依次往前移动
						}
					}
					cout<<"删除成功!"<<endl;
					b--;break;
				}
		case 'C':
				{	for(i=0;i<c;i++)
					{	if(strcmp(names,CC[i].name)==0)
						{	for(k=i;k<c;k++)
								CC[k]=CC[k+1];//i后元素依次往前移动
						}
					}
					cout<<"删除成功!"<<endl;
					c--;break;
				}
		case 'D':
				{	for(i=0;i<d;i++)
					{	if(strcmp(names,DD[i].name)==0)
						{	for(k=i;k<d;k++)
								DD[k]=DD[k+1];//i后元素依次往前移动
						}
					}
					cout<<"删除成功!"<<endl;
					d--;break;
				}
	}
}

void change()  //信息修改:通过姓名比对在数组中找到相应位置,替换掉原信息
{
	int i,y;
	char x;
	char names[10];
	cout<<"请选择修改人员的类型:A.同事 B.家人 C.朋友 D.同学:"<<endl;
	cin>>x;
	switch(x)
	{
		case 'A':
				{	cout<<"请输入要修改人员的原姓名:"<<endl;
					scanf("%s",names);
					for(i=0;i<a;i++)
					{	if(strcmp(AA[i].name,names)==0)
						{	cout<<"请输入要修改的选项:1.姓名 2.电话号码:"<<endl;
							cin>>y;
							if(y==1)
							{	cout<<"将姓名改为:"<<endl;
								cin>>AA[i].name;
								cout<<"修改成功!"<<endl;
							}
							else if(y==2)
							{	cout<<"将电话号码改为:"<<endl;
								cin>>AA[i].phone;
								cout<<"修改成功!"<<endl;
							}
						}
					}
					break;
				}
		case 'B':
				{	cout<<"请输入要修改人员的原姓名:"<<endl;
					scanf("%s",names);
					for(i=0;i<b;i++)
					{	if(strcmp(BB[i].name,names)==0)
						{	cout<<"请输入要修改的选项:1.姓名 2.电话号码:"<<endl;
							cin>>y;
							if(y==1)
							{	cout<<"将姓名改为:"<<endl;
								cin>>BB[i].name;
								cout<<"修改成功!"<<endl;
							}
							else if(y==2)
							{	cout<<"将电话号码改为:"<<endl;
								cin>>BB[i].phone;
								cout<<"修改成功!"<<endl;
							}
						}
					}
					break;
				}
		case 'C':
				{	cout<<"请输入要修改人员的原姓名:"<<endl;
					scanf("%s",names);
					for(i=0;i<c;i++)
					{	if(strcmp(CC[i].name,names)==0)
						{	cout<<"请输入要修改的选项:1.姓名 2.电话号码:"<<endl;
							cin>>y;
							if(y==1)
							{	cout<<"将姓名改为:"<<endl;
								cin>>CC[i].name;
								cout<<"修改成功!"<<endl;
							}
							else if(y==2)
							{	cout<<"将电话号码改为:"<<endl;
								cin>>CC[i].phone;
								cout<<"修改成功!"<<endl;
							}
						}
					}
					break;
				}
		case 'D':
				{	cout<<"请输入要修改人员的原姓名:"<<endl;
					scanf("%s",names);
					for(i=0;i<d;i++)
					{	if(strcmp(DD[i].name,names)==0)
						{	cout<<"请输入要修改的选项:1.姓名 2.电话号码:"<<endl;
							cin>>y;
							if(y==1)
							{	cout<<"将姓名改为:"<<endl;
								cin>>DD[i].name;
								cout<<"修改成功!"<<endl;
							}
							else if(y==2)
							{	cout<<"将电话号码改为:"<<endl;
								cin>>DD[i].phone;
								cout<<"修改成功!"<<endl;
							}
						}
					}
					break;
				}
	}
}


void menu()  //主菜单
{
	printf("\n-------------------------------\n");
	printf("1.录入\n");
	printf("2.输出\n");
	printf("3.删除\n");
	printf("4.修改\n");
	printf("5.退出系统\n");
	printf("-------------------------------\n");
}

int main()  //主函数
{
	int x;
	int y;
	int z=1;
	while(z)
	{
		menu();
		printf("请输入要执行功能的序号:\n");
		scanf("%d",&x);
		switch(x)
		{
		case 1:
			{	y=input();
				break;
			}
		case 2:
			{	display();
				break;
			}
		case 3:
			{	delet();
				break;
			}
		case 4:
			{	change();
				break;
			}
		case 5:
			{
				z=0;
				printf("退出系统成功!感谢您的使用!\n");
				break;
			}
		}
	}
}

(3)运行结果

2. 最小生成树算法

      最小生成树算法是图论中的一个经典问题,常用于网络设计、电路布线等领域。在设计算法时,我们需要考虑如何从一个给定的图中选择边,使得这些边构成的子图是一个无环连通图(即树),并且这些边的权值之和最小。

      常见的最小生成树算法有Prim算法和Kruskal算法。Prim算法从任意一个顶点开始,每次选择一条与当前子图相连且权值最小的边,直到所有顶点都被包含在子图中。Kruskal算法则按照边的权值从小到大进行排序,然后依次选择边,但要求选择的边不能与已选择的边构成环。

      在实现算法时,我们需要考虑如何高效地选择边和判断环。这可以通过使用并查集数据结构来实现。并查集可以快速地判断两个顶点是否属于同一个集合(即是否已经连通),从而避免形成环。

(1)实验思想

      a)最小生成树的定义为:是原图的极小连通子图,包含图中所有结点,最重要的是保持图连通最少的边。

      b)邻接矩阵存储两顶点的权值:

      c)prim算法(普利姆算法):对图G(V,E)设置集合S,存放已访问的顶点,然后每次从集合V-S中选择与集合S的最短距离最小的一个顶点(记为u),访问并加入集合S。之后,令顶点u为中介点,优化所有从u能到达的顶点v与集合S之间的最短距离。执行n次(n为顶点个数),直到集合S已包含所有顶点。Prim算法利用优先级队列来存储候选边,以便每次都能快速找到权重最小的边,这体现了贪心策略的思想。

(2)代码

#include "stdafx.h"
#include "stdafx.h"
#include <iostream>
#include <algorithm>
#define MAX 9999
using namespace std;

struct AMGraph
{
    char *vertices;    //顶点表,储存所有顶点元素  
    int *visited;      //蓝白点数组,蓝点为1,白点为0;选择离白点最短路径的蓝点洗白
    int **arcWeight;    //邻接矩阵,存储两顶点间的权重
    int currVex, currArc;       //顶点数 、边数
};
int get_Index(const AMGraph &G, char u)
{
    for(int i = 0; i < G.currVex; i++)
        if(u == G.vertices[i]) 
			return i;//获得顶点在顶点数组中的下标
    return -1;
}

void createUND(AMGraph &G)//创建邻接矩阵
{
    cout << "请输入图的顶点个数和边数:";
    cin >> G.currVex >> G.currArc;
    G.vertices = new char[G.currVex];
    G.visited = new int[G.currVex];
    cout << "请输入顶点的值(char类型):";
    for(int i = 0; i < G.currVex; i++)
	{	cin >> G.vertices[i]; }
    //动态邻接矩阵开辟空间,同时进行初始化
    G.arcWeight = new int*[G.currVex];
    for(int i = 0; i < G.currVex; i++)
	{
        G.arcWeight[i] = new int[G.currVex];
        for(int j = 0; j < G.currVex; j++)
            if(i == j) 
				G.arcWeight[i][j] = 0;	//邻接矩阵中主对角线全为0,即结点到自己的边初始化为0
            else 
				G.arcWeight[i][j] = MAX;		//除了上条语句情况其余邻接矩阵的位置的权值初始化为无穷大
    }
    fill(G.visited, G.visited+G.currVex, 1);	//填充蓝白点数组,初始化每个顶点的标记都为1(即蓝点)
    char v1, v2;
    int i, j, power;
    cout << "请依次输入两顶点值和权值:"<<endl;
    for(int k = 0; k < G.currArc; k++)
	{
        cin >> v1 >> v2 >> power;
        i = get_Index(G, v1);
        j = get_Index(G, v2);
        G.arcWeight[i][j] = power;		//这里是 i 行 j 列
        G.arcWeight[j][i] = power;		//这里是 j 列 i 行
    }
    return ;
}

void Prim(AMGraph& G)
{
    int Min[100];
     fill(Min, Min+G.currVex, MAX);
    Min[0] = MAX;		// min[0]定义为无穷大才能实现每 i 轮的 Min 的初始化
    int MST = 0;
    cout << "洗白点的顺序:";
    for(int i = 0; i < G.currVex; i++)
    {   int w = 0;
        for(int j = 0; j < G.currVex; j++)
            if(G.visited[j] && Min[w] > Min[j])
			{  //选择离白点最短路径的蓝点洗白,将该节点添加入生成树。
                w = j;
            }
        G.visited[w] = 0;       //洗白
        cout << G.vertices[w] << "  ";
        if(w != 0)      // 权值累加时排除刚开始节点的权值(刚开始的节点权值为无穷大)
            MST += Min[w];      //权值累加
        for(int k = 0; k < G.currVex; k++)
            if(G.visited[k] && G.arcWeight[w][k] < Min[k])
			{
                Min[k] = G.arcWeight[w][k]; //更新每个蓝点到白点集合的最小值
            }
    }
    cout << endl;
    cout << "连通图最小边权总和:" << MST << endl;
}

int main()
{
    AMGraph G;		//构造图
    createUND(G);	//初始化无向图
    Prim(G);
    return 0;
}

(3)运行结果

三、总结

      总结来说,本次实验不仅加深了我对数据结构和算法的理解,还提升了我解决实际问题的能力。通过设计和实现手机通信录管理系统,我学习了如何管理复杂的数据集。同时,通过实现最小生成树算法,我掌握了图论中的一个重要概念及其在实际中的应用;它告诉我们,在解决复杂问题时,我们可以通过选择最优的局部解来逼近全局最优解。这些经验对于我们未来的学习和工作都将是宝贵的财富。

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值