C语言数据结构——顺序表
(图片由AI生成)
0.前言
在程序设计的世界里,数据结构是非常重要的基础概念。本文将专注于C语言中的一种基本数据结构——顺序表。我们将从数据结构的基本概念讲起,逐步深入到顺序表的内部结构、分类,最后通过一个实战项目来具体展示顺序表的应用。
1.什么是数据结构
数据结构是计算机科学中的一个重要概念,它是指计算机中存储、组织数据的方式。数据结构关注的不仅仅是数据的存储,还包括数据之间的关系以及如何高效地访问和修改这些数据。数据结构的选择和设计对于软件开发的效率、性能和可维护性有着决定性的影响。
1.1基本概念
- 数据:数据是信息的载体,是可以输入到计算机中并被计算机程序处理的符号的集合。
- 数据元素:是组成数据的、有意义的最小单位。在计算机程序中,通常被视为一个整体进行考虑和处理。
- 数据项:一个数据元素可以由多个数据项组成。数据项是数据不可分割的最小单位。
1.2数据结构的分类
数据结构通常分为两类:逻辑结构和物理结构。
-
逻辑结构:它关注数据对象中数据元素之间的关系。逻辑结构分为线性结构、树结构、图结构等。
- 线性结构:数据元素之间是一对一的关系。例如,数组、链表。
- 树结构:数据元素之间存在一对多的层次关系。例如,二叉树。
- 图结构:数据元素之间是多对多的关系。例如,网络、有向图。
-
物理结构(存储结构):它关注数据的存储方式。物理结构主要包括顺序存储结构和链式存储结构。
- 顺序存储结构:数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的。例如,数组。
- 链式存储结构:数据元素存放在任意的存储单元里,这些存储单元可以连续,也可以不连续。
1.3数据结构的重要性
数据结构对于计算机程序的设计至关重要。它不仅决定了程序的内存使用效率,还直接影响到程序中各种操作的执行效率。比如,在一个排序好的二叉搜索树中查找一个元素通常比在链表中快得多。因此,选择合适的数据结构可以极大地提高程序的效率和性能。
2.线性表
线性表是数据结构中最简单和最常用的一种结构,它是一组具有相同数据类型的数据元素的有限序列。
2.1基本特征
- 有序性:线性表中的元素是有序排列的,每个元素都有一个确定的位置。
- 元素个数的有限性:虽然线性表的长度是可变的,但任一时刻表中元素的个数是有限的。
- 单一的数据类型:表中的所有元素必须是同一类型或满足同一类型约束。
2.2基本操作
线性表支持多种操作,包括但不限于:
- 初始化:创建一个空的线性表。
- 插入:在指定位置插入一个新元素。
- 删除:删除指定位置的元素。
- 查找:按位置或条件检索元素。
- 遍历:遍历表中的所有元素。
- 清空:清除表中的所有元素。
2.3存储方式
线性表可以用两种方式存储:
- 顺序存储:使用连续的存储单元一次性存储线性表的元素。这种方式的优点是可以快速地访问表中任一位置的元素,但插入和删除操作需要移动大量元素,效率较低。
- 链式存储:使用一组任意的存储单元存放线性表的元素。每个元素节点除了存储数据外,还需要存储指向下一个元素的指针,这样形成了一个链。链式存储的优点是插入和删除操作方便,但访问特定位置的元素时效率较低。
2.4应用场景
线性表广泛应用于程序设计中,例如:
- 数据收集:如字符串的处理。
- 数据缓存:临时存储数据。
- 辅助结构:在更复杂的数据结构(如栈和队列)的实现中。
3.顺序表的概念
顺序表是线性表的一种存储方式,它通过一段连续的存储单元依次存放线性表的数据元素。在顺序表中,每个数据元素的位置关系是由它们的存储顺序决定的。这种数据结构在C语言等编程语言中通常使用数组来实现。
3.1顺序表的特点
- 随机访问:由于顺序表使用连续的存储空间,可以通过索引直接访问任意位置的元素,具有很高的访问效率。
- 固定长度:在静态顺序表中,表的大小在定义时就已确定,不能动态改变。
- 空间利用率:顺序表可能存在空间浪费的问题,特别是当表中元素数量远小于分配的存储空间时。
- 插入和删除效率低:插入或删除元素时可能需要移动大量元素以保持元素的连续性,这导致操作效率相对较低。
4.顺序表的分类
在C语言中,顺序表通常有以下两种实现方式:
4.1静态顺序表
静态顺序表:使用固定大小的数组来存储元素。例如:
#define MAX_SIZE 100
typedef struct {int data[MAX_SIZE];int length;
} StaticList;
在这种结构中,MAX_SIZE 定义了顺序表的最大容量,data 数组用于存储表中的元素,length 表示顺序表的当前长度。
4.2动态顺序表
动态顺序表:使用动态分配的数组来存储元素。这种方式更加灵活,可以在运行时根据需要调整表的大小。例如:
typedef struct {int *data;int length;int capacity;
} DynamicList;
在这种结构中,data 指向一个动态分配的数组,length 表示表的当前长度,capacity 表示分配的数组的容量。
5.动态顺序表的实现
5.1完整代码
文件组成
-
SeqList.h:这个头文件定义了动态顺序表的结构体
SeqList,其中包括指向动态数组的指针、数组的当前大小和容量。它还声明了各种操作动态顺序表所需的函数。 -
SeqList.c:这个源文件包含了头文件中声明的所有函数的具体实现。
-
test.c:这个源文件包含main函数,为测试文件,用来测试函数接口的准确性。
//SeqList.h#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>typedef int SLDataType;
//动态顺序表
typedef struct SeqList
{SLDataType* arr;//动态开辟的数组size_t size;//有效元素个数size_t capacity;//容量
}SL;//初始化
void SLInit(SL* ps);
//销毁
void SLDestory(SL* ps);
//判断容量
void SLCheckCapacity(SL* ps);
//打印
void SLPrint(SL* ps);
//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);
//查找
int SLFind(SL* ps, SLDataType x);
//在pos位置插入x
void SLInsert(SL* ps, size_t pos, SLDataType x);
//删除pos位置的元素
void SLErase(SL* ps, size_t pos);//SeqList.c#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"//初始化
void SLInit(SL* ps)
{assert(ps);ps->arr = (SLDataType*)malloc(sizeof(SLDataType)* 3);if (ps->arr == NULL){assert(0);return;}ps->size = 0;ps->capacity = 3;
}//销毁
void SLDestory(SL* ps)
{assert(ps);free(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}//检查容量
void SLCheckCapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->arr, sizeof(SLDataType)*ps->capacity * 2);if (tmp == NULL){perror("realloc fail\n");return;}ps->arr = tmp;ps->capacity *= 2;}
}
//打印
void SLPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}printf("\n");
}
//尾插
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);ps->arr[ps->size] = x;ps->size++;
}//尾删
void SLPopBack(SL* ps)
{assert(ps);if (ps->size == 0){return;}ps->size--;
}//头插
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--){ps->arr[i + 1] = ps->arr[i];}ps->arr[0] = x;ps->size++;
}//头删
void SLPopFront(SL* ps)
{assert(ps);if (ps->size == 0){return;}for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}//查找
int SLFind(SL* ps, SLDataType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (ps->arr[i] == x){return i;}}return -1;//没找到,返回-1
}
//插入
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size - 1; i >= pos; i--){ps->arr[i + 1] = ps->arr[i];}ps->arr[pos] = x;ps->size++;
}
//删除
void SLErase(SL* ps, size_t pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);if (ps->size == 0){return;}for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;}//test.c#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"void SLTest01()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushFront(&sl, 4);SLPushFront(&sl, 5);SLPrint(&sl);SLPopFront(&sl);SLPrint(&sl);}
int main()
{SLTest01();return 0;
}
5.2头文件解析
5.2.1. 数据类型定义(SLDataType)
typedef int SLDataType;
这行代码定义了顺序表中元素的数据类型。在这里,SLDataType被定义为int类型。这意味着在这个顺序表实现中,每个元素都是一个整数。使用typedef为数据类型创建一个别名(在这里是SLDataType)使得代码更具可读性和灵活性。如果将来需要改变顺序表中存储的数据类型,只需修改这个typedef声明即可。
5.2.2. 动态顺序表的结构定义(SeqList)
typedef struct SeqList {SLDataType* arr; // 动态开辟的数组size_t size; // 有效元素个数size_t capacity; // 容量
} SL;
这部分定义了动态顺序表的结构体,用于存储顺序表的数据和相关信息。
-
SLDataType* arr;:这是一个指向SLDataType类型的指针,它指向顺序表的实际数据存储区域。在这个顺序表实现中,这个区域是动态分配的。这意味着顺序表的大小可以在运行时改变,而不是在编译时固定。 -
size_t size;:这个成员变量存储顺序表中当前存储的元素数量。它不仅用于追踪顺序表中已经使用的元素数量,还用于确定在哪里插入新元素或从哪里删除元素。 -
size_t capacity;:这个成员变量表示顺序表分配的总容量,即arr可以存储的最大元素数量。这个值通常大于或等于size。当size达到capacity时,需要扩展arr来容纳更多元素。
5.3函数剖析
下面,我们对SeqList.c中的各个函数接口逐一剖析,以加强对顺序表实现的理解。
1. 初始化(SLInit)
void SLInit(SL* ps) {assert(ps);ps->arr = (SLDataType*)malloc(sizeof(SLDataType) * 3);if (ps->arr == NULL) {assert(0);return;}ps->size = 0;ps->capacity = 3;
}
- 功能:初始化顺序表,分配初始容量并设置初始大小。
- 实现:首先通过断言确保指针
ps非空。然后,使用malloc为数组分配初始容量(这里是3个元素的空间)。如果内存分配失败,则触发断言。最后,设置顺序表的大小为0,容量为3。
2. 销毁(SLDestory)
void SLDestory(SL* ps) {assert(ps);free(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}
- 功能:销毁顺序表,释放其动态分配的内存并重置结构。
- 实现:使用断言确保
ps非空,然后释放动态数组arr所占用的内存,并将其指针设置为NULL。最后,将顺序表的大小和容量都重置为0。
3. 检查容量(SLCheckCapacity)
void SLCheckCapacity(SL* ps) {assert(ps);if (ps->size == ps->capacity) {SLDataType* tmp = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * ps->capacity * 2);if (tmp == NULL) {perror("realloc fail\n");return;}ps->arr = tmp;ps->capacity *= 2;}
}
- 功能:如果顺序表的大小达到其容量,则将容量加倍。
- 实现:首先检查
ps非空。如果顺序表已满(size等于capacity),则使用realloc将数组空间加倍。如果重新分配失败,则打印错误信息并返回。如果成功,则更新数组指针和容量值。
4. 打印(SLPrint)
void SLPrint(SL* ps) {assert(ps);for (int i = 0; i < ps->size; i++) {printf("%d ", ps->arr[i]);}printf("\n");
}
- 功能:打印顺序表中的所有元素。
- 实现:断言确保
ps非空,然后遍历顺序表,打印每个元素。
5. 尾插(SLPushBack)
void SLPushBack(SL* ps, SLDataType x) {assert(ps);SLCheckCapacity(ps);ps->arr[ps->size] = x;ps->size++;
}
- 功能:在顺序表的尾部插入一个新元素。
- 实现:断言确保
ps非空,调用SLCheckCapacity以确保有足够的容量。然后在数组的size位置插入新元素x,并将size增加1。
6. 尾删(SLPopBack)
void SLPopBack(SL* ps) {assert(ps);if (ps->size == 0) {return;}ps->size--;
}
- 功能:删除顺序表尾部的元素。
- 实现:断言确保
ps非空,然后检查顺序表是否为空。如果不为空,则将size减1来移除最后一个元素。
7. 头插(SLPushFront)
void SLPushFront(SL* ps, SLDataType x) {assert(ps);SLCheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--) {ps->arr[i + 1] = ps->arr[i];}ps->arr[0] = x;ps->size++;
}
- 功能:在顺序表的头部插入一个新元素。
- 实现:断言确保
ps非空,检查容量。然后将所有元素向后移动一个位置,为新元素腾出空间。最后在数组的第一个位置插入新元素,并将size增加1。
8. 头删(SLPopFront)
void SLPopFront(SL* ps) {assert(ps);if (ps->size == 0) {return;}for (int i = 0; i < ps->size - 1; i++) {ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
- 功能:删除顺序表头部的元素。
- 实现:断言确保
ps非空,检查顺序表是否为空。如果不为空,则将所有元素向前移动一个位置,并将size减1。
9. 查找(SLFind)
int SLFind(SL* ps, SLDataType x) {assert(ps);for (int i = 0; i < ps->size; i++) {if (ps->arr[i] == x) {return i;}}return -1; // 没找到,返回-1
}
- 功能:查找顺序表中是否有指定的元素,并返回其位置。
- 实现:断言确保
ps非空,然后遍历顺序表。如果找到与x相等的元素,则返回其位置;如果遍历完仍未找到,则返回-1。
10. 插入(SLInsert)
void SLInsert(SL* ps, size_t pos, SLDataType x) {assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size - 1; i >= pos; i--) {ps->arr[i + 1] = ps->arr[i];}ps->arr[pos] = x;ps->size++;
}
- 功能:在顺序表的指定位置插入一个新元素。
- 实现:断言确保
ps非空,并且插入位置pos有效(即在0和size之间)。检查并调整容量。然后从尾部开始,将每个元素向后移动一个位置,为新元素腾出空间。最后在指定位置插入新元素,并将size增加1。
11. 删除(SLErase)
void SLErase(SL* ps, size_t pos) {assert(ps);assert(pos >= 0 && pos < ps->size);for (size_t i = pos; i < ps->size - 1; i++) {ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
- 功能:删除顺序表中指定位置的元素。
- 实现:断言确保
ps非空,并且删除位置pos有效。然后从pos位置开始,将每个元素向前移动一个位置,覆盖掉要删除的元素。最后将size减1。
6.项目实战:基于动态顺序表的通讯录项目
6.1项目结构
Contact.h: 定义了通讯录的数据结构和操作函数原型。SeqList.h: 定义了动态顺序表的数据结构和操作函数原型,扩展自Contact.h。Contact.c: 实现了Contact.h中声明的通讯录操作函数。SeqList.c: 实现了SeqList.h中声明的动态顺序表操作函数。test.c: 主函数,用于运行通讯录应用。
6.2核心概念
- 动态顺序表 (
SeqList): 用于存储通讯录中的联系人信息。动态顺序表提供了灵活的数据存储和访问方式。 - 联系人信息 (
PersonInfo): 定义了通讯录中每个联系人的详细信息,包括姓名、年龄、性别、电话和地址。
6.3功能实现
- 初始化和销毁: 通讯录在使用前进行初始化,在使用后进行资源清理和销毁。
- 增加联系人 (
ContactAdd): 允许用户输入新的联系人信息,并将其添加到通讯录中。 - 删除联系人 (
ContactDel): 根据用户输入的姓名,查找并删除相应的联系人。 - 查找联系人 (
ContactFind): 根据用户输入的姓名,查找并显示联系人的详细信息。 - 修改联系人 (
ContactModify): 允许用户根据姓名查找联系人并修改其详细信息。 - 显示通讯录 (
ContactShow): 显示通讯录中所有联系人的详细信息。 - 菜单界面 (
menu): 提供一个简单的文本菜单,让用户选择不同的操作。
6.4运行流程
- 用户通过主函数 (
main) 运行程序。 - 程序显示菜单,并根据用户输入执行相应的操作,如添加、删除、查找、修改或显示联系人。
- 用户选择退出时,程序销毁通讯录并结束运行。
6.5完整代码
//Contact.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>#define NAME_MAX 100
#define GENDER_MAX 10
#define TEL_MAX 12
#define ADDR_MAX 100typedef struct PersonInfo
{char name[NAME_MAX];int age;char gender[GENDER_MAX];char tel[TEL_MAX];char addr[ADDR_MAX];
}Info;//前置声明
typedef struct SeqList Contact;//菜单栏
void menu();
//初始化
void ContactInit(Contact* pcon);
//销毁
void ContactDestroy(Contact* pcon);
//增加
void ContactAdd(Contact* pcon);
//删除
void ContactDel(Contact* pcon);
//查找
void ContactFind(Contact* pcon);
int FindByName(Contact* pcon, char name[NAME_MAX]);
//修改
void ContactModify(Contact* pcon);
//展示
void ContactShow(Contact* pcon);//SeqList.h
#pragma once
#include"Contact.h"typedef struct PersonInfo SLDataType;
//动态顺序表
typedef struct SeqList
{SLDataType* arr;//动态开辟的数组size_t size;//有效元素个数size_t capacity;//容量
}SL,Contact;//初始化
void SLInit(SL* ps);
//销毁
void SLDestory(SL* ps);
//判断容量
void SLCheckCapacity(SL* ps);
//打印
void SLPrint(SL* ps);
//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);
//查找
int SLFind(SL* ps, SLDataType x);
//在pos位置插入x
void SLInsert(SL* ps, size_t pos, SLDataType x);
//删除pos位置的元素
void SLErase(SL* ps, size_t pos);//Contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Contact.h"
#include"SeqList.h"
void menu()
{printf("*****************通讯录***************\n");printf("***** 1.添加联系人 2.删除联系人*****\n");printf("***** 3.查找联系人 4.修改联系人*****\n");printf("***** 5.查看联系人 0.退出 *****\n");printf("**************************************\n");
}
//初始化
void ContactInit(Contact* pcon)
{SLInit(pcon);
}
//销毁
void ContactDestroy(Contact* pcon)
{SLDestory(pcon);
}
//增加
void ContactAdd(Contact* pcon)
{//创建联系人结构体变量Info info;//初始化printf("请输入姓名:\n");scanf("%s", info.name);printf("请输入年龄:\n");scanf("%d", &info.age);printf("请输入性别:\n");scanf("%s", info.gender);printf("请输入电话:\n");scanf("%s", info.tel);printf("请输入地址:\n");scanf("%s", info.addr);//将结构体变量尾插到顺序表中SLPushBack(pcon, info);
}
//删除
void ContactDel(Contact* pcon)
{printf("请输入要删除的姓名:\n");char name[NAME_MAX] = { 0 };scanf("%s", name);//1.查找要删除的元素下标int pos = FindByName(pcon, name);if (pos == -1){printf("要删除的联系人不存在\n");return;}//2.删除元素SLErase(pcon, pos);printf("删除成功\n");
}
//查找
int FindByName(Contact*pcon, char name[NAME_MAX])
{//遍历顺序表,查找姓名为name的联系人int i = 0;for (i = 0; i < pcon->size; i++){if (strcmp(pcon->arr[i].name, name) == 0){return i;}}return -1;
}
void ContactFind(Contact* pcon)
{//按照姓名查找//1.输入要查找的姓名char name[NAME_MAX] = { 0 };printf("请输入要查找的姓名:\n");scanf("%s", name);//2.查找姓名为name的联系人int pos = FindByName(pcon, name);if (pos == -1){printf("要查找的联系人不存在\n");return;}//3.打印联系人信息printf("%-10s\t%-4s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");printf(" %-10s\t%-4d\t%-5s\t%-12s\t%-20s\n",pcon->arr[pos].name,pcon->arr[pos].age,pcon->arr[pos].gender,pcon->arr[pos].tel,pcon->arr[pos].addr);}
//修改
void ContactModify(Contact* pcon)
{char name[NAME_MAX] = { 0 };printf("请输入要修改的姓名:\n");scanf("%s", name);//1.查找要修改的元素下标int pos = FindByName(pcon, name);if (pos == -1){printf("要修改的联系人不存在\n");return;}//2.修改元素Info* pInfo = &pcon->arr[pos];printf("请输入姓名:\n");scanf("%s", pInfo->name);printf("请输入年龄:\n");scanf("%d", &pInfo->age);printf("请输入性别:\n");scanf("%s", pInfo->gender);printf("请输入电话:\n");scanf("%s", pInfo->tel);printf("请输入地址:\n");scanf("%s", pInfo->addr);printf("修改成功\n");
}
//展示
void ContactShow(Contact* pcon)
{//打印表头printf("%-10s\t%-4s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");//遍历顺序表中的每个元素,打印每个元素的值for (int i = 0; i < pcon->size; i++){printf(" %-10s\t%-4d\t%-5s\t%-12s\t%-20s\n", pcon->arr[i].name,pcon->arr[i].age,pcon->arr[i].gender,pcon->arr[i].tel,pcon->arr[i].addr);}
}//SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//初始化
void SLInit(SL* ps)
{assert(ps);ps->arr = (SLDataType*)malloc(sizeof(SLDataType)* 3);if (ps->arr == NULL){assert(0);return;}ps->size = 0;ps->capacity = 3;
}
//销毁
void SLDestory(SL* ps)
{assert(ps);free(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}
//检查容量
void SLCheckCapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->arr, sizeof(SLDataType)*ps->capacity * 2);if (tmp == NULL){perror("realloc fail\n");return;}ps->arr = tmp;ps->capacity *= 2;}
}
//尾插
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);ps->arr[ps->size] = x;ps->size++;
}//尾删
void SLPopBack(SL* ps)
{assert(ps);if (ps->size == 0){return;}ps->size--;
}//头插
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--){ps->arr[i + 1] = ps->arr[i];}ps->arr[0] = x;ps->size++;
}//头删
void SLPopFront(SL* ps)
{assert(ps);if (ps->size == 0){return;}for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}//插入
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size - 1; i >= pos; i--){ps->arr[i + 1] = ps->arr[i];}ps->arr[pos] = x;ps->size++;
}
//删除
void SLErase(SL* ps, size_t pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);if (ps->size == 0){return;}for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;}//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//#include"Contact.h"int main()
{Contact con;ContactInit(&con);int op = -1;do {menu();printf("请选择:>");scanf("%d", &op);switch (op){case 1:printf("添加联系人\n");ContactAdd(&con);break;case 2:printf("删除联系人\n");ContactDel(&con);break;case 3:printf("查找联系人\n");ContactFind(&con);break;case 4:printf("修改联系人\n");ContactModify(&con);break;case 5:printf("查看通讯录\n");ContactShow(&con);break;case 0:printf("退出\n");break;default:printf("选择错误\n");}} while (op);ContactDestroy(&con);return 0;
}
7.结语
在本文中,我们全面探讨了C语言中顺序表的理论与应用,尤其是通过动态顺序表实现的通讯录项目,深入理解了其实际应用价值。这不仅加强了我们对数据结构基本概念的理解,还展示了如何将理论知识应用于实际问题解决中。顺序表的学习是编程之路上的重要一步,为我们未来探索更复杂的数据结构和算法奠定了坚实的基础。最后,希望本文对您有所帮助,无论您是一名初学者还是希望巩固基础知识的程序员。在编程的世界里,永远有新知识等待我们去探索和学习。
相关文章:
C语言数据结构——顺序表
(图片由AI生成) 0.前言 在程序设计的世界里,数据结构是非常重要的基础概念。本文将专注于C语言中的一种基本数据结构——顺序表。我们将从数据结构的基本概念讲起,逐步深入到顺序表的内部结构、分类,最后通过一个实…...
网络安全:守护数字世界的盾牌
在当今数字化的时代,网络已经渗透到我们生活的方方面面。从社交媒体到在线银行,从在线购物到工作文件传输,网络几乎无处不在。然而,随着网络的普及,网络安全问题也日益凸显。那么,如何确保我们的数字资产安…...
vue3hooks的使用
hook是钩子的意思,看到“钩子”是不是就想到了钩子函数?事实上,hooks 还真是函数的一种写法。 vue3 借鉴 react hooks 开发出了 Composition API ,所以也就意味着 Composition API 也能进行自定义封装 hooks。 vue3 中的 hooks …...
elementUI+el-upload 上传、下载、删除文件以及文件展示列表自定义为表格展示
Upload 上传组件的使用 官方文档链接使用el-upload组件上传文件 具体参数说明,如何实现上传、下载、删除等功能获取文件列表进行file-list格式匹配代码 文件展示列表自定义为表格展示 使用的具体参数说明文件大小展示问题(KB/MB)文件下载代码…...
供应链安全项目in-toto开源框架详解
引言:in-toto 是一个开源框架,能够以密码学的方式验证构件生产路径上的每个组件和步骤。它可与主流的构建工具、部署工具进行集成。in-toto已经被CNCF技术监督委员会 (Technical Oversight Committee,TOC)接纳为CNCF孵化项目。 1. 背景 由于…...
自己是如何使用单元测试
前言 自己是如何使用单元测试 进行单元测试能够让我们在编写方法的具体实现代码后,能清晰地看到其是否能实现预期的功能,有助于我们及时修正自己方法中存在的bug,以免在后续使用到某方法时出现意想不到的错误。 一、引入单元测试所使用的依赖…...
第二百七十八回
文章目录 1. 概念介绍2. 使用方法2.1 DropdownMenu2.1 DropdownMenuEntry 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何禁止页面跟随手机自动旋转"相关的内容,本章回中将介绍DropdownMenu组件.闲话休提,让我们一起Talk Flutter吧。 1.…...
Java 内存模型深度解析
优质博文:IT-BLOG-CN 一、并发编程模型的两个关键问题 【1】并发中常见的两个问题:线程之间如何通信及线程之间如何同步。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:内存共享和消息传递&…...
python爬取图片(thumbURL和html文件标签分别爬取)
当查看源代码,发现网址在thumbURL之后时,用此代码: # 当查看源代码,发现网址在thumbURL之后时,用此代码:import requestsheaders {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121…...
MySQL、Oracle 常用SQL:建表、建视图、数据增删改查、常用condition
目录 1 MySQL、Oracle 建表语句整理1.1 MySQL 建表1.2 Oracle 建表1.3 补充1.3.1 主键:新增、删除1.3.2 字段:新增、修改、删除 2 MySQL、Oracle 建视图3 数据:增删改查3.1 插入数据3.1.1 MySQL、Oracle 插入一条数据3.1.2 MySQL、Oracle 插入…...
Docker(八)高级网络配置
作者主页: 正函数的个人主页 文章收录专栏: Docker 欢迎大家点赞 👍 收藏 ⭐ 加关注哦! 高级网络配置 注意:本章属于 Docker 高级配置,如果您是初学者,您可以暂时跳过本章节,直接学习…...
VUE--- ref refs
ref & refs 的作用:用于获取dom元素或组件实例,也可用于组件组件间数据的获取和修改 ref & refs 与querySelector的区别: ● ref & refs 查找的范围是当前组件内,更加精确稳定 ● querySelector 查找的范围是整个页面…...
微信小程序之WXML 模板语法之数据绑定、事件绑定、wx:if和列表渲染
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需…...
maven导入无法拉取所需依赖
maven导入无法拉取所需依赖 1.原因2.解决搞定收工! 1.原因 公司使用的是gradle,配置的私有云,maven里面配置私有云完全使用不了,无论配置国内还是国外的,导入的项目报错拉不到jar包。 <mirror><id>mirro…...
【2023-08-20】字节跳动秋招笔试四道编程题解
恭喜发现宝藏!搜索公众号【TechGuide】回复公司名,解锁更多新鲜好文和互联网大厂的笔经面经。 作者@TechGuide【全网同名】 订阅专栏【进阶版】2023最新大厂笔试真题 & 题解,不容错过的宝藏资源! 第一题:最小交换次数 题目描述 小盖将n个珠子排成一排,然后将它们串…...
VPS网站发布-个人网站搭建与部署-个人简历网站示例-个人简历网站案例-网站推广
文章目录 1. 个人网站搭建指南1.1 网站示例 | 个人网站 | 个人简历模版 | 个人简历网站 | 网站案例1.2 准备工具 2. 网页部署教程(ubuntu)2.1 购买域名2.2 购买VPS2.3 部署工具 Apache || Nginx2.1.1 网页相关文件上传到github库2.1.2 在VPS中执行一键部…...
INTEWORK—PET 汽车软件持续集成平台
产品概述 INTEWORK-PET-CI是经纬恒润自主研发的汽车软件持续集成&持续交付平台,在传统的持续集成基础上深化了研运一体化(DevOps)的概念,将嵌入式软件中的拉取代码、检查、构建、测试、版本管理以及发布交付等环节串联起来&am…...
【Git】 取消上一次commit或push
一、取消上一次commit 如果你需要取消上一次的 Git 提交,有几个不同的方法可以实现。其中包括撤消提交、提交到新的分支、使用 Git 回滚等等。 下面介绍三种方法: 方法1:使用 Git reset 使用 Git reset 命令来取消上一次提交: …...
回归预测 | Matlab基于OOA-SVR鱼鹰算法优化支持向量机的数据多输入单输出回归预测
回归预测 | Matlab基于OOA-SVR鱼鹰算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于OOA-SVR鱼鹰算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于OOA-SVR鱼鹰算法优化支持向量机的数据…...
Spring Boot整合MyBatis
引言 在现代Java开发中,Spring Boot和MyBatis被广泛使用,它们分别代表了轻量级的企业级开发框架和优秀的持久化框架。本文将探讨如何在Spring Boot项目中整合MyBatis,以构建高效、灵活且易于维护的持久层。通过这一完美结合,开发…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
Monorepo架构: Nx Cloud 扩展能力与缓存加速
借助 Nx Cloud 实现项目协同与加速构建 1 ) 缓存工作原理分析 在了解了本地缓存和远程缓存之后,我们来探究缓存是如何工作的。以计算文件的哈希串为例,若后续运行任务时文件哈希串未变,系统会直接使用对应的输出和制品文件。 2 …...
32位寻址与64位寻址
32位寻址与64位寻址 32位寻址是什么? 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元(地址),其核心含义与能力如下: 1. 核心定义 地址位宽:CPU或内存控制器用32位…...
