【数据结构(顺序表)】

一、什么是数据结构?


数据结构是由"数据"和"结构"两词组合而来。
什么是数据?常见的数值1、2、3、4.....、教务系统里保存的用户信息(姓名、性别、年龄、学历等等)、网页里肉眼可以看到的信息(文字、图片、视频等等),这些都是数据
什么是结构?
当我们想要使用大量使用同⼀类型的数据时,通过手动定义大量的独立的变量对于程序来说,可读性非常差,我们可以借助数组这样的数据结构将大量的数据组织在⼀起,结构也可以理解为组织数据的方式。
想要找到草原上名叫"咩咩"的羊很难,但是从羊圈里找到1号羊就很简单,羊圈这样的结构有效将
羊群组织起来。
概念:数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在⼀种或多种特定关系
的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的结构。
总结:
1)能够存储数据(如顺序表、链表等结构)
2)存储的数据能够方便查找

二、为什么需要数据结构?

如图中所示,不借助排队的方式来管理客户,会导致客户就餐感受差、等餐时间长、餐厅营业混乱等情况。同理,程序中如果不对数据进行管理,可能会导致数据丢失、操作数据困难、野指针等情况。通过数据结构,能够有效将数据组织和管理在⼀起。按照我们的方式任意对数据进行增删改查等操作。最基础的数据结构:数组。
【思考】有了数组,为什么还要学习其他的数据结构?
假定数组有10个空间,已经使用了5个,向数组中插入数据步骤:
求数组的长度,求数组的有效数据个数,向下标为数据有效个数的位置插入数据(注意:这里是
否要判断数组是否满了,满了还能继续插⼊吗).....
假设数据量非常庞⼤,频繁的获取数组有效数据个数会影响程序执行效率。
结论:最基础的数据结构能够提供的操作已经不能完全满足复杂算法实现。

顺序表

一、顺序表的概念及结构

1.1、线性表

线性表( linear list )是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储。
案例:蔬菜分为绿叶类、⽠类、菌菇类。线性表指的是具有部分相同特性的⼀类数据结构的集合
如何理解逻辑结构和物理结构?

二、顺序表分类

• 顺序表和数组的区别
◦ 顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接⼝
• 顺序表分类
◦ 静态顺序表

概念:使用定长数组存储元素

静态顺序表缺陷:空间给少了不够用,给多了造成空间浪费

◦ 动态顺序表

三、动态顺序表的实现

#define INIT_CAPACITY 4

typedef int SLDataType;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
	SLDataType* a;
	int size; // 有效数据个数
	int capacity; // 空间容量
}SL;

//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);

//扩容
void SLCheckCapacity(SL* ps);

//头部插⼊删除 / 尾部插⼊删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);

//指定位置之前插⼊/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);

顺序表的应用

一、基于动态顺序表实现通讯录

C语言基础要求:结构体、动态内存管理、顺序表、文件操作

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

2、代码实现
【思考1】用静态顺序表和动态顺序表分别如何实现
【思考2】如何保证程序结束后,历史通讯录信息不会丢失

//SeqList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
#include<stdlib.h> 
#include"contact.h"

//数据类型为PersonInfo
typedef struct PersonInfo SQDataType;
//typedef int SQDataType;

//动态顺序表
typedef struct SeqList {
	SQDataType* a;
	int size;//保存有效数据个数
	int capacity;//空间的⼤⼩
}SLT;

//初始化与销毁
void SeqListInit(SLT* psl);
void SeqListDesTroy(SLT* psl);
void SeqListPrint(SLT sl);
void CheckCapacity(SLT* psl);

// 头部插⼊删除 / 尾部插⼊删除
void SeqListPushBack(SLT* psl, SQDataType x);
void SeqListPushFront(SLT* psl, SQDataType x);
void SeqListPopBack(SLT* psl);
void SeqListPopFront(SLT* psl);

//查找
int SeqListFind(SLT* psl, SQDataType x);
// 在指定位置之前插⼊/删除
//void SeqListInsert(SLT* psl, int pos, SQDataType x);
void SeqListInsert(SLT* psl, size_t pos, SQDataType x);
void SeqListErase(SLT* psl, size_t pos);

size_t SeqListSize(SLT* psl);
//修改指定位置的值
void SeqListAt(SLT* psl, size_t pos, SQDataType x);

//contact.h

#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SeqList 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 DestroyContact(contact* con);

//contact.c

#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
#include"SeqList.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(PeoInfo), 1, pf))
	{
		SeqListPushBack(con, info);
	}
	printf("历史数据导⼊通讯录成功!\n");
}

void InitContact(contact* con) {
	SeqListInit(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);
	SeqListPushBack(con, info);
	printf("插⼊成功!\n");
}

int FindByName(contact* con, char name[]) {
	for (int i = 0; i < con->size; i++)
	{
		if (0 == strcmp(con->a[i].name, name)) {
			return i;
		}
	}
	return -1;
}

void DelContact(contact* con) {
	char name[NAME_MAX];
	printf("请输⼊要删除的⽤⼾姓名:\n");
	scanf("%s", name);
	int pos = FindByName(con, name);
	if (pos < 0) {
		printf("要删除的⽤⼾不存在,删除失败!\n");
		return;
	}
	SeqListErase(con, pos);
	printf("删除成功!\n");
}

void ShowContact(contact* con) {
	printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "联系电话",
		for (int i = 0; i < con->size; i++)
		{
			printf("%-10s %-4s %-4d %15s %-20s\n",
				con->a[i].name,
				con->a[i].sex,
				con->a[i].age,
				con->a[i].tel,
				con->a[i].addr);
		}
}

void FindContact(contact* con) {
	char name[NAME_MAX];
	printf("请输⼊要查找的⽤⼾姓名:\n");
	scanf("%s", name);
	int pos = FindByName(con, name);
	if (pos < 0) {
		printf("要查找的⽤⼾不存在,查找失败!\n");
		return;
	}
	printf("查找成功!\n");
	printf("%-10s %-4s %-4d %15s %-20s\n",
		con->a[pos].name,
		con->a[pos].sex,
		con->a[pos].age,
		con->a[pos].tel,
		con->a[pos].addr);
}

void ModifyContact(contact* con) {
	char name[NAME_MAX];
	printf("请输⼊要修改的⽤⼾名称:\n");
	scanf("%s", name);
	int pos = FindByName(con, name);
	if (pos < 0) {
		printf("要查找的⽤⼾不存在,修改失败!\n");
		return;
	}
	PeoInfo info;
	printf("请输⼊要修改的姓名:\n");
	scanf("%s", &con->a[pos].name);
	printf("请输⼊要修改的性别:\n");
	scanf("%s", &con->a[pos].sex);
	printf("请输⼊要修改的年龄:\n");
	scanf("%d", &con->a[pos].age);
	printf("请输⼊要修改的联系电话:\n");
	scanf("%s", &con->a[pos].tel);
	printf("请输⼊要修改的地址:\n");
	scanf("%s", &con->a[pos].addr);
	printf("修改成功!\n");
}

void SaveContact(contact* con) {
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL) {
		perror("fopen error!\n");
		return;
	}
	//将通讯录数据写⼊⽂件
	for (int i = 0; i < con->size; i++)
	{
		fwrite(con->a + i, sizeof(PeoInfo), 1, pf);
	}
	printf("通讯录数据保存成功!\n");
}

void DestroyContact(contact* con) {
	SaveContact(con);
	SeqListDesTroy(con);
}

//test.c

#include"SeqList.h"
#include"contact.h"

void menu() {
	//通讯录初始化
	contact con;
	InitContact(&con);
	int op = -1;
	do {
		printf("********************************\n");
		printf("*****1、添加⽤⼾ 2、删除⽤⼾*****\n");
		printf("*****3、查找⽤⼾ 4、修改⽤⼾*****\n");
		printf("*****5、展⽰⽤⼾ 0、退出 *****\n");
		printf("********************************\n");
		printf("请选择您的操作:\n");
		scanf("%d", &op);
		switch (op)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			FindContact(&con);
			break;
		case 4:
			ModifyContact(&con);
			break;
		case 5:
			ShowContact(&con);
			break;
		default:
			printf("输⼊有误,请重新输⼊\n");
			break;
		}
	} while (op != 0);
	//销毁通讯录
	DestroyContact(&con);
}

二、顺序表经典算法

经典算法OJ题1: 移除元素
经典算法OJ题2: 合并两个有序数组

三、顺序表的问题及思考

  1. 中间/头部的插⼊删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容⼀般是呈2倍的增长,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到
    200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
    思考:如何解决以上问题呢?
相关推荐
就爱学编程27 分钟前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战项目二)
数据结构·c++·算法
DARLING Zero two♡2 小时前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode
波音彬要多做3 小时前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
Noah_aa4 小时前
代码随想录算法训练营第五十六天 | 图 | 拓扑排序(BFS)
数据结构
KpLn_HJL4 小时前
leetcode - 2139. Minimum Moves to Reach Target Score
java·数据结构·leetcode
AC使者10 小时前
5820 丰富的周日生活
数据结构·算法
无 证明11 小时前
new 分配空间;引用
数据结构·c++
别NULL15 小时前
机试题——疯长的草
数据结构·c++·算法