C语言数据结构——顺序表

(图片由AI生成) 

0.前言

在程序设计的世界里,数据结构是非常重要的基础概念。本文将专注于C语言中的一种基本数据结构——顺序表。我们将从数据结构的基本概念讲起,逐步深入到顺序表的内部结构、分类,最后通过一个实战项目来具体展示顺序表的应用。

1.什么是数据结构

数据结构是计算机科学中的一个重要概念,它是指计算机中存储、组织数据的方式。数据结构关注的不仅仅是数据的存储,还包括数据之间的关系以及如何高效地访问和修改这些数据。数据结构的选择和设计对于软件开发的效率、性能和可维护性有着决定性的影响。

1.1基本概念

  • 数据:数据是信息的载体,是可以输入到计算机中并被计算机程序处理的符号的集合。
  • 数据元素:是组成数据的、有意义的最小单位。在计算机程序中,通常被视为一个整体进行考虑和处理。
  • 数据项:一个数据元素可以由多个数据项组成。数据项是数据不可分割的最小单位。

1.2数据结构的分类

数据结构通常分为两类:逻辑结构物理结构

  1. 逻辑结构:它关注数据对象中数据元素之间的关系。逻辑结构分为线性结构、树结构、图结构等。

    • 线性结构:数据元素之间是一对一的关系。例如,数组、链表。
    • 树结构:数据元素之间存在一对多的层次关系。例如,二叉树。
    • 图结构:数据元素之间是多对多的关系。例如,网络、有向图。
  2. 物理结构(存储结构):它关注数据的存储方式。物理结构主要包括顺序存储结构和链式存储结构。

    • 顺序存储结构:数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的。例如,数组。
    • 链式存储结构:数据元素存放在任意的存储单元里,这些存储单元可以连续,也可以不连续。

1.3数据结构的重要性

数据结构对于计算机程序的设计至关重要。它不仅决定了程序的内存使用效率,还直接影响到程序中各种操作的执行效率。比如,在一个排序好的二叉搜索树中查找一个元素通常比在链表中快得多。因此,选择合适的数据结构可以极大地提高程序的效率和性能。

2.线性表

线性表是数据结构中最简单和最常用的一种结构,它是一组具有相同数据类型的数据元素的有限序列。

2.1基本特征

  • 有序性:线性表中的元素是有序排列的,每个元素都有一个确定的位置。
  • 元素个数的有限性:虽然线性表的长度是可变的,但任一时刻表中元素的个数是有限的。
  • 单一的数据类型:表中的所有元素必须是同一类型或满足同一类型约束。

2.2基本操作

线性表支持多种操作,包括但不限于:

  • 初始化:创建一个空的线性表。
  • 插入:在指定位置插入一个新元素。
  • 删除:删除指定位置的元素。
  • 查找:按位置或条件检索元素。
  • 遍历:遍历表中的所有元素。
  • 清空:清除表中的所有元素。

2.3存储方式

线性表可以用两种方式存储:

  1. 顺序存储:使用连续的存储单元一次性存储线性表的元素。这种方式的优点是可以快速地访问表中任一位置的元素,但插入和删除操作需要移动大量元素,效率较低。
  2. 链式存储:使用一组任意的存储单元存放线性表的元素。每个元素节点除了存储数据外,还需要存储指向下一个元素的指针,这样形成了一个链。链式存储的优点是插入和删除操作方便,但访问特定位置的元素时效率较低。

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完整代码

文件组成

  1. SeqList.h:这个头文件定义了动态顺序表的结构体SeqList,其中包括指向动态数组的指针、数组的当前大小和容量。它还声明了各种操作动态顺序表所需的函数。

  2. SeqList.c:这个源文件包含了头文件中声明的所有函数的具体实现。

  3. 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项目结构

  1. Contact.h: 定义了通讯录的数据结构和操作函数原型。
  2. SeqList.h: 定义了动态顺序表的数据结构和操作函数原型,扩展自Contact.h
  3. Contact.c: 实现了Contact.h中声明的通讯录操作函数。
  4. SeqList.c: 实现了SeqList.h中声明的动态顺序表操作函数。
  5. test.c: 主函数,用于运行通讯录应用。

6.2核心概念

  • 动态顺序表 (SeqList): 用于存储通讯录中的联系人信息。动态顺序表提供了灵活的数据存储和访问方式。
  • 联系人信息 (PersonInfo): 定义了通讯录中每个联系人的详细信息,包括姓名、年龄、性别、电话和地址。

6.3功能实现

  1. 初始化和销毁: 通讯录在使用前进行初始化,在使用后进行资源清理和销毁。
  2. 增加联系人 (ContactAdd): 允许用户输入新的联系人信息,并将其添加到通讯录中。
  3. 删除联系人 (ContactDel): 根据用户输入的姓名,查找并删除相应的联系人。
  4. 查找联系人 (ContactFind): 根据用户输入的姓名,查找并显示联系人的详细信息。
  5. 修改联系人 (ContactModify): 允许用户根据姓名查找联系人并修改其详细信息。
  6. 显示通讯录 (ContactShow): 显示通讯录中所有联系人的详细信息。
  7. 菜单界面 (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 100

typedef 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语言中顺序表的理论与应用,尤其是通过动态顺序表实现的通讯录项目,深入理解了其实际应用价值。这不仅加强了我们对数据结构基本概念的理解,还展示了如何将理论知识应用于实际问题解决中。顺序表的学习是编程之路上的重要一步,为我们未来探索更复杂的数据结构和算法奠定了坚实的基础。最后,希望本文对您有所帮助,无论您是一名初学者还是希望巩固基础知识的程序员。在编程的世界里,永远有新知识等待我们去探索和学习。

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

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

相关文章

Unity常用的优化技巧集锦

Unity性能优化是面试的时候经常被问道的一些内容&#xff0c;今天给大家分享一些常用的Unity的优化技巧和思路&#xff0c;方便大家遇到问题时候参考与学习。 对啦&#xff01;这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白&#xff0c;也有一些正在从事游…

电脑pdf如何转换成word格式?用它实现pdf文件一键转换

pdf转word格式可以用于提取和重用pdf文档中的内容&#xff0c;有时候&#xff0c;我们可能需要引用或引用pdf文档中的一些段落、表格或数据&#xff0c;通过将pdf转换为可编辑的Word文档&#xff0c;可以轻松地复制和粘贴所需内容&#xff0c;节省我们的时间&#xff0c;那么如…

【守护工地安全】YOLOv8实现安全帽检测

学习《OpenCV应用开发&#xff1a;入门、进阶与工程化实践》一书 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; 数据集 该图像数据集包含8000张图像&#xff0c;两个类别分别是安全帽与人、以其中200多张图像为验证集&#xff0c;其余为训…

谁说知识库都是英文的 今天就来一个中文版的

1.安装 1.1创建目录 mkdir -p /opt/trilium-cn cd /opt/trilium-cn 1.2.编写docker-compose.yml文件 version: 3 services:trilium-cn:image: nriver/trilium-cnrestart: alwaysports:- "10012:8080"volumes:# 把同文件夹下的 trilium-data 目录映射到容器内- /opt…

基于JavaWeb+SSM+Vue基于微信小程序生鲜云订单零售系统的设计和实现

基于JavaWebSSMVue基于微信小程序生鲜云订单零售系统的设计和实现 滑到文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 滑到文末获取源码 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计…

6.4.4释放音频

6.4.4释放音频 许多Flash动画里的音乐或歌曲非常好听&#xff0c;能不能在没有源文件的情况下把里面的声音文件取出来呢&#xff1f;利用Swf2VideoConverter2可以轻松做到这一点。 1&#xff0e;单击“添加”按钮&#xff0c;在弹出的下拉菜单中选择“添加文件”&#xff0c;…

java小项目:简单的收入明细记事本,超级简单(不涉及数据库,通过字符串来记录)

一、效果 二、代码 2.1 Acount类 package com.demo1;public class Acount {public static void main(String[] args) {String details "收支\t账户金额\t收支金额\t说 明\n"; //通过字符串来记录收入明细int balance 10000;boolean loopFlag true;//控制循…

iOS中的视频播放

引言 在数字时代&#xff0c;视频已成为我们日常沟通和娱乐不可或缺的一部分。从简短的社交媒体剪辑到全长电影&#xff0c;我们对流畅、高质量视频播放的需求从未如此强烈。对于开发者来说&#xff0c;为iOS用户提供无瑕疵的视频体验是一项挑战&#xff0c;也是一种艺术。本篇…

数据结构之list类

前言 list是列表类。从list 类开始&#xff0c;我们就要接触独属于 Python 的数据类型了。Python 简单、易用&#xff0c;很大一部分原因就是它对基础数据类型的设计各具特色又相辅相成。 话不多说&#xff0c;让我们开始学习第一个 Python 数据类型一list。 1. list的赋值 输…

手把手教你使用 VS Code 运行和调试 Python 程序

本文以 Ubuntu 系统为例&#xff0c;介绍如何在 VS Code 上配置 Python 的编程环境&#xff0c;并把 Python 程序运行、调试起来。由于 Python 是解释型语言&#xff0c;并且 VS Code 中提供了内置的调试器可用于调试 Python 代码&#xff0c;因此配置和操作流程比调试 C/C 代码…

SpringSecurity+JWT前后端分离架构登录认证

目录 1. 数据库设计 2. 代码设计 登录认证过滤器 认证成功处理器AuthenticationSuccessHandler 认证失败处理器AuthenticationFailureHandler AuthenticationEntryPoint配置 AccessDeniedHandler配置 UserDetailsService配置 Token校验过滤器 登录认证过滤器接口配置…

C++将信息输入到文件内

第一步检查文件是否打开&#xff0c;用到头文件&#xff1a; #include <fstream> #include <sstream> 文件打开的函数为 file.isopen() 信息输入到文件应该为 file << "" << value; 注意是file<< 如图 定义file ofstream f…

计算机组成原理 第一弹

ps&#xff1a;本文章的图片来源都是来自于湖科大教书匠高老师的视频&#xff0c;声明&#xff1a;仅供自己复习&#xff0c;里面加上了自己的理解 这里附上视频链接地址&#xff1a;1-2 计算机的发展_哔哩哔哩_bilibili ​​ 目录 &#x1f680;计算机系统 &#x1f680;计…

UI测试脚本录制器已上线,RunnerGo :UI自动化测试平台

想快速配置可视化UI自动化测试脚本&#xff1f;RunnerGo近期上线脚本录制器&#xff0c;根据你的测试操作直接生成UI自动化测试脚本&#xff0c;下面是使用方法 Step1:下载录制器 点击RunnerGo上方插件按钮下载录制器 Step2:录制器使用 将插件文件拖入浏览器扩展程序 点击打…

Zabbix 系统监控详解

1 介绍 1.1 摘要 本文深入浅出&#xff0c;切近实际运维应用&#xff0c;由 zabbix 3.4 版本入手&#xff0c;学习 zabbix 监控告警实现方式&#xff0c;由 zabbix 5.0 浅出实现快速部署、快速应用。本人从业多年&#xff0c;关注 zabbix 开源社区&#xff0c;以及 zabbix 官…

【计算机网络】3、IPv6、网络三层模型、网络的规划与设计、网络的规划与设计、网络存储技术、网络地址翻译NAT、默认网关、虚拟局域网VLAN、虚拟专用网VPN、URL

文章目录 IPv6IPv6的特点IPv4和IPv6的过渡期间主要采用三种基本技术双协议栈隧道技术翻译技术 网络三层模型核心层汇聚层接入层 网络的规划与设计工作区子系统水平布线子系统管理子系统垂直干线子系统设备间子系统建筑群子系统总结 廉价磁盘网络存储技术直接附加存储(DAS)网络附…

Git学习笔记(第1章):Git概述

目录 1.1 版本控制 1.1.1 何为版本控制 1.1.2 为什么需要版本控制 1.1.3 版本控制工具 1.2 发展历史 1.3 工作机制 1.4 代码托管中心&#xff08;远程库&#xff09; Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。…

LeetCode19:删除链表的倒数第N个结点

力扣题目链接 思路&#xff1a;由于本题有可能删除头结点&#xff0c;为保证删除头结点和其他结点的操作一致&#xff0c;因此首先创建一个虚拟头结点dummy。 其次&#xff0c;本题需要删除倒数第N个结点&#xff0c;由于单链表只有next指针&#xff0c;因此需要找到倒数第N1…

事件驱动架构

请求驱动 服务注册&#xff0c;服务发现&#xff0c;虽然调用地址隐藏了&#xff0c;但是调用stub必须相同。 rpc通信&#xff0c;远程调用。 生产者和消费者要有相同的stub存根。 消费者和生产者的调用接口是耦合的。 事件驱动 核心&#xff1a;上下游不进行通信 中间通过M…

AP5101C 高压线性 LED恒流驱动器 DFN2*2 LED灯汽车雾灯转向灯

产品描述 AP5101C 是一款高压线性 LED 恒流芯片 &#xff0c; 简单 、 内置功率管 &#xff0c; 适用于6- 100V 输入的高精度降压 LED 恒流驱动芯片。电流2.0A。AP5101C 可实现内置MOS 做 2.0A,外置 MOS 可做 3.0A 的。AP5101C 内置温度保护功能 &#xff0c;温度保护点为 130 …