数据结构(基本概念及顺序表——c语言实现)

news2024/11/20 0:36:49

基本概念:

1、引入

程序=数据结构+算法

数据:

数值数据:能够直接参加运算的数据(数值,字符)

非数值数据:不能够直接参加运算的数据(字符串、图片等)

数据即是信息的载体,能够被输入到计算机中存储,识别和处理的符号总称;

数据元素:

数据的一个基本单位,又叫做记录;

数据类型:

数据元素决定了数据的存储范围以及计算方法;例如:short x;则这个x的取值范围【-32768,+32767】,限定计算{+ - * / %}

数据结构:

数据结构就是去探讨数据元素与数据元素之间的相互关系的学科;

2、基本概念

逻辑结构:数据元素之间的抽象关系(相邻/隶属)

存储结构:在计算机中的具体实现方法

数据运算:对数据进行的操作,例如:增删改查

3、逻辑结构

集合(gather):数据元素在同一范围内部,无其他关系

表(list):一对一,例如:线性表,队列,栈

树(tree):一对多

图(graph):多对多

4、存储结构

存储结构又叫做物理结构,存放到地址上的关系;

顺序存储(sequence):在存储器上按照顺序存放

链式存储(link):利用元素存储地址的指针来描述元素之间的关系

索引存储:(index):在存储数据时额外提供一张索引表

散列存储(hash):先提供一个散列函数,利用散列函数去计算存储位置

数据的逻辑结构与存储结构关系密切

算法的设计:取决于决定的逻辑结构

算法的实现:依赖与采用的存储结构

5、算法

算法是一个有穷规则(语句、命令)的有序集合,它确定了解决某一个问题的一个运算序列。对于问题的初始输入,通过算法的有限步的运行,产生一个或多个输出。

算法的特性:

1)有穷性——算法执行的步骤是有限的

2)确定性——每一个计算步骤是唯一的

3)可行性——每个计算步骤能在有效的时间内完成

4)输入——算法可以有零个或者多个外部输入

5)输出——算法有一个或多个输出

算法的优劣:

1)消耗的时间的多少

2)消耗的存储空间的多少

3)容易理解,容易实现调试和维护是否容易

6、时间复杂度T(n)

通过问题的规模,算法所消耗的时间的多少

#include <stdio.h>
int main(int argc, char *argv[])
{
    for(int i=0;i<n-1;i++)    //=>n-1
    {
        for(int j=0;j<n-1-i;j++)  //=>(n-1)/2
        {
        }
    }
    return 0;
}

频度:语句执行的次数

每一条语句的频度之和就是这个算法的时间复杂度

上述代码的时间复杂度为:(n-1)*(n-1)/2 ==>n²/2 - n + 1/2 (当n趋于无穷大时和n²为同阶无穷大

冒泡排序的时间复杂度(o(n²))

7、空间复杂度D(n)

通过问题规模的扩大,所需要的额外空间的量

8、线性表

线性表的特征:

1)对于非空表而言,a0表头没有前驱

2)an-1表尾,没有后继

3)对于其它的每一个元素ai有且仅有一个直接前驱和直接后继

9、数据结构创建表时之所以常在堆区开空间,主要基于以下几点原因:

1. 灵活性和动态性

  • 动态内存分配:堆区允许程序员在运行时动态地分配和释放内存,这意味着可以根据实际需要调整数据结构的大小,而无需在编译时就确定其大小。这对于链表、动态数组等需要频繁调整大小的数据结构尤为重要。

  • 避免栈溢出:栈区通常用于存储局部变量和函数调用信息,其空间有限。如果数据结构过大或复杂,可能会导致栈溢出。而在堆区分配内存可以避免这一问题,因为堆区的空间相对较大,且由程序员控制释放。

2. 内存管理灵活性

  • 手动管理内存:在堆区分配的内存需要程序员手动释放(使用如free等函数),这提供了更大的内存管理灵活性。程序员可以根据需要决定何时释放内存,从而优化内存使用。

  • 生命周期控制:堆区分配的内存的生命周期由程序员控制,这意味着可以在数据结构不再需要时立即释放内存,减少内存泄漏的风险。

3. 链表等特殊数据结构的需要

  • 链表节点:链表节点通常需要在堆区分配内存,因为链表节点的数量和位置在运行时是动态变化的。每个节点都包含数据域和指针域(指向下一个节点的指针),这些节点在内存中的位置不连续,因此适合在堆区分配。

  • 内存碎片化:链表等数据结构能够很好地适应内存碎片化的情况,因为它们在堆区分配内存时不需要连续的内存块。

4. 性能优化

  • 局部性原理:虽然堆区分配的内存可能不如栈区分配的内存那样具有局部性(即内存访问的集中性),但对于某些数据结构(如哈希表、红黑树等),其内存访问模式可能更适合在堆区分配内存。

  • 减少栈空间占用:将大型数据结构放在堆区可以减少栈空间的占用,从而降低栈溢出的风险,并提高程序的稳定性。

顺序表 

顺序存储的线性表

顺序存储结构的特点:

1)逻辑上相邻的元素ai,ai+1,其存储位置也是相邻的;

2)对数据元素ai的存取为随机存取或按地址存取

3)存储密度高,存储密度=(数据结构中元素所占存储空间)/(整个数据结构所占空间)

顺序存储结构的不足:

1)对表的插入和删除等运算的时间复杂度较差

2)要求系统提供一片较大的连续存储空间

3)插入、删除等运算耗时,且存在元素在存储器中成片移动的现象

#ifndef _SEQLIST_H
#define _SEQLIST_H

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

typedef int Type;     //声明顺序表的数据类型
#define LEN 10        //顺序表的大小
#define OUT(A) {printf("%d ",A);} //宏函数最好加;和{}

typedef struct{
    Type data[LEN];   //表的存储空间
    int count;        //记录表的已存元素量
}list;                //线性表结构体

//创建
list *create_list();  //结构体指针
//判满(满为0,不满为1,错误为-1)
int full_list(list *l);
//判空(空为0,非空为1,错误为-1)
int null_list(list *l);

void whether_full(int a);

//初始化
void init_list(list *l);
//求长度
int length_list(list *l);
//首插入
void head_insert_list(list *l,Type data);
//插入(尾插)
void insert_list(list *l,Type data);
//查找 按值找,把找到值的下标返回
int search_list(list *l,Type data);
//更新 按值更新,把原值改为新值
void update_list(list *l,Type oldData,Type newData);
//删除
void delete_list(list *l,Type data);
//遍历
void traverse_list(list *l);
//回收
void free_list(list **l);

#endif

       顺序表的代码较多,所以使用多文件封装来写比较清晰;顺序表的存储既然是要连续的空间地址,那么我们就使用数组来存储,因为数组中的元素就是连续存储的;一个顺序表中除了数组来存储元素外,还需要一个变量来存储数组的长度,因此我定义了一个结构体来存放这两个成员,将结构体重命名为list方便后续使用。

#include "seqlist.h"
//创建
list *create_list()
{
    list *p = (list *)malloc(sizeof(list));
    if(NULL == p)
    {
        perror("create malloc");
        return NULL;
    }
    return p;
}
//判满(满为0,不满为1,错误为-1)
int full_list(list *l)
{
    if(NULL == l)
    {
        puts("list is NULL");
        return -1;
    }
    else if(LEN == l->count)
    {
        puts("表已放满");
        return 0;
    }
    else
    {
        //puts("表未满");
        return 1;
    }
}
//判空(空为0,非空为1,错误为-1)
int null_list(list *l)
{
    if(NULL == l)
    {
        puts("list is NULL");
        return -1;
    }
    return l->count==0?0:1;
}

void whether_full(int a)
{
    if(0 == a)
        puts("表已满");
    else if(1 == a)
        puts("表未满");
}
//初始化
void init_list(list *l)
{
    if(NULL == l)
    {
        puts("list is NULL");
        return;
    }
    l->count=0;
}
//求长度
int length_list(list *l)
{
    if(NULL == l)
    {
        puts("list is NULL");
        return -1;
    }
    return l->count;
   // printf("index:%d\n",l->count);
}
//首插入
void head_insert_list(list *l,Type data)
{
    if(0 == full_list(l))
        return;
    int n = l->count;
    while(n)
    {
        l->data[n] = l->data[n-1];
        n--;
    }
    l->data[0] = data;
    l->count++;
}
//插入(尾插)
void insert_list(list *l,Type data)
{
    if(0 == full_list(l))
        return;
    //if(full_list(l))
    //{
    //l->data[l->count] = data;
    //l->count++;
    //}
    l->data[l->count] = data;
    l->count++;
}
//查找 按值找,把找到值的下标返回
int search_list(list *l,Type data)
{
    if(NULL == l)
    {
        puts("list is NULL");
        return -1;
    }
    for(int i=0;i<l->count;i++)
    {
        //memcmp(&data,&l->data[i],sizeof(Type))==0;
        if(data == l->data[i])
            return i;
    }
    return -1;
}
//更新 按值更新,把原值改为新值
void update_list(list *l,Type oldData,Type newData)
{
#if 1
    if(NULL == l)
    {
        puts("list is NULL");
        return;
    }
    for(int i=0;i<l->count;i++)
    {
        if(oldData==l->data[i])
            l->data[i]=newData;
        //break;
    }
#else
    int t;
    while((t=search_list(l,oldData))!=-1)
        l->data[t]=newData;
#endif
}
//删除
void delete_list(list *l,Type data)
{
    if(NULL == l)
    {
        puts("list is NULL");
        return;
    }
#if 0
    for(int i=0;i<l->count;i++)
    {
        if(data == l->data[i])
            for(int j=i;j<l->count-1;j++)
            {
                l->data[j] = l->data[j+1]
            }
            l->count--;
            i--;
    }
#elif 1
    int index = 0;
    for(int i=0;i<l->count;i++)
    {
        if(data != l->data[i]) //不是要删的值就赋值,是要删的就不赋,直到是不删的数再把它放到之前要删那个数的位置,相当于是以覆盖的形式删除
        {
            l->data[index++]=l->data[i];
        }
    }
    l->count = index;
#endif
}
//遍历
void traverse_list(list *l)
{
    if(NULL == l)
    {
        puts("list is NULL");
        return;
    }
    for(int i=0;i<l->count;i++)
    {
        OUT(l->data[i]);
    }
    puts("");
}
//回收
void free_list(list **l) //传二级指针是为了让开辟空间所用的指针指向空 传参传指针的地址
{
    if(NULL == l)
    {
        puts("list is NULL");
        return;
    }
    free(*l);
    *l=NULL;
}

创建:list *create_list()

       创建一个顺序表,在堆区开空间,并且创建完之后我们需要拿到表的首地址,因此函数的类型为list *型,创建空间的大小就是结构体的大小,结构体有多大就开多大的空间,开完空间之后我们需要做一个简单的判断,让我们知道开辟空间是否开辟成功,不成功返回NULL,成功返回首地址。

判断表是否放满:int full_list(list *l)

       对于顺序表来说,因为在结构体里面我们有一个专门用来记录表中元素个数的变量,因此判断表是否放满直接判断这个变量的值是否等于表的长度LEN即可,满返回一个0,不满返回一个1。

判断表是否为空:int null_list(list *l)

      判空跟判满其实差不多,也是判断记录个数的变量的值是否为0,为0就是空,不为0就是非空。

顺序表的初始化和求长度也是跟判空一样的操作,都是操作变量count,初始化就直接把count置零就可以,因为在后续插入内容的时候会将以前的内容覆盖;求长度其实就是直接将count的值当做函数返回值反出去即可。 

头部插入:void head_insert_list(list *l,Type data)

       对于插入函数来说,我们需要操作表因此需要传参传入表的地址,还有我们需要插入的元素,首先需要判断这个表是否放满,如果放满那就不可以继续再插入,此时直接return即可;为放满那就可以插入,因为是头部插入,因此在插入之前,我们需要给要插入的元素把第一个位置空出来,所以我们需要把表中已有的元素全部往后移动一位,通过一个循环从最后一个元素开始移动,有几个元素就移动几次,移动完之后就可以把要放入的元素放入第一个位置,之后一定不要忘记count要自增1。

尾部插入:void insert_list(list *l,Type data) 

       尾部插入相对于头部插入就简单得多,首先也是需要判断表满没满,没满之后直接再最后一个元素后面放入要插入的元素即可,l->data[l->count] = data;之后count自增1。

查找:int search_list(list *l,Type data)

       查找传入的也是两个参数,表和要查找的值,返回值为该值的下标,通过一个循环遍历的方式来查找我们所需要的元素的下标,找到这个元素循环就结束,如果循环结束都没有找到这个数,那就返回一个-1代表表中没有这个数。

更新:void update_list(list *l,Type oldData,Type newData)

       更新函数不需要返回值,传参的话就是表,原值以及需要修改的值,还是跟查找一样的使用循环的方法,找到需要修改的值就直接将这个值修改为更新后的值即可;在这里如果只想要更新查找到的第一个值的话就在循环里面加一个break即可,这样查找到第一个之后就会结束循环。

删除:void delete_list(list *l,Type data)

       对于顺序表的删除,我们有两个办法,但是都需要使用到循环;第一种方法是找到要删除的值之后就将它后面的每一个元素都往前移动一位,将这个删除的值直接覆盖掉;第二种方法也是覆盖,首先定义一个变量作为赋值下标,然后判断的条件是当元素不是我要删除的那个数时,就给数组中的数赋值,l->data[index++]=l->data[i];这里的index和i都是从0开始,对于相同的值,再赋值一遍也不会产生影响,但是要是这个数是我们要删除的数,那就不进行这一个赋值操作,这时index的值就不会增加,会停留在要删除的值的前一个,但是i每次都会自增,当下一次循环时,i所对应的元素就是要删除的元素的后一个元素,这时再将这个元素赋值给index下标对应的位置,就可以将要删除的元素覆盖掉;切记一定要在删除的同时count的值也要发生改变

遍历:void traverse_list(list *l)

       顺序表的遍历其实也就是数组的遍历,通过循环遍历数组再输出即可。

回收:void free_list(list **l)

       在回收的传参这里,我们只传这个表是不行的,我们需要释放这片空间地址,并且还需要将指向这片空间的指针指向空,因此必须传参传入指针的地址,也就是传一个二级指针,将这个空间free之后还需要将指针指向NULL,这样回收才算结束。

测试(主函数)

#include "seqlist.h"
int main(int argc, char *argv[])
{
    list *p = create_list();

    insert_list(p,1);
    insert_list(p,2);
    insert_list(p,3);
    puts("尾插入后");
    traverse_list(p);

    head_insert_list(p,4);
    head_insert_list(p,5);
    head_insert_list(p,6);
    puts("首插入后");
    traverse_list(p);

    printf("length=%d\n",length_list(p));
    whether_full(full_list(p));
    printf("3的下标为:%d\n",search_list(p,3));
    update_list(p,4,8);
    puts("将4更改为8后:");
    traverse_list(p);

    delete_list(p,5);
    puts("删除5之后:");
    traverse_list(p);

    puts("回收");
    free_list(&p);
    printf("p=%p\n",p);
    return 0;
} 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一.安装版本为19c的Oracle数据库管理系统(Oracle系列)

1.数据库版本信息&#xff1a; 版本信息&#xff1a; 或者直接由命令查出来&#xff1a; 2.操作系统的版本信息 3.安装包下载与上传 可以去oracle官网下载也可以从其他人的百度网盘链接中下载&#xff1a; 使用xftp工具或者其他的工具&#xff08;mobaxterm&#xff09;上传到l…

从 IDC 到云原生:稳定性提升 100%,成本下降 50%,热联集团的数字化转型与未来展望

作者&#xff1a;金峰&#xff08;项良&#xff09;、朱永林、赵世振&#xff08;寰奕&#xff09; 公司简介 杭州热联集团股份有限公司成立于 1997 年 10 月&#xff0c;是隶属杭州市实业投资集团的国有控股公司。公司专业从事国际、国内钢铁贸易黑色大宗商品及产业服务&…

说说软件工程中的“协程”

在软件工程中&#xff0c;协程&#xff08;coroutine&#xff09;是一种程序运行的方式&#xff0c;可以理解成“协作的线程”或“协作的函数”。以下是对协程的详细解释&#xff1a; 一、协程的基本概念 定义&#xff1a;协程是一组序列化的子过程&#xff0c;用户能像指挥家…

MinIO 的 S3 over RDMA 计划: 为高速人工智能数据基础设施设定对象存储新标准

随着 AI 和机器学习的需求不断加速&#xff0c;数据中心网络正在迅速发展以跟上步伐。对于许多企业来说&#xff0c;400GbE 甚至 800GbE 正在成为标准选择&#xff0c;因为数据密集型和时间敏感型 AI 工作负载需要高速、低延迟的数据传输。用于大型语言处理、实时分析和计算机视…

怀旧游戏打卡清单(TODO)

感觉忙碌了好久好久&#xff0c;真的好想休息一下。。 整理一下将来休息时候的打卡清单&#xff0c;不工作了去个海边狂打游戏&#xff0c;想想就惬意啊。当然&#xff0c;最好找个work from home&#xff0c;去海边找个酒店上班。挣钱休息两不误。。。 能不能实现另说&#xf…

《Python制作动态爱心粒子特效》

一、实现思路 粒子效果&#xff1a; – 使用Pygame模拟粒子运动&#xff0c;粒子会以爱心的轨迹分布并运动。爱心公式&#xff1a; 爱心的数学公式&#xff1a; x16sin 3 (t),y13cos(t)−5cos(2t)−2cos(3t)−cos(4t) 参数 t t 的范围决定爱心形状。 动态效果&#xff1a; 粒子…

使⽤MATLAB进⾏⽬标检测

目录 数据准备定义模型并训练用测试集评估性能推理过程⼀⾏代码查看⽹络结构⼀⾏代码转onnx结语 ⼈⽣苦短&#xff0c;我⽤MATLAB。 Pytorch在深度学习领域占据了半壁江⼭&#xff0c;最主要的原因是⽣态完善&#xff0c;⽽且api直观易⽤。但谁能想到现在MATLAB⽤起来⽐Pytorch…

word-毕业论文的每一章节的页眉单独设置为该章的题目怎么设置

在Microsoft Word中&#xff0c;为毕业论文的每个章节设置不同的页眉&#xff0c;通常需要使用“分节符”来分隔各个章节&#xff0c;然后在每个章节中单独设置页眉。以下是详细步骤&#xff1a; 使用分节符 插入分节符&#xff1a; 将光标放在每个章节的末尾&#xff08;注意…

【简历】25届江苏二本JAVA简历:本末倒置,重点部分格式错误,不重要的写了一堆

简历总体说明 校招的第一法则就是必须要确定校招层次。 开发岗分为大中小厂&#xff0c;不同的层次对学校背景、时间点、项目和考点的要求都不太一样&#xff0c;所以必须要确定就业的层次。 这是一个25届二本同学的JAVA简历。 最近不知道怎么回事&#xff0c;看两份简历都…

字母异位词分组(java)

题目描述 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单shilie 示例 1: 输入: strs ["eat", "tea", "tan", "ate", "n…

fpga spi回环

SPI设备间的数据传输之所以又被称为数据交换,是因为 SPI协议规定一个 SPI设备 不能在数据通信过程中仅仅只充当一个"发送者(Transmitter)“或者"接收者 (Receiver)”.在每个 Clock 周期内,SPI 设备都会发送并接收一个 bit 大小的数据(不管主 设备好还是从设备),相当于…

计算机网络-理论部分(二):应用层

网络应用体系结构 Client-Server客户-服务器体系结构&#xff1a;如Web&#xff0c;FTP&#xff0c;Telnet等Peer-Peer&#xff1a;点对点P2P结构&#xff0c;如BitTorrent 应用层协议定义了&#xff1a; 交换的报文类型&#xff0c;请求or响应报文类型的语法字段的含义如何…

路由器基本原理与配置

一 &#xff0c; 路由是什么&#xff1f; 从源主机到目标主机的转发过程&#xff1b; 二 &#xff0c; 路由器 &#xff08;1&#xff09;路由器的工作原理 路由器是一种三层设备&#xff0c;是使用IP地址寻址&#xff0c;实现从源IP到达目标IP地址的端到端的服务&#xff0c…

windows的WSL Ubuntu子系统重置root或其他用户的密码

思路&#xff1a;以管理员身份运行PowerShell&#xff0c;在命令行窗口重置密码 &#xff0c;不需要删除或重新安装Linux子系统。 1、以管理员身份运行PowerShell 2、用root用户启动Ubuntu&#xff0c;执行 wsl.exe --user root 3、重置密码&#xff0c;执行passwd username&…

autoDL微调训练qwen2vl大模型

autodl是一家GPU服务厂商&#xff0c;提供专业的GPU租用服务&#xff0c;秒级计费、稳定好用 先去autodl把官方的帮助文档看懂先 AutoDL帮助文档 autodl注册并登陆&#xff0c;充钱&#xff0c;根据自己的情况租用新实例 创建新实例后马上关机&#xff0c;因为有个省钱的办法…

9.2 使用haarcascade_frontalface_default.xml分类器检测视频中的人脸,并框出人脸位置。

1&#xff09;程序代码&#xff1a; # 2.使用haarcascade_frontalface_default.xml分类器检测视频中的人脸&#xff0c;并框出人脸位置 import cv2# 加载人脸检测的 Haar 级联分类器 face_cascade cv2.CascadeClassifier(./data/haarcascades/haarcascade_frontalface_defaul…

K8S containerd拉取harbor镜像

前言 接前面的环境 K8S 1.24以后开始启用docker作为CRI&#xff0c;这里用containerd拉取 参考文档 正文 vim /etc/containerd/config.toml #修改内容如下 #sandbox_image "registry.aliyuncs.com/google_containers/pause:3.10" systemd_cgroup true [plugins.…

ARM64环境部署EFK8.15.3收集K8S集群容器日志

环境规划 主机IP系统部署方式ES版本CPU架构用户名密码192.168.1.225Ubuntu 22.04.4 LTSdockerelasticsearch:8.15.3ARM64elasticllodyi4TMmZD ES集群部署 创建持久化目录(所有节点) mkdir -p /data/es/{data,certs,logs,plugins} mkdir -p /data/es/certs/{ca,es01}服务器…

前端小练习——星辰宇宙(JS没有上限!!!)

前言&#xff1a;在刚开始学习前端的时候&#xff0c;我们会学习到前端三件套中的JavaScript&#xff0c;可能那时候读者没有觉得JavaScript这个语言有多么的牛逼&#xff0c;本篇文章将会使用一个炫酷的案例来刷新你对JavaScript这个语言的认知与理解。 ✨✨✨这里是秋刀鱼不做…

图文教程 | 2024年IDEA安装使用教程,JDK简易下载方法

前言 &#x1f4e2;博客主页&#xff1a;程序源⠀-CSDN博客 &#x1f4e2;欢迎点赞&#x1f44d;收藏⭐留言&#x1f4dd;如有错误敬请指正&#xff01; 目录 一、IDEA安装 二、激活 三、JDK安装 四、JDK环境配置 五、验证 一、IDEA安装 进入官网下载&#xff1a; Other…