初阶数据结构之---顺序表和链表(C语言)

本文围绕线性表中的顺序表和链表展开。详细介绍了顺序表的静态和动态结构,以及动态顺序表的初始化、扩容、增删查改等操作;还阐述了链表的多种分类,实现了单链表和双向链表的创建、插入、删除、查找等功能,并对顺序表和链表进行了小结。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言-线性表

线性表:

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。线性表在逻辑上是线性结构,也就是说是连续的一条直线。但在物理上并不一定是连续的。线性表在物理上存储时,通常以数组链式结构的形式存储。

我们今天的主角,顺序表和链表,其实都是线性表,当然线性表不止包含这两个

线性表:

  • 顺序表
  • 链表
  • 队列
  • 字符串
  • ……

再次声明:线性表的逻辑结构是线性的,物理结构不一定是线性

顺序表

概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

1.静态顺序表:使用定长存储元素

2.动态顺序表:使用动态开辟数组的存储

来跟我一起手搓个顺序表吧

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们手搓动态顺序表。

我们先写一个头文件,里面写好我们维护的动态顺序表以及要实现的接口函数

结构及接口Sqlist.h

//Sqlist.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<assert.h>
#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);

往下就可以开始实现我们的顺序表内容了,下面对于接口的实现放在 Sqlist.c 中

初始化和销毁

void SLInit(SL* ps)
{
    ps->a = NULL; //开始时,给一个空指针
    ps->capacity = ps->size = 0;
}

void SLDestroy(SL* ps)
{
    assert(ps); //断言,防止ps为空指针
    ps->capacity = ps->size = 0;
    free(ps->a);
    ps->a = NULL;
}

顺序表打印

void SLPrint(SL* ps)
{
    assert(ps);
    for (int i = 0; i < ps->size; i++) {
        printf("%d\n",ps->a[i]);
    }
    printf("\n");
}

 这里需要注意的是,在打印过程中,往顺序表中放置的数据类型不同,所打印的方式也会有所不同,在头文件Sqlist.h中

typedef int SLDataType;

这句代码说明放入的数据类型是int,所以我这里就使用int的打印方式了。

扩容

void SLCheckCapacity(SL* ps)
{
    if (ps->size == ps->capacity) {
        int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
        //防止开辟空间失败返回空指针
        if (tmp == NULL) {
            perror("malloc fail:");
            exit(1);
        }
        ps->a = tmp;
        //更新容量
        ps->capacity = newCapacity;
    }
}

扩容的部分在整个动态顺序表中占据非常重要的地位,关系到堆中空间的开辟,保证后续数据操作的顺利进行。

头部插入删除和尾部插入删除数据

//头部插入删除
void SLPushFront(SL* ps, SLDataType x)
{
    assert(ps);
    SLCheckCapacity(ps);//保证插入时不会越界
    for (int i = ps->size; i > 0; i--) {
        ps->a[i] = ps->a[i - 1];
    }
    ps->a[0] = x;
    ps->size++;
}
void SLPopFront(SL* ps)
{
    assert(ps);
    
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Forcible Bug Maker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值