一、数据结构概述

基本概念:

数据结构指的是计算机存储数据和组织数据的方式,数据结构和组织数据的目的是为了后期对数据的再次利用,所以存储的数据一般是具有一个或者多个特定关系的集合,利用不同的数据结构可以提高数据的访问效率。

数据指的是可以被输入到计算机并且可以被计算机处理的符号的总称,数据的英文是Data。

数据结构:

数据是有单位的,数据的基本单位是数据元素(Data Element),在计算机中数据元素是作为整体来处理的,比如学生的信息。数据元素是由多个数据项组成的,所以数据项也被称为数据的最小单位,比如学生信息中的学号、姓名、年龄,数据项属于数据元素不可分割的一部分。

数据结构就是描述多个数据之间的逻辑结构和物理结构。逻辑结构指的是数据元素之间的逻辑关系,物理结构指的是计算机中存储数据的方式,所以物理结构也被称为存储结构。

注意:数据元素的逻辑关系和物理关系没有必然的联系,数据元素可能同时存储逻辑关系和物理关系,数据元素之间也可能只存在一种关系,或者数据元素之间一种关系都没有。 注意:数据元素的逻辑关系和物理关系没有必然的联系,数据元素可能同时存储逻辑关系和物理关系,数据元素之间也可能只存在一种关系,或者数据元素之间一种关系都没有。

逻辑关系:

对于数据结构的逻辑关系,可以分为四种:集合(无关系)、线性结构(一对一)、树状结构(一对多)、图状结构(多对多)。

物理关系

数据的物理关系可以分为两种:一种是顺序结构(连续存储),另一种是离散结构(离散存储),一般把顺序结构也称为顺序存储,一般把离散结构也称为链式存储

算法

广义上讲算法是研究数据之间的逻辑关系,然后选择某种方案来存储数据,并在此基础上对数据进行处理,其实更加直白的说:算法指的是计算或者解决问题的步骤。

算法特征

(1) 有穷性:指的是程序执行必须在有限次数内完成,而每一次必须在有限时间内执行完成。

(2) 确定性:执行的每一条语句都必须有准确的解释,不能出现二义性,意味着相同的输入 就会相同的输出。

(3) 可行性:程序中每一条复杂语句都可以分解为基本指令,并且每条基本指令都必须在有 限时间完成。
(4) 输入项:指的是算法可以有一个或者多个参数作为初始条件,然后对程序进行有效执行。

(5) 输出项:指的是算法经过运算之后可以有一个或者多个输出,所以一个有意义的算法是 应该有输出结果的。

总结:一个程序的执行是需要用户选择合适的算法和数据结构的,程序 = 数据结构+算法。

到底什么样的数据结构和算法是合适的?怎么去评定选择的数据结构和算法是否合适?

对于数据结构的选择和算法的选择并不是唯一的,但是选择要是合适的,衡量数据结构和算法的选择是否合适,取决于算法实现的运行时间和内存空间。一般是通过两个专业性名称,分别是“时间复杂度”和“空间复杂度” 对于数据结构的选择和算法的选择并不是唯一的,但是选择要是合适的,衡量数据结构和算法的选择是否合适,取决于算法实现的运行时间和内存空间。一般是通过两个专业性名称,分别是“时间复杂度”和“空间复杂度”

时间复杂度

时间复杂度不是算法的运行时间来衡量,因为程序的运行时间取决于CPU的性能,不同性能的CPU执行指令的周期是不一样的,比如8bit单片机的主频是12MHZ,而32bit单片机的主机可以168MHZ,而计算机的CPU主频都是xxx.GHZ 。

时间复杂度指的是算法程序的语句的执行次数,也可以称为语句频度,一个程序的语句执行次数越多,则时间复杂度越大,则说明算法不合适。时间复杂度一般采用数学符号大O()表示,一般时间复杂度的计算中都会出现n,n表示规模,对于时间复杂度是表示算法的趋势。

一般会把算法程序的语句的执行次数用T()表示,但是对于函数T()可能是一个多项式,而时间复杂度就是找出函数T()影响最大的项,所以时间复杂度是执行语句的估算值,使用数学符号大O()表示。O其实是order的缩写。大O的括号中写的值就是影响程序执行语句最大的那个项。

计算技巧:只需要计算出算法的基本执行语句的最高次项,并且把最高次项的系数舍弃,就是算法的时间复杂度,需要使用数学符号O(xxx),如果计算出的是常数项,则时间复杂度衡为O(1)。 计算技巧:只需要计算出算法的基本执行语句的最高次项,并且把最高次项的系数舍弃,就是算法的时间复杂度,需要使用数学符号O(xxx),如果计算出的是常数项,则时间复杂度衡为O(1)。

空间复杂度 空间复杂度

空间复杂度指的是程序运行期间所需要的内存空间,空间复杂度越大,则说明程序运行期间需要的内存越多,则说明算法不合适。

注意:程序中的时间复杂度和空间复杂度是可以互相转换的,一般情况下是相互制约的,意味着“鱼和熊掌不可兼得”,所以用户根据实际情况去选择时间还是空间,意味着要选择合适的算法来保持平衡。 注意:程序中的时间复杂度和空间复杂度是可以互相转换的,一般情况下是相互制约的,意味着“鱼和熊掌不可兼得”,所以用户根据实际情况去选择时间还是空间,意味着要选择合适的算法来保持平衡。
一个好的算法通常是执行时间短,占用空间少,并且可读性好、容易维护,易于移植到其他平台。

结构类型

数组在数据结构中是属于线性表的一种,线性表是由一组具有n个相同类型的数据元素组成的。

线性表中的任何一个数据元素有且只有一个直接前驱,以及有且只有一个直接后继,另外首元素是没有前驱的,尾元素是没有后继的。

根据数据的存储方式可以把线性表分为两种:顺序存储的线性表,链式存储的线性表

顺序表

顺序表指的是使用一组内存地址连续的内存单元来依次存储线性表中的数据元素,使用这种存储结构的线性表就被称为顺序表。

简单理解:数据存储在一块连续的内存中,在C语言中可以具名的数组,也可以使用匿名的数组(堆内存)。

顺序表的特点:数据元素之间的逻辑关系是相邻的,并且内存地址也是相邻的,所以只要知道存储线性表的第一个数据元素的内存地址,就可以对线性表中的任意一个元素进行随机访问。通常用户使用动态分配的数组来实现顺序表,也就是使用堆内存实现。

随机访问指的是在同等时间内具有访问任意元素的能力,和随机访问相对立的就是顺序访问,顺序访问花费的时间要高于随机访问,比如卷轴(顺序)和书籍(随机)、磁带(顺序)和唱片(随机)。

既然数组可以作为线性表来使用,请问如何对数组中的元素进行增加和删除以及访问?

如果打算使用数组实现线性表的特性,需要知道三个条件:数组首元素地址、数组元素的容量、数组有效的最后一个元素的下标。

顺序表部分代码

//main.c
#include <stdio.h>
#include "seqlist.h"

#define size 50
int main(int argc,char const * argv[]){
    SeqList_t *manager=SeqList_Create(size);
    if(SeqList_IsFull(manager)==false){
        for(int i=0;i<=50;i+=2){
            SeqList_TailAdd(manager,i);
        }
    }else{
        printf("SeqList is full");
        return -1;
    }

SeqList_Show(manager);

if(SeqList_IsFull(manager)==false){
    SeqList_HeadAdd(manager,5);
}else{
    printf("SeqList is full");
    return -1;
}

SeqList_Show(manager);

SeqList_Del(manager,10);
SeqList_Del(manager,7);
SeqList_Del(manager,0);
SeqList_Del(manager,19);
SeqList_Show(manager);

SeqList_Free(manager);

    return 0;
}
/**************************************************************
*file name      : seqlist.h
*brief          : 创建顺序表,并实现顺序表的增删改查
*auto           : TingFengLuo9@162.com
*date           :2025-11-16
*note           :NULL
*copyright (c) TingFengLuo9@163.com ALL Rights Reserved
**************************************************************/
#ifndef __SEQLIST_H
#define __SEQLIST_H

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

//定义顺序表中首地址的类型,方便用户修改
typedef int Datatype_t;
typedef unsigned int uint_t;

typedef struct SequentialList{
    Datatype_t *Addr; //记录顺序表的首地址
    uint_t Element;   //记录顺序表中的容量
    int Last;         //记录顺序表中最后元素的下标
}SeqList_t;

SeqList_t * SeqList_Create(uint_t Element);
bool SeqList_IsFull(SeqList_t *manager);
bool SeqList_TailAdd(SeqList_t *manager,Datatype_t Data);
bool SeqList_HeadAdd(SeqList_t *manager,Datatype_t Data);
bool SeqList_IsEmpty(SeqList_t *manager);
bool SeqList_Del(SeqList_t *manager,Datatype_t DestVal);
void SeqList_Show(SeqList_t *manager);
void SeqList_Free(SeqList_t *manager);
#endif

给出部分代码块,可以根据部分代码块来填充自己的思维

/**************************************************************
*function name      : SeqList_Create
*brief              : 创建顺序表
*para               : 
        @uint_t Element:用户要创建顺序表容量的大小
*return             : 返回顺序表的地址
*auto               : TingFengLuo9@162.com
*date               : 2025-11-16
*note               : NULL
**************************************************************/
SeqList_t * SeqList_Create(uint_t Element)
{
    //创建顺序表并从堆空间分配内存
    SeqList_t *manager = (SeqList_t *)calloc(1,sizeof(manager));
    if(NULL == manager){
        perror("calloc mempry failed for manager");
        exit(-1);
    }
    //初始化顺序表
    manager->Addr=(Datatype_t *)calloc(Element,sizeof(Datatype_t));
    if(NULL == manager->Addr){
        perror("calloc mempry failed for Addr");
        free(manager);
        exit(-1);
    }

    manager->Element = Element;
    manager->Last = -1;

    return  manager;
}

/**************************************************************
*function name      : SeqList_LastAdd
*brief              : 在顺序表的末尾插入数据
*para               : 
        @manager:在哪一个顺序表中
        @Data   :要插入的数据
*return             : 插入成功返回true,否则返回false
*auto               : TingFengLuo9@162.com
*date               : 2025-11-16
*note               : NULL
**************************************************************/
bool SeqList_TailAdd(SeqList_t *manager,Datatype_t Data)
{   
    if(SeqList_IsFull(manager)){
        printf("Sequential is Full!!!");
        return false;
    }
    //如果顺序表没有满,则在顺序表最后插入
    manager->Addr[++manager->Last]=Data;
    return true;
}

/**************************************************************
*function name      : SeqList_IsEmpty
*brief              : 判断顺序表是否为空
*para               : 
        @manager:在哪一个顺序表中
*return             : 如果为空返回true,否则返回false
*auto               : TingFengLuo9@162.com
*date               : 2025-11-16
*note               : NULL
**************************************************************/

bool SeqList_IsEmpty(SeqList_t *manager)
{
    return (-1 == manager->Last) ? true : false;
}

有问题可以一起讨论哦!!!
又是变强的一天,头发变少的一天