顺序表的学习与通讯录的模拟实现

前面几篇文章,我们已经大概的学习了c语言的整体基础知识。那么,学会了前面那些知识,我们当如何应用?接下来,我们就讲一个数据结构的基础应用:顺序表。

顺序表是什么呢?我们平时存储一串数据的时候,一般使用的是数组,但是数组有个缺点,那就是数组的大小是固定的,无法调整的,当你用[]时,那个数组的大小就已经确定了。但有时我们需要对数组中的元素进行增删查改,那要怎么办呢?此时,顺序表就可以完成这份工作

typedef struct seqlist
{
	TypeSort* arr;
	int size;
	int capcity;

}SL;

假设,这是一份名为seqlist的结构体,我将他重定义名字为SL。这个结构体中存放了一个指针(arr),一个数字(size),和另一个数字(capcity),指针的类型是TypeSort,可以用#define来定义不同的类型以灵活调用,size描述的是当前数组已经包含的数据的数量,capcity表示的是当前数组的总大小。这样的一个结构体变量,就构成了一个顺序表。那么,我们要如何添删查改呢?这就是本篇文章的重点内容啦

顺序表

初始化结构体

我们在使用结构体的时候,是需要对内部的三个变量进行初始化的,否则会造成一些错误。比如,如不初始化结构体中的arr的话,会导致这个arr是个野指针。因此,我们在最开始写一个这样的函数。

void Init_seqlist(SL* sl)
{
	sl->size = 0;
	sl->capcity = 0;
	sl->arr = NULL;
}

增添数据

 在初始化以后,我们可以向数组中添加数据,若空间足够大的话,我们可以直接进行赋值,将某个数据直接写入这个参数之中。但空间不够大呢?那我们就需要对空间进行扩容。

空间扩容

首先,我们需要先知道空间够不够大,才知道需不需要扩容,因此我们需要在函数中先写一个if语句,判断size和capcity是否相等。若相等,则进入扩容函数。在扩容函数中,我们先定义一个局部变量new_capcity,然后先判断capcity是否为0,如果为0,那我们就让capcity=Init_capcity, Init_capcity是我们利用define定义的一个数据,在此代码中,他就是4.如果capcity不为0,那就让他扩大两倍并用new_capcity来接受,接着用realloc创建一个空间,并用一个局部指针来接收,然后断言ptr是否为空指针,若不是空指针,那就让原数组的地址调整为ptr,接着把结构体中的capcity改成new_capcity.

void Longer_sl(SL*sl)
{
	int  new_capcity = 0;
	if (sl->capcity == 0)
	{
		new_capcity = Init_capcity;
	}
	else
		new_capcity = 2 * sl->capcity;
		TypeSort* ptr = (TypeSort*)realloc(sl->arr,sizeof(TypeSort)* new_capcity);
		assert(ptr);
		sl->arr = ptr;
		sl->capcity = new_capcity;
}

尾部插入

在掌握了扩容以后,尾部插入便显得及其简单了。我们直接将数据赋值给arr指向的地址的size位置。然后size++就可以了

void AddSL_Back(SL* example,TypeSort data)
{
	assert(example);
	if (example->size == example->capcity)
	{
		Longer_sl(example);
	}
	example->arr[example->size] = data;
	example->size++;//也可以放在里面++

}

头部插入

头部插入和尾部插入略有不同。为了将数据插入到头部,那原先所有的数据都需要往后面移一位。所以在尾插的基础上,我们需要将原先的所有数据往后面移动一位。为了更加高效的移动,我们从后面开始移动。最后实现的代码如图所示。

void AddSL_Head(SL* example, TypeSort data)
{
	assert(example);
	if (example->size == example->capcity)
	{
		Longer_sl(example);
	}
	for (int i = example->size; i >= 0; i--)
	{
		example->arr[i + 1] = example->arr[i];//arr[1] = arr[0]
	}

	example->arr[0] = data;
	example->size++;//也可以放在里面++

}

随意插入

随意插入和头部插入又很相似,只不过这次又多了一个参数dire,这个参数就是决定在哪里插入。接着将dire地址往后的数据都往后面移动一个位置。

void AddSL_ALL(SL* example, TypeSort data, int dire)//4
{
	assert(example);
	if (example->size == example->capcity)
	{
		Longer_sl(example);
	}
	for (int i = example->size; i >=dire ; i--)
	{
		example->arr[i + 1] = example->arr[i];
	}
	example->arr[dire] = data;
	example->size++;//也可以放在里面++
}

删除数据

数据的删除便不需要空间的扩容了,数据的删除相比较于数据的增加较为简单。因此,我便一次性将所有删除的方法全部列出来了

void DelateSL_back(SL* example)
{
	assert(example);
	assert(example->size);
	example->size--;
}
void DelateSL_Head(SL* example)
{
	assert(example);
	assert(example->size);
	for (int i = 0; i < example->size; i--)
	{
		example->arr[i] = example->arr[i+1];
	}
	example->size--;
}
void DelateSL_ALL(SL* example,int dire)
{
	assert(example);
	assert(example->size&&dire<example->size);
	for (int i = dire; i < example->size-1; i--)
	{
		example->arr[i] = example->arr[i + 1];
	}
	example->size--;
	
}

数据查询

要查找到该顺序表中是否有目标顺序,我们同样需要用到for循环。我们需要从0位置一直往后面进行查询,一直到最后。如果在这个过程中就找到了该数据,那就返回该数据指向的下标。若没找到,那就返回打印没找到。因为我这里后续是在通讯录中利用名字来进行查询,因此该代码不具有普遍性。

int FindName_SL(SL* example, char *data)
{
	assert(example);
	for (int i = 0; i < example->size; i++)
	{
		if (0 == strcmp(example->arr[i].name ,data))
		{
			printf("找到了,在%d下标处\n", i);
			return i;
		}
	}
	return -1;
}

摧毁结构体

当我们使用完顺序表以后,我们需要及时的将他所占的内存释放,否则会占用我们的内存空间,释放的过程如下图所示。

void Init_seqlist(SL* sl)
{
	sl->size = 0;
	sl->capcity = 0;
	sl->arr = NULL;
}

通讯录

在我们学会了顺序表以后,那我们要如何使用呢?接下来,我们就来举例使用顺序表实现通讯录的功能。

首先,通讯录的本质和上面的顺序表是一样的,都是一个数组,一个size表示当前数据的数量,一个capcity表示当前数组的最大值。但是现在数组内储存的不再是int 或者char类型了,它所储存的是一个结构体类型,这个结构体的定义如下:

typedef struct man
{
	char name[NAME_MAX];
	char HomeAddr[ADDR_MAX];
	char  Tele[TELE_MAX];
	char gender[TELE_MAX];
	int age;
}x;

如图,我将结构体赋予了5个属性,分别是名字家庭住址,电话,性别以及年龄。现在,我们可以开始针对通讯录这个结构体进行操作了。

初始化与摧毁

因为通讯录的本质是一个顺序表,只不过数组里面存的是指向另一个结构体的指针,所以通讯录的初始化与摧毁和原先的顺序表相同。

void init_contact(SL* con)
{
	void Init_seqlist(SL*con);//获得了一个顺序表
}

void Des_contact(SL* con)
{
	seqlist_Destory(con);
}

添加数据

在这个环节,我们就开始给通讯录添加数据了,在这里我们统一使用尾插的方法进行添加。我们首先定义一个x类型的数据,接着通过scanf由用户往里面输入数据,然后再讲被填好的x类型数据传给顺序表的尾插函数,这样子,一个x类型数据就传入到通讯录中了。

void Add_man(SL* con)
{
	x infor;
	printf("请输入用户名字\n");
	scanf("%s", infor.name);
	printf("请输入用户年龄\n");
	scanf("%d", &infor.age);
	printf("请输入用户性别\n");
	scanf("%s", infor.gender);
	printf("请输入用户电话\n");
	scanf("%s", infor.Tele);
	printf("请输入用户家庭住址\n");
	scanf("%s", infor.HomeAddr);
	AddSL_Back(con, infor);
}

删除数据

删除数据,我们需要先找一下该数据是否存在于该通讯录中,我们定义将要删除的人的名字放入我们前面写的顺序表顺序查询函数,然后用一个int类型的数据来接受这个返回值。如果函数返回的是的-1也就是小于0,那么就是数据不存在,那么就告诉用户数据不存在并直接返回。若函数返回了相对应下标,那就是数据存在,接着就调用顺序表的尾删函数,并打印删除成功。

void Delate_man(SL* con)
{
	char Name[NAME_MAX];
	printf("请输入你要删除的人的名字\n");
	scanf("%s", Name);
	int find = FindName_SL(con, Name);
	if (find < 0)
	{
		printf("你要删的不存在!");
		return;
	}
	else
	{
		DelateSL_ALL(con,find);
		printf("删除成功");

	}
}

改变数据

前面部分与删除数据类似,当find的值小于0的时候直接返回,当大于零的时候,我们就让用户输入新的数据。

void Change_man(SL* con)
{
	char Name[NAME_MAX];
	printf("请输入你要改变的人的名字\n");
	scanf("%s", Name);
	int find = FindName_SL(con, Name);
	if (find < 0)
	{
		printf("你要改的人不存在!");
		return;
	}
	else
	{
		printf("请输入新的姓名:\n");
		scanf("%s", con->arr[find].name);

		printf("请输入新的性别:\n");
		scanf("%s", con->arr[find].gender);

		printf("请输入新的年龄:\n");
		scanf("%d", &con->arr[find].age);

		printf("请输入新的电话:\n");
		scanf("%s", con->arr[find].Tele);

		printf("请输入新的住址:\n");
		scanf("%s", con->arr[find].HomeAddr);
		printf("改变成功!");
	}
}

查找并显示数据

在该环节,前列的代码并不两样,主要的区别在后面部分。当我们确定该数据存在的时候,直接将数据打印出来(我这个格式打印出来的好像不是很好看,各位可自行调格式)

void ContactFind(SL* con)
{
	//11
	char name[NAME_MAX];
	printf("请输入要查找的联系人姓名\n");
	scanf("%s", name);
	int find = FindName_SL(con, name);
	if (find < 0)
	{
		printf("要查找的联系人数据不存在!\n");
		return;
	}
	// 姓名 性别 年龄 电话  地址
	// 11   11   11   11   11
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
	printf("%3s %3s %3d %3s %3s\n", //手动调整一下格式
		con->arr[find].name,
		con->arr[find].gender,
		con->arr[find].age,
		con->arr[find].Tele,
		con->arr[find].HomeAddr
	);
}

最后的整合

当我们把这些代码写完以后,我们需要把他们串在一起,并在main函数中使用。首先是各个c文件的头文件,两个头文件对自己的函数进行声明,并包含了所需要的库函数。并且为了后续数据以及类型的方便转换,我们还使用了#define对数据进行了定义。因为struct seqlist 与struct man在c文件的重命名无法直接在头文件里面使用,因此我们需要在头文件中重新进行一次命名。

#pragma once
#include "stdio.h"
#include "assert.h"
#include "stdlib.h"
#include "contact.h"
#define Init_capcity 4
typedef x TypeSort;
typedef struct seqlist
{
	TypeSort* arr;
	int size;
	int capcity;

}SL;

void Init_seqlist(SL* sl);
void seqlist_Destory(SL* sl);
void Longer_sl(SL* sl);
void AddSL_Back(SL* example, TypeSort data);
void AddSL_Head(SL* example, TypeSort data);
void AddSL_ALL(SL* example, TypeSort data, int dire);//4
void DelateSL_back(SL* example);
void DelateSL_Head(SL* example);
void DelateSL_ALL(SL* example, int dire);
int FindName_SL(SL* example, char* data);
#pragma once
#include "stdio.h"
#include "stdlib.h"

#define ADDR_MAX 100
#define NAME_MAX 20
#define TELE_MAX 20

typedef struct man
{
	char name[NAME_MAX];
	char HomeAddr[ADDR_MAX];
	char  Tele[TELE_MAX];
	char gender[TELE_MAX];
	int age;
}x;
typedef struct seqlist SL;
void init_contact(SL* con);
void Des_contact(SL* con);
void Add_man(SL* con);
void Delate_man(SL* con);
void Change_man(SL* con);
void ContactFind(SL* con);

到最后的main文件处,为了显示的美观与清楚,我们提前写了一个简单的菜单文件。在main函数中,我们首先创建一个SL 型的数据叫做con,接着对这个顺序表进行了初始化,接着让用户根据菜单选择操作,利用do-while和switch语句可以实现对应的功能。当用户最后退出通讯录以后,将这个通讯录以txt文件形式输出并摧毁这个通讯录,接着结束函数。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "seqlist.h"
#include "contact.h"

FILE* Data;

void menu()
{
	printf("******************\n");
	printf("*****1.添加联系人   2.删除联系人*********\n");
	printf("*****3.改变联系人   4.查找联系人*********\n");
	printf("*************0.退出通讯录****************\n");
}

void Txt_Write(SL* contact)
{
	SL person = *contact;
	Data = fopen("tonguxnlu.txt", "w");
	TypeSort* First = person.arr;
	for (int i = 0; i < person.size; i++)
	{
		fprintf(Data,"%s    ", person.arr->name);
		person.arr++;
	}
	fprintf(Data, "\n");
	person.arr= First;
	for (int i = 0; i < person.size; i++)
	{
		fprintf(Data,"%d    ", person.arr->age);
		person.arr++;
	}
	fprintf(Data, "\n");
	person.arr = First;
	for (int i = 0; i < person.size; i++)
	{
		fprintf(Data,"%s    ", person.arr->Tele);
		person.arr++;
	}
	fprintf(Data, "\n");
	person.arr = First;
	for (int i = 0; i < person.size; i++)
	{
		fprintf(Data,"%s    ", person.arr->HomeAddr);
		person.arr++;
	}
	fprintf(Data, "\n");
	person.arr = First;
	for (int i = 0; i < person.size; i++)
	{
		fprintf(Data,"%s    ", person.arr->gender);
		person.arr++;
	}
	fprintf(Data, "\n");
	person.arr = First;
	fclose(Data);

}

int main()
{
	SL con;
	Init_seqlist(&con);
	int opt = 0;
	do
	{
		menu();
		printf ("请输入你的选择\n");
		scanf("%d", &opt);	
		switch (opt)
		{
		case 1:
			Add_man(&con);
			break;
		case 2:
			Delate_man(&con);
			break;
		case 3:
			Change_man(&con);
			break;
		case 4:
			ContactFind(&con);
			break;
		case 0:
			printf("退出通讯录!");
			Txt_Write(&con);
			break;
		default:
			printf("输入错误!请重新输入!");
			break;
		}
	} while (opt!=0);
	Des_contact(&con);
	return 0;
}

谢谢翻阅!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Delta lake with Java--使用stream同步数据

今天继续学习Delta lake Up and Running 的第8章&#xff0c;处理流数据&#xff0c;要实现的效果就是在一个delta表&#xff08;名为&#xff1a;YellowTaxiStreamSource&#xff09;插入一条数据&#xff0c;然后通过流的方式能同步到另外一个delta表 &#xff08;名为&#…

机器人系统可以支持对接人工系统吗?

​ 随着科技的飞速发展&#xff0c;机器人系统在各行各业都扮演着越来越重要的角色。它们可以高效地处理大量数据&#xff0c;执行繁琐的任务&#xff0c;甚至在某些领域超越了人类的能力。然而&#xff0c;机器人系统也有其局限性&#xff0c;特别是在处理复杂的人际交往…

自动驾驶主流芯片及平台架构(四)华为、​高通、英伟达高算力平台

上一章节有提到低算力的自动驾驶平台&#xff0c;本章内容重点介绍高算力的自动驾驶平台&#xff0c;华为、高通、英伟达。 华为自动驾驶MDC平台方案介绍 以整车数字架构为基础&#xff0c;全面管理软硬件的复杂性&#xff0c;并确保整车的可靠性&#xff1a;我们提出华为CCAVe…

批量图片重命名及汇总

又一堆图片文件需要处理... 源文件分布&#xff1a; 有N个文件夹&#xff0c;每个文件夹下又有M个子文件夹&#xff0c;每个子文件夹下有X张图片。 例如文件夹A下有子文件夹A1,A2,A3&#xff0c;子文件夹A1下有图片a-1,a-2,a-3...... 处理目标&#xff1a; 1、将所有图片汇…

算法提高之树的中心

算法提高之树的中心 核心思想&#xff1a;树形dp 换根dp 每个点作为根节点 找其子树的最大距离和父节点的最大距离 dfs1&#xff1a;求子树对于当前根节点的最大距离和次大距离 求次大距离原因&#xff1a;如果当前节点是其父节点子树的最大路径上的点&#xff0c;最大距离不…

基于Linux中的 进程相关知识 综合讲解

目录 一、进程的基本概念 二、pid&#xff0c;ppid&#xff0c;fork函数 三、进程的状态讲解 四、进程的优先级 五、完结撒❀ 一、进程的基本概念 概念&#xff1a; ● 课本概念&#xff1a;程序的一个执行实例&#xff0c;正在执行的程序等 ● 内核观点&#xff1a;担当…

深度学习实例2_车牌识别分割——自学笔记

import cv2 from matplotlib import pyplot as plt import os import numpy as np from PIL import ImageFont, ImageDraw, Image彩色图片显示 def plt_show0(img):b,g,r = cv2.split(img)img = cv2.merge([r, g, b])plt.imshow(img)plt.show()灰度图片显示 def plt_show(img…

暗区突围PC测试资格获取 Twitch老鼠台一键领取测试资格教程

Twitch平台&#xff0c;这个广受欢迎的直播巨头&#xff0c;不仅是游戏文化的直播聚集地&#xff0c;还常与各类游戏携手合作&#xff0c;为观众带来独特的互动体验&#xff0c;观看直播即可解锁游戏内奖励。正值热门游戏《暗区突围》PC版测试阶段&#xff0c;Twitch再次发力&a…

Ai时代使用语音笔记整理文稿提高创作效率

其实传统的创作方式是用钢笔或者圆珠笔手写草稿。成稿后花钱誊抄数份邮寄给出版商。 计算机普及后&#xff0c;有人开始直接使用打字机或计算机创做&#xff0c;打字其实要比手写的速度快数倍&#xff0c;这样效率的提升&#xff0c;加上文创平台基本上都是按字数给收益&#…

去哪找高清视频素材?哪个网站有视频素材?

在这个视觉表达日益重要的时代&#xff0c;获取高品质的视频素材变得尤为关键。4K和无水印视频素材特别受到创作者的青睐&#xff0c;因为它们能极大地提升视觉作品的吸引力和专业度。接下来&#xff0c;我将介绍几个国内外的优秀视频素材网站&#xff0c;助您在创作旅程上一帆…

为什么现在越来越多的人会选择陪诊

现在越来越多的人选择陪诊的原因有多方面。 首先&#xff0c;随着人口老龄化、医疗资源分配不均等问题的日益突出&#xff0c;许多老年人和病患在就医过程中面临诸多困难&#xff0c;如挂号、排队、取药等繁琐的手续和流程。陪诊服务能够为他们提供极大的便利&#xff0c;帮助…

[初阶数据结构】单链表

前言 &#x1f4da;作者简介&#xff1a;爱编程的小马&#xff0c;正在学习C/C&#xff0c;Linux及MySQL。 &#x1f4da;本文收录于初阶数据结构系列&#xff0c;本专栏主要是针对时间、空间复杂度&#xff0c;顺序表和链表、栈和队列、二叉树以及各类排序算法&#xff0c;持…

添砖Java之路其三——自增自减运算符,数据转换与原码反码补码。

目录 运算符&#xff1a; 转换&#xff1a; 隐式转换&#xff1a; 小范围数据可以直接可以给大范围数据&#xff1a; 这里做了一张图范围向下兼容表​编辑 运算时&#xff0c;数据范围小的和数据范围大的&#xff0c;需要讲运算范围小的提升为运算范围大的同类&#xff0c…

软考系列必过资料分享-系统架构师-系统分析师-信息系统项目管理师

建议,写在前面 知识点是公用的,原则上不分新旧。每年会有少部分的题目切合当前时间段&#xff08;也是通过旧的知识演变的&#xff09; 信息系统项目管理师证书 系统架构师证书 系统分析师证书 资料分享 关注公众号 回复 信息系统项目管理师资料 即可获取信息系统项目管理师资…

如何使用香草看涨期权进行投机?

如何使用香草看涨期权进行投机&#xff1f; 香草看涨期权&#xff0c;通常也称为香草期权&#xff0c;是金融市场上的一种金融衍生品&#xff0c;由券商或金融机构推出。使用香草看涨期权进行投机&#xff0c;主要依赖于对市场走势的预测和对杠杆效应的运用。以下是一些关键步…

【前端】-【前端文件操作与文件上传】-【前端接受后端传输文件指南】

目录 前端文件操作与文件上传前端接受后端传输文件指南 前端文件操作与文件上传 一、前端文件上传有两种思路&#xff1a; 二进制blob传输&#xff1a;典型案例是formData传输&#xff0c;相当于用formData搭载二进制的blob传给后端base64传输&#xff1a;转为base64传输&…

力扣HOT100 - 35. 搜索插入位置

解题思路&#xff1a; 二分法模板 class Solution {public int searchInsert(int[] nums, int target) {int left 0;int right nums.length - 1;while (left < right) {int mid left ((right - left) >> 1);if (nums[mid] target)return mid;else if (nums[mid…

spring模块(六)spring监听器(1)ApplicationListener

一、介绍 1、简介 当某个事件触发的时候&#xff0c;就会执行的方法块。 当然&#xff0c;springboot很贴心地提供了一个 EventListener 注解来实现监听。 2、源码&#xff1a; package org.springframework.context;import java.util.EventListener; import java.util.fu…

深入解析智能指针:从实践到原理

&#x1f466;个人主页&#xff1a;晚风相伴 &#x1f440;如果觉得内容对你有所帮助的话&#xff0c;还请一键三连&#xff08;点赞、关注、收藏&#xff09;哦 如果内容有错或者不足的话&#xff0c;还望你能指出。 目录 智能指针的引入 内存泄漏 RAII 智能指针的使用及原…

安卓LayoutParams浅析

目录 前言一、使用 LayoutParams 设置宽高二、不设置 LayoutParams2.1 TextView 的 LayoutParams2.2 LinearLayout 的 LayoutParams 三、getLayoutParams 的使用四、setLayoutParams 的作用五、使用 setWidth/setHeight 设置宽高 前言 先来看一个简单的布局&#xff0c;先用 x…
最新文章