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

基本概念:

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 1if(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;}
#elseint 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 0for(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 1int 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.mzph.cn/diannao/61175.shtml

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

相关文章

Springboot+Vue的项目搭建(一)

一、JDK配置环境变量 1、在官网下载jdkJava Downloads | Oracle 中国 2、下载之后双击安装。 3、配置环境变量&#xff0c;做法&#xff1a;此电脑->右键->属性->高级系统设置 然后点击确定即可 点击winr java -version 检查一下是否配置成功 二、maven包管理器…

使用 JavaScript 制作 To-Do List

使用 JavaScript 制作 To-Do List 本文记录了使用 HTML、CSS 和 JavaScript 制作一个简单的 To-Do List 网页的全过程&#xff0c;包含功能描述、代码实现以及优化方向。 **&#x1f389;&#x1f389;&#x1f389;欢迎来到我的博客,我是一名自学了2年半前端的大一学生,熟悉的…

esp32c3开发板通过micropython的mqtt库连MQTT物联网消息服务器

MQTT介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的消息协议&#xff0c;旨在设备之间进行通信&#xff0c;尤其是在网络条件较差的情况下。MQTT v3.1.1 和 MQTT v5 是该协议的两个主要版本。 MQTT v3.1.1&#xff1a; 优点&#xff…

stm32启动过程解析startup启动文件

1.STM32的启动过程模式 1.1 根据boot引脚决定三种启动模式 复位后&#xff0c;在 SYSCLK 的第四个上升沿锁存 BOOT 引脚的值。BOOT0 为专用引脚&#xff0c;而 BOOT1 则与 GPIO 引脚共用。一旦完成对 BOOT1 的采样&#xff0c;相应 GPIO 引脚即进入空闲状态&#xff0c;可用于…

数据结构查找-哈希表(开发地址法+线性探测法)+(创建+查找+删除代码)+(C语言代码)

#include<stdlib.h> #include<stdio.h> #include<stdbool.h> #define NULLKEY -1//单元为空 #define DELKEY -2//单元内容被删除 #define M 20 typedef struct {int key;//关键字int count;//统计哈希冲突探测次数 }HashTable; //插入到哈希表 void InsertHT…

〔 MySQL 〕数据类型

目录 1.数据类型分类 2 数值类型 2.1 tinyint类型 2.2 bit类型 2.3 小数类型 2.3.1 float 2.3.2 decimal 3 字符串类型 3.1 char 3.2 varchar 3.3 char和varchar比较 4 日期和时间类型 5 enum和set mysql表中建立属性列&#xff1a; 列名称&#xff0c;类型在后 n…

数据库审计工具--Yearning 3.1.9普民的使用指南

1 页面登录 登录地址:18000 &#xff08;不要勾选LDAP&#xff09; 2 修改用户密码 3 DML/DDL工单申请及审批 工单申请 根据需要选择【DML/DDL/查询】中的一种进行工单申请 填写工单信息提交SQL检测报错修改sql语句重新进行SQL检测&#xff0c;如检测失败可以进行SQL美化后…

tcp 超时计时器

在 TCP&#xff08;传输控制协议&#xff09;中有以下四种重要的计时器&#xff1a; 重传计时器&#xff08;Retransmission Timer&#xff09; 作用&#xff1a;用于处理数据包丢失的情况。当发送方发送一个数据段后&#xff0c;就会启动重传计时器。如果在计时器超时之前没有…

前端(4)——demo分享

这两天需要用HTML、CSS和js简单组合一个html网页用于展示一些数据内容&#xff0c;这是我简单组合别人的一些文件形成的简单demo&#xff0c;大家也可以拿过去使用。 登录界面&#xff1a; 场景选择界面&#xff0c;有五个场景&#xff0c;每个场景中都需要展示一些特定的数据…

Java-空链处理

什么是 null 在 Java 中&#xff0c;null 是一个非常常见的关键字&#xff0c;用于表示“没有值”或“空”。然而&#xff0c;对于初学者来说&#xff0c;null 的本质可能会感到有些困惑。在本文中&#xff0c;我们将详细探讨 null 在 Java 中的含义和使用。 在 Java 中&…

PyTorch使用教程-深度学习框架

PyTorch使用教程-深度学习框架 1. PyTorch简介 1.1-什么是PyTorch ​ PyTorch是一个广泛使用的开源机器学习框架&#xff0c;特别适合深度学习的应用。它以其动态计算图而闻名&#xff0c;允许在运行时修改模型&#xff0c;使得实验和调试更加灵活。PyTorch提供了强大的GPU加…

供应链管理、一件代发系统功能及源码分享 PHP+Mysql

随着电商行业的不断发展&#xff0c;传统的库存管理模式已经逐渐无法满足市场需求。越来越多的企业选择“一件代发”模式&#xff0c;即商家不需要自己储备商品库存&#xff0c;而是将订单直接转给供应商&#xff0c;由供应商直接进行发货。这种方式极大地降低了企业的运营成本…

Pr:音频过渡

Adobe Premiere Pro 自带一组共三个音频过渡 Audio Transitions效果。 对音频剪辑之间应用交叉淡化 Crossfade过渡&#xff0c;操作方式类似于应用视频过渡效果。 对于交叉淡化&#xff0c;要保证前剪辑的出点之后及后剪辑的入点之前有足够的预留内容&#xff08;也称“手柄”&…

前端页面开发步骤详解

目录 前言1. 页面搭建1.1 HTML 标签结构1.2 CSS 样式设计 2. 数据绑定与事件处理2.1 数据绑定2.2 表单校验 3. 调用后台接口3.1 接口文档与工具封装3.2 参数传递与接口调用 结语 前言 在前端开发过程中&#xff0c;从页面搭建到与后台接口对接是一个必不可少的完整流程。无论是…

A037-基于Spring Boot的二手物品交易的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

两行命令搭建深度学习环境(Docker/torch2.5.1+cu118/命令行美化+插件),含完整的 Docker 安装步骤

深度学习环境的配置过于繁琐&#xff0c;所以我制作了两个基础的镜像&#xff0c;希望可以帮助大家节省时间&#xff0c;你可以选择其中一种进行安装&#xff0c;版本说明&#xff1a; base 版本基于 pytorch/pytorch:2.5.1-cuda11.8-cudnn9-devel&#xff0c;默认 python 版本…

EXCEL延迟退休公式

如图&#xff1a; A B为手工输入 C2EOMONTH(A2,B2*12) D2EOMONTH(C2,IF(C2>DATEVALUE("2025-1-1"),INT((DATEDIF(DATEVALUE("2025-1-1"),C2,"m")4)/4),0)) E2EOMONTH(A2,B2*12IF(EOMONTH(A2,B2*12)>DATEVALUE("2025-1-1"),INT(…

区块链技术在数据安全中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 区块链技术在数据安全中的应用 区块链技术在数据安全中的应用 区块链技术在数据安全中的应用 引言 区块链技术基础 1.1 区块链的…

GIT 入门详解指南

前言&#xff1a; 注&#xff1a;本博客仅用于记录本人学习过程中对git的理解&#xff0c;仅供学习参考&#xff0c;如有异议请自行查资料求证 安装 使用git之前必须完成git的安装&#xff0c;Git 目前支持 Linux/Unix、Solaris、Mac和 Windows 平台上运行 git 安装教程 基本…