C语言数据结构 ---- 单链表实现通讯录

今日备忘录: "折磨我们的往往是想象, 而不是现实."

目录

  • 1. 前言
  • 2. 通讯录的功能
  • 3. 通讯录的实现思路
  • 5. 效果展示
  • 6. 完整代码
  • 7. 总结


正文开始

1. 前言

顺表实现通讯录: 点击~ 顺序表实现通讯录

在日常生活中,我们经常需要记录和管理大量的联系人信息,比如朋友的电话号码、家庭成员的生日等等。而通讯录就是一种常见的方式,用于存储和组织这些联系人信息。

通讯录可以采用不同的数据结构来实现,前面我使用了顺序表创建, 单链表是一种常用且简单的数据结构之一。单链表由一系列节点组成,每个节点包含两部分:数据域和指针域。数据域用于存储联系人的信息,指针域用于指向下一个节点。

在这篇文章中,我们将使用单链表来实现一个简单的通讯录。我们将定义联系人的结构体,包含姓名、电话号码等信息,并使用单链表来管理这些联系人。

通过单链表实现通讯录有很多好处。首先,单链表的插入和删除操作都比较高效,可以快速地添加和删除联系人。其次,通过遍历链表,我们可以方便地查找和修改联系人的信息。此外,单链表还可以动态扩展,不需要提前分配固定大小的内存空间。

接下来的文章中,我们将逐步实现通讯录的各项功能,包括添加联系人、删除联系人、查找联系人等等。通过这个实例,我们将更深入地了解单链表的原理和操作。

希望通过本文的介绍,读者能够掌握使用单链表来实现通讯录的方法,进一步提升对数据结构的理解和应用能力。让我们一起开始吧!

更多精彩, 博客主页: 酷酷学!!!

代码在这里: 代码仓库Gitee: 爱马仕

不要忘了关注哟~~


2. 通讯录的功能

1)至少少能够存储100个⼈的通讯信息
2)能够保存用户信息:名字、性别、年龄、电话、地址等
3)增加联系人信息
4)删除指定联系人
5)查找制定联系人
6)修改指定联系人
7)显示联系⼈信息


3. 通讯录的实现思路

第一步:
基于单链表的基础上, 我们再创建两个文件: Contact.h 和 Contact.c, 一个用来声明通讯录的结构和函数声明, 一个用来实现通讯录的功能.

在这里插入图片描述
第二步:
创建通讯录头文件, 把需要用到的结构定义和函数声明都先罗列出来:

其中需要前置声明更改链表的名字为contact.

注意: 这里为什么要前置声明呢?
因为头文件不可以嵌套包含, 这样会使程序代码冗余, 在单链表的头文件包含了通讯录的头文件, 就不要在让通讯录的头文件包含单链表了, 单链表中需要使用到通讯录的结构定义, 如下所示:

SList.h
需要更改存储数据类型为通讯录

#include"Contact.h"typedef PeoInfo Datatype;typedef struct SListNode
{Datatype data;struct SListNode* next;
}SNode;

Contact.h

#pragma once#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100typedef  struct SListNode contact;//前置声明typedef struct PersonInfo
{char name[NAME_MAX];char sex[SEX_MAX];int age;char tel[TEL_MAX];char addr[ADDR_MAX];
}PeoInfo;//初始化,把之的数据导入
void InitContact(contact** con);//添加通讯录数据
void AddContact(contact** con);//删除通讯录数据
void DelContact(contact** con);//链表删除指定位置的数据//展示通讯录数据
void ShowContact(contact* con);//链表的打印//查找通讯录数据
void FindContact(contact* con);//修改通讯录数据
void ModifyContact(contact** con);//销毁通讯录数据
//保存信息
void DestoryContact(contact** con);

第三步:
通讯录的功能实现:
其中需要包含两个头文件, 其实通讯录底层的方法就是用到了单链表的方法, 所以这里我们都需要包含:

#include"Contact.h"
#include"SList.h"
  1. 初始化: 就是将原来的数据导入到新的通讯录之中

这里简单回顾一下二进制的文件操作函数, 更多详情可以查看cpp官网Cplusplus

	//fread从二进制文件中读取数据,//fread函数:// 以数组举例:  //size_t fread(const void* ptr, size_t size, size_t count, FILE* stream);//可以理解为一个读取到一个指向首元素的数组, ptr是首元素的地址, //size为一个元素的大小,单位字节, count为一个要读取多少个元素, stream为读取的那个文件的文件流//首先得有一个大于count字节数组,然后把首地址给到fread,让fread读取到的数据保存到这个数组中//如://doubule b[size]//fread(b, sizeof * b, size, "fp");// fwrite函数:// size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);// 理解为写入到文件中, ptr为数组首元素, size写入的每个元素的字节, count写入多少个这样的元素, stream文件流// 
void LoadContact(contact** con)
{FILE* pf = fopen("contact.txt", "rb");//二进制只读,不创建新文件if (pf == NULL){perror("fopen error!\n");return;}//打开成功PeoInfo info;//创建一个通讯录变量while (fread(&info, sizeof(info), 1, pf))//读取成功返回count,读取不成功返回0{PushBack(con, info);}printf("历史数据导入通讯录成功\n");
}void InitContact(contact** con)
{LoadContact(con);
}
  1. 通讯录的增加

简单理解, 创建一个通讯录的类型的变量, 根据用户的输入进行初始化, 完成之后调用单链表尾插法.

void AddContact(contact** con)
{PeoInfo info;printf("请输入姓名: \n");scanf("%s",info.name);printf("请输入性别: \n");scanf("%s",info.sex);printf("请输入年龄: \n");scanf("%d",&info.age);printf("请输入电话: \n");scanf("%s",info.tel);printf("请输入地址: \n");scanf("%s",info.addr);PushBack(con, info);printf("插入成功!\n");
}
  1. 通讯录的删除

这里实现的是根据姓名, 当然也可以其他方式, 但逻辑都是一样的, 单独封装一个根据姓名查找的函数, 变量查找, 并返回节点, 如果找到了就直接调用单链表指定位置删除函数,这里也不难发现, 其实通讯录就是基于单链表进行套娃.

contact* FindByName(contact* con,char name[])
{contact* cur = con;while (cur){if (strcmp(name, (con->data).name) == 0){return cur;}cur = cur->next;}return NULL;
}//根据姓名来删除
void DelContact(contact** con)
{char name[NAME_MAX];printf("请输入要删除的用户姓名:\n");scanf("%s",name);contact* pos = FindByName(*con,name);if (pos == NULL){printf("要删除的用户不存在,删除失败\n");return;}Erase(con,pos);printf("删除成功\n");
}
  1. 展示通讯录

占位符保持一致, -n%,-表示右对齐,n表示多少字符对齐, 变量展示数据.

void ShowContact(contact* con)
{printf("%-10s %-4s %-4s %-15s %-20s\n", "姓名", "性别", "年龄", "联系电话", "地址");contact* cur = con;while (cur){printf("%-10s %-4s %-4d %-15s %-20s\n",cur->data.name,cur->data.sex,cur->data.age,cur->data.tel,cur->data.addr);cur = cur->next;}
}
  1. 查找通讯录

因为我们封装过姓名查找的函数方法了, 所以这里就可以直接使用查找姓名的方式来显示联系人,
找到之后打印数据.

void FindContact(contact* con)
{char name[NAME_MAX];printf("请输入要查找的用户姓名:\n");scanf("%s",name);contact* pos = FindByName(con,name);if (pos == NULL){printf("要查找的用户不存在,查找失败\n");return;}printf("查找成功\n");printf("%-10s %-4s %-4s %-15s %-20s\n", "姓名", "性别", "年龄", "联系电话", "地址");printf("%-10s %-4s %-4d %-15s %-20s\n",pos->data.name,pos->data.sex,pos->data.age,pos->data.tel,pos->data.addr);
}
  1. 修改通讯录

根据姓名就行修改, 先查找,找到之后直接修改就行.

void ModifyContact(contact** con)
{char name[NAME_MAX];printf("请输入要修改的用户名称:\n");scanf("%s",&name);contact* pos = FindByName(*con, name);if (pos == NULL){printf("要查找的用户不存在,修改失败\n");return;}printf("请输入要修改的姓名: \n");scanf("%s",pos->data.name);printf("请输入要修改的性别: \n");scanf("%s",pos->data.sex);printf("请输入要修改的年龄: \n");scanf("%d",&pos->data.age);printf("请输入要修改的电话: \n");scanf("%s",pos->data.tel);printf("请输入要修改的地址: \n");scanf("%s",pos->data.addr);printf("修改成功\n");
}
  1. 通讯录扫尾

先保存数据, 封装一个函数保存数据, 以二进制的形式循环的输入到指定文件, 知道循环结束,最后调用单链表销毁函数, 释放内存

void SaveContact(contact* con)
{FILE* pf = fopen("contact.txt", "wb");if (pf == NULL){perror("fopen error\n");return;}contact* cur = con;while (cur){fwrite(&(cur->data), sizeof(cur->data), 1, pf);cur = cur->next;}printf("通讯录数据保存成功\n");
}void DestoryContact(contact** con)
{SaveContact(*con);SListDesTory(con);
}

第四步:

进行主函数的封装:

SLIst.h 已经包含了Contact.h所以我们直接包含SList.h就可以了, 封装一个函数打印初始菜单,我这里没有进行初始化, 是因为没有之前的数据可导入, 需要的也可自行加上.


#define _CRT_SECURE_NO_WARNINGS 1#include"SList.h"
void menu()
{printf("********************通讯录*********************\n");printf("********1.增加联系人    2.删除联系*************\n");printf("********3.修改联系人    4.查找联系人***********\n");printf("********5.展示联系人    0.退出*****************\n");printf("***********************************************\n");
}int main()
{//test01();int op = 0;contact* con = NULL;do {menu();scanf("%d", &op);switch (op){case 1:AddContact(&con);break;case 2:DelContact(&con);break;case 3:ModifyContact(&con);break;case 4:FindContact(con);break;case 5:ShowContact(con);break;case 0:printf("退出通讯录~~\n");break;default:break;}}while (op);DestoryContact(&con);return 0;
}

5. 效果展示

下面是我在控制台界面自行测试的结果,需要其他效果也可自行修改

在这里插入图片描述

在这里插入图片描述


6. 完整代码

SList.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"Contact.h"typedef PeoInfo Datatype;typedef struct SListNode
{Datatype data;struct SListNode* next;
}SNode;//void SLTPrint(SNode* phead);void PushBack(SNode** pphead, Datatype x);
void PushFront(SNode** pphead, Datatype x);
void PopBack(SNode** pphead);
void PopFront(SNode** pphead);//SNode* FindNode(SNode* phead, Datatype x);
void SLTInsert(SNode** pphead, SNode* pos, Datatype x);
void SLTInsertAfter(SNode* pos, Datatype x);void Erase(SNode** pphead, SNode* pos);
void SlTErase(SNode* pos);void SListDesTory(SNode** pphead);

SList.c

#define _CRT_SECURE_NO_WARNINGS 1#include"SList.h";//void SLTPrint(SNode* phead)
//{
//	SNode* pcur = phead;
//	while (pcur)
//	{
//		printf("%d -> ", pcur->data);
//		pcur = pcur->next;
//	}
//	printf("NULL\n");
//}SNode* BuyNode(Datatype x)
{SNode* NewNode = (SNode*)malloc(sizeof(SNode));if (NewNode == NULL){perror("malloc fail");exit(1);}NewNode->data = x;NewNode->next = NULL;return NewNode;
}void PushBack(SNode** pphead, Datatype x)
{assert(pphead);SNode* NewNode = BuyNode(x);SNode* pcur = *pphead;if (pcur == NULL){*pphead = BuyNode(x);*pphead = NewNode;}else{while (pcur->next){pcur = pcur->next;}pcur->next = NewNode;}
}void PushFront(SNode** pphead, Datatype x)
{assert(pphead);SNode* NewNode = BuyNode(x);NewNode->next = *pphead;*pphead = NewNode;
}void PopBack(SNode** pphead)
{assert(pphead && *pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SNode* ptail = *pphead;SNode* prev = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;prev->next = NULL;}
}void PopFront(SNode** pphead)
{assert(pphead && *pphead);SNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}void SLTInsert(SNode** pphead, SNode* pos, Datatype x)
{assert(pphead && *pphead);assert(pos);SNode* NewNode = BuyNode(x);if (pos == *pphead){PushFront(pphead, x);}else{SNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}NewNode->next = pos;prev->next = NewNode;}
}void SLTInsertAfter(SNode* pos, Datatype x)
{assert(pos);SNode* NewNode = BuyNode(x);NewNode->next = pos->next;pos->next = NewNode;
}void Erase(SNode** pphead, SNode* pos)
{assert(pphead && *pphead);assert(pos);if (pos == *pphead){PopFront(pphead);}else {SNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev pos pos->nextprev->next = pos->next;free(pos);pos = NULL;}
}void SlTErase(SNode* pos)
{assert(pos && pos->next);SNode* del = pos->next;//pos del del->nextpos->next = del->next;free(del);del = NULL;
}void SListDesTory(SNode** pphead)
{assert(pphead && *pphead);SNode* pcur = *pphead;while (pcur){SNode* next = pcur->next;free(pcur);pcur = next;}//pcur*pphead = NULL;
}

Contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Contact.h"
#include"SList.h"void LoadContact(contact** con)
{FILE* pf = fopen("contact.txt", "rb");//二进制只读,不创建新文件if (pf == NULL){perror("fopen error!\n");return;}//打开成功PeoInfo info;//创建一个通讯录变量while (fread(&info, sizeof(info), 1, pf))//读取成功返回count,读取不成功返回0{PushBack(con, info);}printf("历史数据导入通讯录成功\n");
}void InitContact(contact** con)
{LoadContact(con);
}void AddContact(contact** con)
{PeoInfo info;printf("请输入姓名: \n");scanf("%s",info.name);printf("请输入性别: \n");scanf("%s",info.sex);printf("请输入年龄: \n");scanf("%d",&info.age);printf("请输入电话: \n");scanf("%s",info.tel);printf("请输入地址: \n");scanf("%s",info.addr);PushBack(con, info);printf("插入成功!\n");
}contact* FindByName(contact* con,char name[])
{contact* cur = con;while (cur){if (strcmp(name, (con->data).name) == 0){return cur;}cur = cur->next;}return NULL;
}//根据姓名来删除
void DelContact(contact** con)
{char name[NAME_MAX];printf("请输入要删除的用户姓名:\n");scanf("%s",name);contact* pos = FindByName(*con,name);if (pos == NULL){printf("要删除的用户不存在,删除失败\n");return;}Erase(con,pos);printf("删除成功\n");
}void ShowContact(contact* con)
{printf("%-10s %-4s %-4s %-15s %-20s\n", "姓名", "性别", "年龄", "联系电话", "地址");contact* cur = con;while (cur){printf("%-10s %-4s %-4d %-15s %-20s\n",cur->data.name,cur->data.sex,cur->data.age,cur->data.tel,cur->data.addr);cur = cur->next;}
}void FindContact(contact* con)
{char name[NAME_MAX];printf("请输入要查找的用户姓名:\n");scanf("%s",name);contact* pos = FindByName(con,name);if (pos == NULL){printf("要查找的用户不存在,查找失败\n");return;}printf("查找成功\n");printf("%-10s %-4s %-4s %-15s %-20s\n", "姓名", "性别", "年龄", "联系电话", "地址");printf("%-10s %-4s %-4d %-15s %-20s\n",pos->data.name,pos->data.sex,pos->data.age,pos->data.tel,pos->data.addr);
}void ModifyContact(contact** con)
{char name[NAME_MAX];printf("请输入要修改的用户名称:\n");scanf("%s",&name);contact* pos = FindByName(*con, name);if (pos == NULL){printf("要查找的用户不存在,修改失败\n");return;}printf("请输入要修改的姓名: \n");scanf("%s",pos->data.name);printf("请输入要修改的性别: \n");scanf("%s",pos->data.sex);printf("请输入要修改的年龄: \n");scanf("%d",&pos->data.age);printf("请输入要修改的电话: \n");scanf("%s",pos->data.tel);printf("请输入要修改的地址: \n");scanf("%s",pos->data.addr);printf("修改成功\n");
}void SaveContact(contact* con)
{FILE* pf = fopen("contact.txt", "wb");if (pf == NULL){perror("fopen error\n");return;}contact* cur = con;while (cur){fwrite(&(cur->data), sizeof(cur->data), 1, pf);cur = cur->next;}printf("通讯录数据保存成功\n");
}void DestoryContact(contact** con)
{SaveContact(*con);SListDesTory(con);
}

Contact.h

#pragma once#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100typedef  struct SListNode contact;typedef struct PersonInfo
{char name[NAME_MAX];char sex[SEX_MAX];int age;char tel[TEL_MAX];char addr[ADDR_MAX];
}PeoInfo;//初始化,把之的数据导入
void InitContact(contact** con);//添加通讯录数据
void AddContact(contact** con);//删除通讯录数据
void DelContact(contact** con);//链表删除指定位置的数据//展示通讯录数据
void ShowContact(contact* con);//链表的打印//查找通讯录数据
void FindContact(contact* con);//修改通讯录数据
void ModifyContact(contact** con);//销毁通讯录数据
//保存信息
void DestoryContact(contact** con);

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"SList.h"//void test01()
//{
//	SNode* plist = NULL;
//	PushBack(&plist, 200);
//	PushFront(&plist, 300);
//
//	PopBack(&plist);
//
//	SLTPrint(plist);
//}void menu()
{printf("********************通讯录*********************\n");printf("********1.增加联系人    2.删除联系*************\n");printf("********3.修改联系人    4.查找联系人***********\n");printf("********5.展示联系人    0.退出*****************\n");printf("***********************************************\n");
}int main()
{//test01();int op = 0;contact* con = NULL;do {menu();scanf("%d", &op);switch (op){case 1:AddContact(&con);break;case 2:DelContact(&con);break;case 3:ModifyContact(&con);break;case 4:FindContact(con);break;case 5:ShowContact(con);break;case 0:printf("退出通讯录~~\n");break;default:break;}}while (op);DestoryContact(&con);return 0;
}

7. 总结

总的来说,单链表是一种简单且灵活的数据结构,非常适合实现通讯录功能。它可以实现通讯录的常见操作,并且具有较好的扩展性和性能优化的空间.

后续更多精彩, 请持续关注~

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

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

相关文章

【研发管理】产品经理知识体系-组合管理

导读&#xff1a;新产品开发的组合管理是一个重要的过程&#xff0c;它涉及到对一系列新产品开发项目进行策略性选择、优先级排序、资源分配和监控。这个过程旨在确保企业能够最大化地利用有限的资源&#xff0c;以实现其战略目标。 目录 1、组合管理、五大目标 2、组合管理的…

第74天:漏洞发现-Web框架中间件插件BurpSuite浏览器被动主动探针

目录 思维导图 前置知识 案例一&#xff1a;浏览器插件-辅助&资产&漏洞库-Hack-Tools&Fofa_view&Pentestkit 案例二&#xff1a; BurpSuite 插件-被动&特定扫描-Fiora&Fastjson&Shiro&Log4j 思维导图 前置知识 目标&#xff1a; 1. 用…

基于springboot实现公司日常考勤系统项目【项目源码+论文说明】

基于springboot实现公司日常考勤系统演示 摘要 目前社会当中主要特征就是对于信息的传播比较快和信息内容的安全问题&#xff0c;原本进行办公的类型都耗费了很多的资源、传播的速度也是相对较慢、准确性不高等许多的不足。这个系统就是运用计算机软件来完成对于企业当中出勤率…

数据结构-链表OJ

1.删除链表中等于给定值 val 的所有结点。 . - 力扣&#xff08;LeetCode&#xff09; 思路一&#xff1a;遍历原链表&#xff0c;将值为val的节点释放掉 思路二&#xff1a;创建一个新链表&#xff0c;将值不为val的节点尾插到新链表中 /*** Definition for singly-linked …

2024年五一数学建模竞赛C题论文首发

基于随机森林的煤矿深部开采冲击地压危险预测 摘要 煤炭作为中国重要的能源和工业原料&#xff0c;其开采活动对国家经济的稳定与发展起着至关重要的作用。本文将使用题目给出的数据探索更为高效的数据分析方法和更先进的监测设备&#xff0c;以提高预警系统的准确性和可靠性…

智能消费记账|基于SSM+vue的大学生智能消费记账系统(源码+数据库+文档)

智能消费记账目录 基于SSMvue的大学生智能消费记账系统 一、前言 二、系统设计 三、系统功能设计 1 用户列表 2 预算信息管理 3 预算类型管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1…

代码随想录算法训练营DAY48|C++动态规划Part9|121.买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III

文章目录 121.买卖股票的最佳时机思路CPP代码 122.买卖股票的最佳时机II思路CPP代码 123.买卖股票的最佳时机III思路CPP代码 121.买卖股票的最佳时机 力扣题目链接 文章讲解&#xff1a;121.买卖股票的最佳时机 视频讲解&#xff1a;动态规划之 LeetCode&#xff1a;121.买卖股…

Android 音视频基础知识

本系列文章会介绍两个 Android NDK Demo&#xff0c;拉流端会实现一个基于 FFmpeg 的视频播放器 Demo&#xff0c;推流端会实现一个视频直播 Demo&#xff0c;当然在做 Demo 之前会介绍音视频的基础知识。以下是本系列文章的目录&#xff1a; Android 音视频基础知识 Android 音…

抢先体验:MacOS成功安装PHP8.4教程

根据官方消息&#xff0c;PHP 8.4将于2024年11月21日发布。它将通过三个 alpha 版本、三个 beta 版本和六个候选版本进行测试。 这次的重大更新将为PHP带来许多优化和强大的功能。我们很高兴能够引导您完成最有趣的更新升级&#xff0c;这些更改将使我们能够编写更好的代码并构…

Mac brew安装Redis之后更新配置文件的方法

安装命令 brew install redis 查看安装位置命令 brew list redis #查看redis安装的位置 % brew list redis /usr/local/Cellar/redis/6.2.5/.bottle/etc/ (2 files) /usr/local/Cellar/redis/6.2.5/bin/redis-benchmark /usr/local/Cellar/redis/6.2.5/bin/redis-check-ao…

WebAssembly学习记录

1.WebAssembly 1.1 指令集 概念&#xff1a;二进制编码集合。 依据计算机组成原理和计算机概论&#xff0c;指令集是一组二进制编码。 作用&#xff1a;控制硬件。 这些二进制指令直接作用于硬件电路&#xff0c;控制硬件完成指定操作。 例如&#xff1a;控制数据进入某个寄存…

【unocss】自用

unocss中文官网1 不知道简写的可以在这里查 第一步 npm install -D unocss第二步 // vite.config.ts import UnoCSS from unocss/vite import { defineConfig } from viteexport default defineConfig({plugins: [UnoCSS()] })// main.ts import virtual:uno.css第三步 在…

基于Springboot的在线博客网站

基于SpringbootVue的在线博客网站的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 博客标签 博客分类 博客列表 图库相册 后台登录 后台首页 用户管理 博客标…

Android 设置头像 - 裁剪及圆形头像

书接上文 Android 设置头像 - 相册拍照&#xff0c;通过相册和照片的设置就可以获取到需要的头像信息&#xff0c;但是在通常情况下&#xff0c;我们还想要实现针对头像的裁剪功能和圆形头像功能。 先上截图&#xff1a; 图像裁剪 通常裁剪可以分为程序自动裁剪和用户选择裁剪…

基于SpringBoot实现各省距离Excel导出实战

目录 前言 一、列表及图表信息展示 1、数据过滤调整 2、信息列表及图表展示 3、Excel写入 二、界面可视化 1、Echarts图表和列表展示 2、城市详情和下载功能设计 三、成果展示 1、图表展示 2、部分城市数据分析 总结 前言 今天是五一黄金周假期第二天&#xff0c;不知…

电脑自带dll修复在哪里,使用dll修复工具解决dll问题

在我们日常与电脑相伴的工作与学习过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中最常见的就是“无法找到.dll”或“找不到.dll文件”。这种情况通常是由于dll文件丢失或损坏导致的。dll文件是动态链接库文件&#xff0c;它包含了许多程序运行所需的函数和资源…

使用 BurpSuite 基于 Token 机制实施暴力破解

前言 Token是一种用于身份验证和授权的令牌&#xff0c;通常由服务器生成并发送给客户端&#xff0c;客户端在后续的请求中携带该令牌来进行身份验证和授权操作。Token的使用可以增强应用程序的安全性&#xff0c;避免了直接传递敏感凭证&#xff08;如用户名和密码&#xff0…

Golang | Leetcode Golang题解之第61题旋转链表

题目&#xff1a; 题解&#xff1a; func rotateRight(head *ListNode, k int) *ListNode {if k 0 || head nil || head.Next nil {return head}n : 1iter : headfor iter.Next ! nil {iter iter.Nextn}add : n - k%nif add n {return head}iter.Next headfor add > …

golang判断通道chan是否关闭的2种方式

chan通道在go语言的办法编程中使用频繁&#xff0c;我们可以通过以下2种方式来判断channel通道是否已经关闭&#xff0c;1是使用 for range循环&#xff0c;另外是通过 for循环中if 简短语句的 逗号 ok 模式来判断。 示例代码如下&#xff1a; //方式1 通过for range形式判断…

进销存单机版和excel进销存那个好用

进销存单机版和EXCEL进销存哪个好用&#xff1f;单机版是安装在单台电脑上使用的&#xff0c;它不能像网络版一样可以多台电脑同时共享数据&#xff0c;所以进销存单机版有一个优势就是不需要连接网络也可以使用。 进销存单机版 进销存软件单机版是经过开发人员设计好的一种信…