【C语言】通讯录系统实现 (保姆级教程,附源码)

目录

1、通讯录系统介绍

2、代码分装

3、代码实现步骤

3.1、制作菜单menu函数以及游戏运行逻辑流程

3.2、封装人的信息PeoInfo以及通讯录Contact结构体类型

3.3、初始化通讯录InitContact函数

3.4、增加联系人AddContact函数

3.5、显示所有联系人ShowContact函数

3.6、删除联系人DelContact函数以及判断是否存在FindByName函数

3.7、查找指定联系人SearchContact函数

3.8、修改指定联系人ModifyContact函数

3.9、以年龄排序联系人SortContact函数

4、使用动态规划优化通讯录 

5、通讯录系统完整代码


1、通讯录系统介绍

实现一个通讯录:

  1. 可以保存100个人的信息(后续优化成动态开辟)
  2. 增加人的信息
  3. 删除指定联系人的信息
  4. 查询指定联系人的信息 
  5. 修改指定联系人的信息
  6. 排序通讯录的信息
  7. 显示所有联系人的信息

其中,人的信息包括:名字、年龄、性别、电话 、地址

2、代码分装

源文件:

test.c: 测试通讯录的基本功能

game.c: 实现通讯录的相关函数

头文件:

game.h: 相关函数和类型的声明,整个代码要引用的头文件以及宏定义

3、代码实现步骤

3.1、制作菜单menu函数以及游戏运行逻辑流程

  • 首先在 test.c 中定义一个menu函数打印菜单,提示玩家进行选择。
  • scanf接收玩家输入并用switch判断,针对判断进行相应操作,输入错误时提示选择错误,重新选择。而为了能够让用户重新选择以及完成一个功能操作时再继续下一个功能操作,需要使用do while语句将它们包含起来。

使用枚举一一列举菜单选择的可能取值,便于后续使用时能够见名知意,增加代码可读性。 

【test.c】 

void menu()
{printf("***********************************\n");printf("******  1.add      2.del     ******\n");printf("******  3.search   4.modify  ******\n");printf("******  5.show     6.sort    ******\n");printf("******  0.exit               ******\n");printf("***********************************\n");
}enum Option  //枚举常量
{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};int main()
{int input = 0;do{menu();printf("请输入你的选择:>");scanf("%d", &input);switch (input){case ADD:break;case DEL:break;case SEARCH:break;case MODIFY:break;case SHOW:break;case SORT:break;case EXIT:printf("退出通讯录\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}

3.2、封装人的信息PeoInfo以及通讯录Contact结构体类型

创建结构体类型PeoInfo用于描述人的信息,然后再创建一个Contact结构体包含PeoInfo和个数,这样一个通讯录结构体类型便完成了 。

为了方便后续代码的修改,这里将所有常量都用 #define 定义了变量。

【contact.h】

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30#define MAX 100typedef struct PeoInfo
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;typedef struct Contact
{PeoInfo data[MAX];int sz; //记录的是当前通讯录中存放的人的信息个数
}Contact;

3.3、初始化通讯录InitContact函数

【contact.h】

//初始化通讯录
void InitContact(Contact* pc);

【contact.c】 

assert断言,对传入的指针进行判断,防止对空指针进行操作。

使用 memset函数 初始化结构体中的数据。针对memset函数,如果不了解其原理,可以前往往期博客进行学习了解:

一篇博客学会系列(1) —— C语言中所有字符串函数以及内存函数的使用和注意事项_Hacynn的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/133243791?spm=1001.2014.3001.5501

void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}

【test.c】 

int main()
{int input = 0;Contact con; //通讯录InitContact(&con);//初始化通讯录do{menu();printf("请输入你的选择:>");scanf("%d", &input);switch (input){case ADD:break;case DEL:break;case SEARCH:break;case MODIFY:break;case SHOW:break;case SORT:break;case EXIT:printf("退出通讯录\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}

3.4、增加联系人AddContact函数

【contact.h】

//增加联系人
void AddContact(Contact* pc);

【contact.c】

assert断言,对传入的指针进行判断,防止对空指针进行操作。 

需要判断当sz不等于MAX时再继续进行添加操作,防止溢出。

此处需要注意的是,人的信息中只有age是整型,其他的name、sex、tele、addr都为字符数组,scanf在接收age的时候需要取出age的地址,即 &(pc->data[pc->sz].age)。

void AddContact(Contact* pc)
{assert(pc);if (pc->sz == MAX){printf("通讯录已满,无法增加\n");return;}//增加信息printf("请输入名字:");scanf("%s", pc->data[pc->sz].name);printf("请输入年龄:");scanf("%d", &(pc->data[pc->sz].age));printf("请输入性别:");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话:");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址:");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("增加成功\n");}

为了查看增加功能是否正确,我们需要显示通讯录信息,因此接下来我们实现一个函数 ShowContact 用于显示所有联系人的信息。

3.5、显示所有联系人ShowContact函数

【contact.h】

虽然显示操作不需要修改通讯录中的内容,但是出于效率和空间的考虑,还是选择传址,然后使用const修饰。

//显示所有联系人
void ShowContact(const Contact* pc);

【contact.c】

注意11行和14行的不同。

因为11行打印的标题行都是字符串,所以都是用了%s,而14行的信息行中年龄是整型的 ,因此需要使用%d打印,需要注意这里的不同。

void ShowContact(const Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通讯录为空,无需打印\n");return;}int i = 0;printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");  //标题行for ( i = 0; i < pc->sz; i++){printf("%-20s%-5d%-5s%-12s%-30s\n",pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}
}

【test.c】

void menu()
{printf("***********************************\n");printf("******  1.add      2.del     ******\n");printf("******  3.search   4.modify  ******\n");printf("******  5.show     6.sort    ******\n");printf("******  0.exit               ******\n");printf("***********************************\n");
}enum Option
{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};int main()
{int input = 0;Contact con; //通讯录InitContact(&con);//初始化通讯录do{menu();printf("请输入你的选择:>");scanf("%d", &input);switch (input){case ADD:AddContact(&con);break;case DEL:break;case SEARCH:break;case MODIFY:break;case SHOW:ShowContact(&con);break;case SORT:break;case EXIT:printf("退出通讯录\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}

 下面试着添加联系人并打印通讯录:

 

3.6、删除联系人DelContact函数以及判断是否存在FindByName函数

【contact.h】

//删除联系人
void DelContact(Contact* pc);

【contact.c】

  • 因为通过名字判断此人是否存在的FindByName函数这个功能在其他操作上也需要使用到,所以最好将它封装成一个函数,减少代码冗余并且提高写代码效率。
  • 当FindByName函数在通讯录中找到此人时返回此人在data数组中的下标,找不到是返回-1。
  • 又因为FindByName函数只在contact.c中被使用,因此可以加上static关键字修饰。
static int FindByName(Contact* pc, char name[])
{assert(pc);int i = 0;for ( i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0)  //两个字符串比较用strcmpreturn i;}return -1;
}void DelContact(Contact* pc)
{char name[NAME_MAX];assert(pc);if (pc->sz == 0){printf("通讯录为空,无法删除\n");return;}printf("请输入要删除人的名字:");scanf("%s", name);//查找名字为name的人int ret = FindByName(pc, name);if (ret == -1){printf("要删除的人不存在\n");return;}//删除这个人int i = 0;for (i = ret; i < pc->sz - 1; i++)    //注意此处的sz - 1 {pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");
}

【删除操作图解】 

这里以0~6的下标作为例子,假如ret返回的下标时1。

只需要将ret后面的所有内容向前平移一位,就可以覆盖掉要ret指向的内容,然后再对sz--,这样变相地完成了对信息的删除。等将来又有新联系人加入时,因为sz--了所以写入信息时会覆盖掉下标为6中的内容。

此时使用该函数尝试删除一下联系人,结果是可以的。

3.7、查找指定联系人SearchContact函数

【contact.h】

//查找指定联系人
void SearchContact(Contact* pc);

【contact.c】

依然是调用FindByName函数进行判断,有此人则返回下标,没有此人则返回-1。

找到之后直接打印就行。

void SearchContact(Contact* pc)
{char name[NAME_MAX];assert(pc);printf("请输入要查找人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("要查找的人不存在\n");return;}//显示查查找到的信息printf("%-20s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-20s%-5d%-5s%-12s%-30s\n",pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);}

3.8、修改指定联系人ModifyContact函数

【contact.h】

//修改指定联系人
void ModifyContact(Contact* pc);

【contact.c】

依然是调用FindByName函数进行判断,有此人则返回下标,没有此人则返回-1。

找到之后直接将该下标下的所有信息都重新接收并覆盖,就完成了修改操作。

void ModifyContact(Contact* pc)
{char name[NAME_MAX];assert(pc);printf("请输入要修改人的名字:");scanf("%s", name);int ret = FindByName(pc, name);if (ret == -1){printf("要修改的人不存在\n");return;}//修改printf("请输入名字:");scanf("%s", pc->data[ret].name);printf("请输入年龄:");scanf("%d", &(pc->data[ret].age));printf("请输入性别:");scanf("%s", pc->data[ret].sex);printf("请输入电话:");scanf("%s", pc->data[ret].tele);printf("请输入地址:");scanf("%s", pc->data[ret].addr);
}

3.9、以年龄排序联系人SortContact函数

【contact.h】

//排序联系人
void SortContact(Contact* pc);

【contact.c】

qsort可以对任意数据进行排序,因此使用qsort以年龄为关键字进行排序。

关于 qsort函数 我在往期的博客中有进行详细讲解,这里由于篇幅原因,就不过多赘述,如果不懂或者想要更深入了解的话,可以前往:【C语言】指针的进阶(二)—— 回调函数的讲解以及qsort函数的使用方式_Hacynn的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/132941465?spm=1001.2014.3001.5501

int cmp_age(const void* e1, const void* e2)
{return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}void SortContact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通讯录为空,无需排序\n");return;}qsort(pc, pc->sz, sizeof(PeoInfo), cmp_age);printf("排序成功\n");
}

到这里通讯录的实现就基本完成了,但是有些读者可以注意到,这个通讯录还存在一些问题:

  1. 录入的信息,等程序结束后就不存在了,这是因为数据存放在内存中的。为了解决这个问题,需要使用到文件存储的知识。
  2. 通讯录的大小是固定的100个元素,只能最多存放100个人。当信息太少时,就会导致空间剩余过大浪费空间,而当信息太多时空间又太小了无法进行存入,而解决这个问题需要使用到动态内存管理的知识。下面就来优化一下通讯录。

4、使用动态规划优化通讯录 

规定:

  1. 通讯录刚开始时可以存放3个人的信息。#define DEFAULT_SZ 3
  2. 当空间放满时,自动增加容量,每次增加2个信息的空间。#define DEFAULT_INC 2

定义通讯录Contact结构体:

因为数组空间是固定的,因此这里改成了指针,后续初始化时对data指针进行calloc动态开辟空间,并且增加用于记录当前最大容量的变量capacity

静态版本
//typedef struct Contact
//{
//	PeoInfo data[MAX];
//	int sz; //记录的是当前通讯录中存放的人的信息个数
//}Contact;//动态版本
typedef struct Contact
{PeoInfo* data;int sz; //记录的是当前通讯录中存放的人的信息个数int capacity; //记录的时通讯录当前的最大容量
}Contact;

初始化通讯录InitContact函数: 

此时就可以使用calloc对data指针进行动态开辟空间,如果开辟失败则用perror打印错误信息。

对于perror函数的使用以及注意事项,在往期博客中有进行详细讲解,这就不过多赘述,感兴趣的可以前往:一篇博客学会系列(1) —— C语言中所有字符串函数以及内存函数的使用和注意事项_Hacynn的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/133243791?spm=1001.2014.3001.5501

静态版本
//void InitContact(Contact* pc)
//{
//	assert(pc);
//	pc->sz = 0;
//	memset(pc->data, 0, sizeof(pc->data));
//}//动态版本
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;pc->capacity = DEFAULT_SZ;  //#define DEFAULT_SZ 3pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));if (pc->data == NULL){perror("InitContact->calloc");return;}
}

 增加联系人AddContact函数:

因为此时使用了动态规划,所以不会存在通讯录满了无法增加的情况,此时if语句改为判断是否已满,如果已满则使用realloc增容。此时需要用另一个指针ptr接收realloc的空间,判断ptr指向的空间是否开辟成功,成功时再将ptr赋值给pc->data,防止数据丢失。

静态版本
//void AddContact(Contact* pc)
//{
//	assert(pc);
//	if (pc->sz == MAX)
//	{
//		printf("通讯录已满,无法增加\n");
//		return;
//	}
//	
//	printf("请输入名字:");
//	scanf("%s", pc->data[pc->sz].name);
//	printf("请输入年龄:");
//	scanf("%d", &(pc->data[pc->sz].age));
//	printf("请输入性别:");
//	scanf("%s", pc->data[pc->sz].sex);
//	printf("请输入电话:");
//	scanf("%s", pc->data[pc->sz].tele);
//	printf("请输入地址:");
//	scanf("%s", pc->data[pc->sz].addr);
//
//	pc->sz++;
//	printf("增加成功\n");
//
//}//动态版本
void AddContact(Contact* pc)
{assert(pc);if (pc->sz == pc->capacity){PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo));if (ptr != NULL){pc->data = ptr;pc->capacity += DEFAULT_INC;  //#define DEFAULT_INC 2printf("增容成功\n");}else{perror("AddContact->realloc");return;}}printf("请输入名字:");scanf("%s", pc->data[pc->sz].name);printf("请输入年龄:");scanf("%d", &(pc->data[pc->sz].age));printf("请输入性别:");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话:");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址:");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("增加成功\n");}

在EXIT退出通讯录时候记得对动态开辟的空间进行free操作:

定义一个DestroyContact函数用于free掉动态开辟的空间。

【contact.h】

//销毁通讯录
void DestroyContact(Contact* pc);

【contact.c】

void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;
}

5、通讯录系统完整代码

 源码已经上传至gitee中,有需要可以前往领取:Contact/Contact · Nadez/Study_c - 码云 - 开源中国 (gitee.com)

 

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/91880.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Idea】idea、datagrip设置输入法

https://github.com/RikudouPatrickstar/JetBrainsRuntime-for-Linux-x64/releases/tag/jbr-release-17.0.6b829.5https://github.com/RikudouPatrickstar/JetBrainsRuntime-for-Linux-x64/releases/tag/jbr-release-17.0.6b829.5 下载后解压并重命名为 jbr, 然后替换对应 ide…

CTF-XSS

知识 例子 。2022 CNSS夏令营 To_be_Admin_Again_and_Again XSS攻击. 与admin有关, 可以尝试http://1.117.6.207:65005/admin, 显示Get out, HACKER! Only admin can see the flag!, 说明需要一个admin的验证, 这里想到获取admin的cookie. 每条留言会自动查看, 可以使用XS…

Python-Flask:编写自动化连接demo脚本:v1.0.0

主函数&#xff1a; # _*_ Coding : UTF-8 _*_ # Time : 13:14 # Author : YYZ # File : Flask # Project : Python_Project_爬虫 import jsonfrom flask import Flask,request,jsonify import sshapi Flask(__name__)# methods: 指定请求方式 接口解析参数host host_info[…

TSM动作识别模型【详解】

文章目录 本文使用的是somethingv2数据集&#xff0c;解压后是如下形式&#xff1b; 由于该压缩数据进行了分卷操作&#xff0c;需要合并后才能进行解压。首先我们将下面4个json文件剪贴到其他文件夹&#xff0c;只保留00-19的文件&#xff0c;然后在该文件夹下打开cmd&#xf…

模块化CSS

1、什么是模块化CSS 模块化CSS是一种将CSS样式表的规则和样式定义封装到模块或组件级别的方法&#xff0c;以便于更好地管理、维护和组织样式代码。这种方法通过将样式与特定的HTML元素或组件相关联&#xff0c;提供了一种更具可维护性、可复用性和隔离性的方式来处理样式。简单…

机器人制作开源方案 | 四轴飞行器

1. 概述 基于探索者搭建的模块化四轴飞行器研究平台&#xff0c;采用独特的设计方式&#xff0c;可实现在室内完成对四轴飞行器、无人机等运动控制的原理研究&#xff0c;以及学习飞行控制的原理知识。 2. 组装 请按照下图进行机架的组装。 整体图 请解压文末资料中的 /软件/Mi…

【LeetCode热题100】--226.翻转二叉树

226.翻转二叉树 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

【聊天系统的优化】RPC方式的优化

RPC方式的优化 聊天系统的中RPC的选择Jsonprotobufmsgpack 聊天系统的中RPC的选择 在RPC方式中&#xff0c;常用的三种方式&#xff1a;Json&#xff0c;protobuf&#xff0c;Msgback 设定一个简单的加和服务&#xff0c;客户端发送一个list给服务端&#xff0c;需要将list的…

QT的ui设计中改变样式表的用法

在QT的ui设计中,我们右键会弹出一个改变样式表的选项,很多人不知道这个是干什么的。 首先我们来看下具体的界面 首先我们说一下这个功能具体是干嘛的, 我们在设置很多控件在界面上之后,常常都是使用系统默认的样式,但是当有些时候为了美化界面我们需要对一些控件进行美化…

Kafka-Kerberos票据刷新问题

线上kafka使用了 kerberos 认证&#xff0c;每隔24小时&#xff0c;票据过期&#xff0c;无法自动续期&#xff0c;出现消息发送失败问题。 从日志可以发现会有如下报错&#xff1a; 2023-09-14 17:48:47,144 [kafka-kerberos-refresh-thread-kafka/hdp-1HADOOP.COM] [] WARN …

Kafka(一)使用Docker Compose安装单机Kafka以及Kafka UI

文章目录 Kafka中涉及到的术语Kafka镜像选择Kafka UI镜像选择Docker Compose文件Kafka配置项说明KRaft vs Zookeeper和KRaft有关的配置关于Controller和Broker的概念解释Listener的各种配置 Kafka UI配置项说明 测试Kafka集群Docker Compose示例配置 Kafka中涉及到的术语 对于…

面试必考精华版Leetcode236. 二叉树的最近公共祖先

题目&#xff1a; 代码(首刷看解析 10.1&#xff09;&#xff1a; class Solution { public:TreeNode* ansnullptr;bool FindSon(TreeNode* root,TreeNode* p,TreeNode* q){if(root nullptr) return false;bool lson FindSon(root->left,p,q);bool rson FindSon(root-&…

基于SpringBoot的课程答疑系统

目录 前言 一、技术栈 二、系统功能介绍 学生信息管理 科目类型管理 老师回答管理 我的收藏管理 学生问题 留言反馈 交流区 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息互联网信息的飞速发展&#xff0c;无纸化作业变成了一种趋势&#x…

vSAN7.0更换硬盘步骤

更换容量盘 预先检查 查看故障硬盘 清单->集群->监控->vsan->skyline运行->物理磁盘->运维运行状况 检查数据同步状态 清单->集群->监控->vsan->重新同步对象&#xff0c;数值全为0表示未重建。 数据迁移检查 清单->集群->监控->…

Java实现word excel ppt模板渲染与导出及预览 LibreOffice jodconverter

Java Office 一、文档格式转换 文档格式转换是office操作中经常需要进行一个操作&#xff0c;例如将docx文档转换成pdf格式。 java在这方面有许多的操作方式&#xff0c;大致可以分为内部调用&#xff08;无需要安装额外软件&#xff09;&#xff0c;外部调用&#xff08;需…

数据结构--Trie字符串统计

1、“Trie树” 作用&#xff1a; 高效地存储和查找字符串集合的数据结构。 2、“Trie树” 存储字符串的形式如下&#xff1a; 用 “0” 来表示 “根节点&#xff08;root&#xff09;”。存入一个字符串时&#xff0c;会在字符串最后结尾的那个字符节点打上标记。比如&#x…

babel.config.js配置文件详解

文章目录 一、前言三、babel 详解四、拓展阅读 一、前言 项目开发阶段&#xff0c;使用可选链操作符 ?. 出现以下编译报错问题&#xff1a; 分析&#xff1a;由于可选链操作符 ?. 是ES2020&#xff08;即ES11&#xff09;中推出的新语法&#xff0c;允许我们不需要校验当前属…

FreeRTOS(以STM32F1系列为例子)

目录 任务管理任务函数任务控制块顶层任务状态创建任务xTaskCreatexTaskCreateStaticxTaskCreateRestricted 任务优先级和心跳设置心跳设置优先级概述vTaskPrioritySetuxTaskPriorityGet 非运行态扩充阻塞态vTaskDelay挂起状态vTaskSuspend就绪状态完整的状态转换图延迟函数vTa…

Spring Boot的自动装配中的@ConditionalOnBean条件装配注解在Spring启动过程中,是如何保证处理顺序靠后的

前言 为什么Spring Boot条件注解那么多&#xff0c;而标题中是ConditionalOnBean呢&#xff1f; 因为&#xff0c;相比之下我们用的比较多的条件装配注解也就是ConditionalOnClass、ConditionalOnBean了&#xff0c;而ConditionalOnClass对顺序并不敏感&#xff08;说白了就是判…

《数据结构、算法与应用C++语言描述》-栈的应用-开关盒布线问题

开关盒布线问题 问题描述 在开关盒布线问题中&#xff0c;给定一个矩形布线区域&#xff0c;其外围有若干管脚。两个管脚之间通过布设一条金属线路来连接。这条金属线路称为电线&#xff0c;它被限制在矩形区域内。两条电线交叉会发生电流短路。因此&#xff0c;电线不许交叉…