【数据结构与算法从0到1 - C实现】一、顺序表的解析与实现

下一节:【数据结构与算法从0到1 - C实现】二、链表的解析与实现 


目录

1 线性表

2 顺序表

2.1 概念及结构

2.2 顺序表的实现

2.3 几个需要了解的点

2.4 数组相关面试题

 2.4 顺序表的问题与思考


本节要探讨的顺序表是线性表一种。

1 线性表

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

2 顺序表

2.1 概念及结构

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

顺序表一般分为 静态顺序表 和 动态顺序表

  • 静态顺序表:使用定长数组存储元素。
    • 特点:如果满了,就不让再插入
    • 缺点:给多大的空间合适?这个很难确定。N给小了不够用,N给大了浪费
#pragma once

#define N 1000
typedef int SLDataType;

//静态顺序表
typedef struct SeqList
{
    SLDataType arr[N];
    int size;//表示数组中存储了多少个数据
}SL;

// 接口函数
void SeqListInit(SL* ps);
void SeqListPushBack(SL* ps, SLDataType x);
void SeqListPopBack(SL* ps);
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPopFront(SL* ps);
  •  动态顺序表:使用动态开辟的数组存储,解决了静态顺序表的缺点。
//动态顺序表
typedef struct SeqList
{
    SLDtaSLDataTypetType *arr;
    int size;//表示数组中存储了多少个数据
    int capacity;//数组实际能存储的数据的空间容量时多大,单位是元素个数
}SL;

// 接口函数
void SeqListInit(SL* ps);
void SeqListPushBack(SL* ps, SLDataType x);
void SeqListPopBack(SL* ps);
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPopFront(SL* ps);

2.2 顺序表的实现

在项目中创建SeqList.h头文件,设计动态顺序表结构体,即各种功能接口函数的声明。

代码如下:

#define _CRT_SECURE_NO_WARNINGS
#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//动态顺序表
typedef struct SeqList
{
    SLDataType *arr;
    int size;//表示数组中存储了多少个数据
    int capacity;//数组实际能存储的数据的空间容量时多大,单位是元素个数
}SL;

// 接口函数
//打印顺序表
void SeqListPrint(SL* ps);
//初始化顺序表
void SeqListInit(SL* ps);
//销毁顺序表
void SeqListDestroy(SL* ps);
//顺序表尾插
void SeqListPushBack(SL* ps, SLDataType x);
//顺序表尾删
void SeqListPopBack(SL* ps);
//顺序表头插
void SeqListPushFront(SL* ps, SLDataType x);
//顺序表头删
void SeqListPopFront(SL* ps);
//查找指定值,找到了返回下标,没找到,返回-1
int SeqListFind(SL* ps, SLDataType x);
//在指定位置插入一个元素
int SeqListInsert(SL* ps, int pos, SLDataType x);
//删除pos位置的数据
void SeqListErase(SL* ps, int pos);

在文件SeqList.c中完成接口的具体实现:

#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"

// 接口函数

//打印顺序表
void SeqListPrint(SL* ps)
{
    int i = 0;
    for (i = 0; i < ps->size; i++)
    {
        printf("%d ", ps->arr[i]);
    }
    printf("\n");
}

//初始化顺序表
void SeqListInit(SL* ps)
{
    ps->arr = NULL;
    ps->size = ps->capacity = 0;
}

//销毁顺序表
void SeqListDestroy(SL* ps)
{
    if (ps->arr != NULL)
    {
        free(ps->arr);
        ps->arr = NULL;
        ps->capacity = ps->size = 0;
    }
}

void SeqListCheckCapacity(SL* ps)
{
    if (ps->size == ps->capacity)
    {
        int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        SLDataType* ptr = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
        if (ptr == NULL)
        {
            perror("realloc");
            exit(-1);
        }
        ps->arr = ptr;
        ps->capacity = newcapacity;
    }
}

//顺序表尾插
void SeqListPushBack(SL* ps, SLDataType x)
{
    SeqListCheckCapacity(ps);
    ps->arr[ps->size] = x;
    ps->size++;
}

//顺序表尾删
void SeqListPopBack(SL* ps)
{
    assert(ps->size > 0);//对于空数组不能进行尾删
    ps->size--;
}

//顺序表头插
void SeqListPushFront(SL* ps, SLDataType x)
{
    SeqListCheckCapacity(ps);
    int i = 0;
    for (i = ps->size; i > 0; i--)
    {
        ps->arr[i] = ps->arr[i - 1];
    }
    ps->arr[0] = x;
    ps->size++;
}

//顺序表头删
void SeqListPopFront(SL* ps)
{
    assert(ps->size > 0);
    int i = 0;
    for(i = 0; i < ps->size - 1; i++)
    {
        ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;
}

//查找指定值,找到了返回下标,没找到,返回-1
int SeqListFind(SL* ps, SLDataType x)
{
    int i = 0;
    for (i = 0; i < ps->size; i++)
    {
        if (ps->arr[i] == x)
        {
            return i;
        }
    }
    return -1;
}

//在指定位置插入一个元素
int SeqListInsert(SL* ps, int pos, SLDataType x)
{
    assert(ps->size >= pos);
    SeqListCheckCapacity(ps);
    int i = 0;
    for (i = ps->size - 1; i >= pos; i--)
    {
        ps->arr[i + 1] = ps->arr[i];
    }
    ps->arr[pos] = x;
    ps->size++;
}

//删除pos位置的数据
void SeqListErase(SL* ps, int pos)
{
    assert(ps->size > pos);
    int i = 0;
    for (i = pos; i < ps->size - 1; i++)
    {
        ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;
}

2.3 几个需要了解的点

  •  内存碎片:计算机内存中存储的数据并不都是物理相连的,我们姑且称连在一起的数据为数据块,那么不同数据块之间是有大小不一的空闲内存的,而我们在编写程序向内存申请空间(malloc/realloc)时,就是申请的碎片式的内存空间中的一个空闲碎片
  • 顺序表扩展的两种情况:(假设当前顺序表的大小为8,要扩展为16,即增加8)
    • ​​​​​​顺序表扩展是默认向后扩展的,如果当前顺序表后8个单位内存刚好都是空闲的,那么就直接向后扩展,不需要重新申请一块16单位的内存来扩展顺序表,也就不需要复制当前的顺序表的数据到新的顺序表
    • 如果当前顺序表后的8个单位内存已经有被占用,那么就需要重新在其他的位置申请16个单位的空间来存放扩展后的顺序表数据,那么就需要将当前顺序表的所有数据拷贝到新的顺序表中

2.4 数组相关面试题

  1. 原地移除数组中的所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。OJ链接
  2.  删除排序数组中的重复项。OJ链接
  3. 合并两个有序数组。OJ链接

 2.4 顺序表的问题与思考

  • 问题:
  • 中间/头部的插入删除,时间复杂度为O(N)
  •  增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。
  •  增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
  • 思考:


所属分类专栏:数据结构与算法

下一节:【数据结构与算法从0到1 - C实现】二、链表的解析与实现

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构》(c语言版) 是由严蔚敏等人编著的一本关于数据结构的教材。该书主要介绍了各种数据结构,如线性表、链表、栈、队列、树、图等,并针对每种数据结构详细介绍了其定义、基本操作以及应用。同时,书中还提供了丰富的习题和考研真题,供学生练习巩固所学知识。 该书的第一部分是对数据结构基础的介绍,包括算法分析与设计、线性表、栈和队列等。其中,算法分析与设计部分主要讨论了算法的时间复杂度和空间复杂度分析方法,为后续章节奠定了基础。线性表部分介绍了顺序表链表的定义、基本操作以及应用,而栈和队列部分则详细介绍了栈和队列的定义、基本操作以及常见应用。 第二部分主要介绍了树和二叉树,包括二叉树的定义、二叉树的遍历、树的存储结构和线索二叉树等。此外,还介绍了树的应用,如赫夫曼树和二叉排序树,以及B树和B+树等高级树结构。 第三部分则介绍了图的基本概念、图的存储结构以及图的遍历算法。此外,还讨论了最小生成树和最短路径等重要算法。 该书尤其重视习题的设计,每章都设置了大量的习题,包括填空题、选择题和编程题等,旨在帮助学生巩固所学知识并提升解决实际问题的能力。此外,书中还附有考研真题和详细解析,为考研学生提供实战练习的机会。 总而言之,严蔚敏《数据结构》(c语言版) 笔记和习题(含考研真题)详解 把数据结构的基础知识讲解得深入浅出,习题设计丰富,适合作为数据结构教材或考研复习资料使用。通过学习该书,读者可以建立起扎实的数据结构知识基础,并熟练掌握各种数据结构的应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值