C语言实现顺序表与链表创建

news2025/2/3 10:01:27

线性表

用于存储若干相同属性元素的有序序列称为线性表

线性表特征:

  1. 存在唯一的第一个元素;
  2. 存在唯一的最后一个元素;
  3. 除第一个序列的每一个元素元素都有一个前驱元素,后一个都有一个后继元素。

顺序表

线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素, 这种表示
也称作线性表的顺序存储结构,称为顺序表。

顺序表满足等差数列:LOC(a;+ 1) = LOC(a;) + i

线性表的第l个数据元素a;的存储位置为: LOC(a;) = LOC(a1) + (i - I) x i

在这里插入图片描述
LOC(a1)是线性表的第一个数据元素a1 的存储 存储地址位置,·通常称作线性表的起始位置或基地址, 表中相邻的元素a; 和a; + I 的存储位置LOC(a;)和LOC(a;+ 1) 是相邻的。因此顺序表中最要的就是起始地址,知道起始地址可以计算任何位置的值。只要确定了存储线性表的起始位置, 线性表中任一数据元素都可随机存取, 所以线性表的顺序存储结构是一种随机存取的存储结构

在这里插入图片描述

数组是c语言中比较特殊一种数据结构,不同于int,float类型,数组的若干个基本数据类型的集合,可以存储多个基本数据类型变量的数据类型。数组的本质也是一个数据类型,只不过会存错多个变量的值。

顺序表也是基于数组实现,可以是动态分配存储空间,也可以是定值表空间。

对于顺序表的数据结构需要对数据实现增删改查的操作。(数组只能用户存储,无操作。如果顺序表基于定值数组实现,那么顺序表至少包含两个信息,表的长度和表的数据。

  1. 添加

数组可以通过循环赋值来实现,顺序表需要试下对数组的操作,在某个位置添加元素,或者通过尾插法,头插法添加数据。

尾部插入数据需要将数组长度加一,并将新的的数组元素的值赋值;在数组任意部位插入值需要将数组该位置之后的元素全部后移一位,给新增的元素空出位置。

  1. 修改

修改就是重新赋值,就是重新个表的指定位置重新赋值。

  1. 删除

删除和添加类似,需要先找到指定位置的元素,在该位置处将后一个元素赋值给前一个元素即该位置处元素前移一位。

  1. 查找

查找需要遍历表的所有元素,直到找到所需元素的位置,遍历即可。

变量是用来信息的,语言间信息分成了若干类即基本数据类型,如int,float,long等,但是这些只能存储一个信息,对于某一类信息的集合无法存储,于是有了数组的数据类型,用于存储同一类型的变量。顺序表就是实现对数组类型的操作。

#include<stdio.h>
#define MAXSIZE 10

//结构体定义
struct List{
	int item[MAXSIZE];
	int length;
};
//结构体声明
typedef struct List SeqList;

//方法申明
int InitList(SeqList *list);
int AddElem(SeqList *list ,int e);
int InsertElem(SeqList *list,int i,int e);
int GetElem(SeqList *list,int e);
int DeleteElem(SeqList *list,int n);
void displayElem(SeqList list);

int main(){
	SeqList a;
	InitList(&a);
	AddElem(&a,2);
	AddElem(&a,8);
	InsertElem(&a,1,10);
	InsertElem(&a,3,20);
	//InsertElem(&a,8,200);  //插入失败但是没有提示
	displayElem(a);
	
	int tmp = GetElem(&a,8);
	printf("取出的值位次为:%d\n",tmp);
	DeleteElem(&a,1);
	displayElem(a);
}

// list -> item  <===> (*list).item

int InitList(SeqList *list){
	list -> item[0] = 0;
	list -> length = 0;
	if (!list) return 0;
	return 1;
}


int AddElem(SeqList *list ,int e){
	//判断表是否满(略)
	int i = list->length ;
	list ->item[i] = e;
	list->length ++;
	
}
int InsertElem(SeqList *list,int i,int e){
	if(i<1 || i> (list ->length)){
		//TODO
		return 0; 
	}
	//判断表是否满了 (略)
	int j;
	for(j=(list->length);j>i-1;j--){
		list->item[j] = list->item[j-1];
	}
	list->item[i-1] = e;
	list->length +=1;
	return i;
}

int GetElem(SeqList *list,int e){
	int i;
	for(i=0;i<(list->length);i++){
		if(list->item[i] == e){
			return i+1;
		}
	}
	return 0;
	
}

//按位次删除
int DeleteElem(SeqList *list,int n){
	if (n<1 || n>list->length){
		return 0;
	}
	for(int i=n-1;i<list->length;i++){
		list->item[i] = list->item[i+1]; 
	}
	list->length--;
}

void displayElem(SeqList list){
	int i;
	for(i= 0;i<= list.length-1;i++){
		//TODO
		printf("%d,",list.item[i]);
	}
	printf("\n");
}

如上的代码就是实现顺序表的操作,其中list -> item <===> (*list).item

上述代码是定值表,也是就该顺序表最多课存储MAXSIZE个元素,若想实现动态的数组,需要将存储数据的变量定义为指针变量。

#include<stdio.h>
#include <stdlib.h>
#define SIZE 5

struct ListTo{
	int *item;
	int length;
	int size;
};
typedef struct ListTo List;

int main(){
	List t;
	t.item=(int*)malloc(SIZE*sizeof(int));
	
	//第一次赋值
	t.item[0] = 1;
	t.size = SIZE;
	printf("第一次赋值后表的容量为%d\n",t.size);
	
	for(int i=1;i<8;i++){
		//TODO
		*(t.item+i) = i;
	}
	for(int i=1;i<8;i++){
		//TODO
		printf("%d\n", *(t.item+i));
		
	}
	//int *p 
	printf("8次赋值后表的长度为%d\n",sizeof(t.item)/sizeof(int));
		
}

在动态内存分配是急需要注意顺序表包含两个长度一个是容积一个是当前长度,当存储的数据超过表的长度时数组会动态分配一个新双倍长度的数组。

链表

线性表链式存储结构的特点是:用一组任意的存储单元存储线性表的数据元素。对于任意一个数据中元素来说除了要存储数据之外,还需要存储直接后继元素的节点信息,使整个数据串联起来。

在链表中每个存储存储数据和后继信息的元素称为结点,包含数据域与指针域。链表的每个结点中只包含一个指针域,故又称线性链表或单链表。

根据链表结点所含指针个数、指针指向和指针连接方式,可将链表分为单链表、循环链表、
双向链表、二叉链表、十字链表、邻接表、邻接多重表等。其中单链表、循环链表和双向链表用
千实现线性表的链式存储结构,其他形式多用于实现树和图等非线性结构。

取自教材数据结构__C语言版__第2版
在这里插入图片描述
可见单链表由头节点唯一确定。

上述对顺序表的操作都是需要使用指针的,原因在于在参数传递是直接使用变量名为值传递,将变量重新克隆一份,因此形参是实参的备份,经过函数操作后实参并不会发生变化,还是原样。使用指正作为形参时,传递的是实参的地址,对地址的操作会改变实参的值,因此指针作为参数函数的执行会改变实参的值。

在这里插入图片描述
在单链表中每一个结点都是一个变量,共同存在于内存中,通过指针信息联系起来。

  • 单链表结点定义
typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,LinkList;

每个结点是个结构体变量,包含数据和一个后继结点指针,指针也是结点类型,递归用法。

LNodeLinkList是同一类型,在逻辑上定义前者为结点,后者为整个链表。

  • 链表的创建

根据之前的图可以看出,链表的结点本质是不同的变量,变量通过记录地址连接,变量是同一级别的n个。

注意不能通过方法创建链表,因为链表是若干个同级的结点通过地址连接,在方法中创建的是局部变量,随方法结束而销毁。

#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0

typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,LinkList;


int main(){
	//生成链表
	LinkList* L = (LNode*)malloc(sizeof(LNode));
	//链表必须存在头指针
	LNode* head = L;
	printf("输入添加元素的个数:\n");
	int e,length;
	scanf("%d",&length);
	for(int i=0;i<length;i++){
		//创建新结点
		LNode* node = (LNode*)malloc(sizeof(LNode));
		printf("请输入:");
		scanf("%d",&e);
		node->data = e;
		node->next =NULL;
		head->next = node;
		head = node;     //头指正记录当前最后一个结点
	}	
	//打印链表
	while(L->next != NULL){
		L=L->next;
		printf("%d",L->data);
	}
}

链表由头结点或者首元结点唯一确定,通过记录的地址依次寻址。上述代码通过尾插法创建链表。

LinkList* L = (LNode*)malloc(sizeof(LNode))创建了一个链表指针。

LNode* head = L创建头结点,实际上就是链表指针,在逻辑上充当头结点,作用是记录当前结点,并传递结点地址。(也可以直接将链表指针当做做头结点,一样的意义)。

LNode* node = (LNode*)malloc(sizeof(LNode))每次输入创建新的结点,并将头结点后继结点地址指向新结点head->next = node,在头结点之后就插入了一个新的结点。

在这里插入图片描述
此时链表中存在两个结点头结点和A结点,如再插入一个结点就有两种方案:(1)将A结点后继结点指向新结点地址即NULL改为新结点(后插法或尾插法);(2)新结点的后继结点地址改为头结点后继节点地址,头结点后继结点改为新结点地址(前插法或头插法)。

(1)后插法
在这里插入图片描述

head = node非常关键,执行该程序使的head重新指向新的地址,实际上头结点head是一个逻辑上的头结点,依次移想新结点地址,而原地址就为存放结点信息的链表结点。

在这里插入图片描述

在后插法中,每次逻辑上的头结点移动到新结点地址,原地址作为存储信息的独立结点。

在这里插入图片描述

在这里插入图片描述

[算法步骤】
1.创建一个只有头结点的空链表。
2.尾指针r初始化, 指向头结点。
3.根据创建链表包括的元素个数n, 循环n次执行以下操作:
• 生成一个新结点p;
• 输入元素值赋给新结点
p 的数据域;
• 将新结点p 插入到尾结点r之后;
• 尾指针r指向新的尾结点*p。

(2)前插法

在前插法中头结点是不变的,改变的只有头结点的指针域。每有一个新的结点就将新结点的指针域指向头结点的指针域,头结点的指针域该新结点,将头结点初始指针域设置为NULL,那么从结点生成开始后续结点的指针域都存储了前一个结点地址。

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

typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,LinkList;

int main(){
	//创建空链表
	LinkList* L = (LinkList*)malloc(sizeof(LinkList));
	L->next =NULL;
	for(int i=0;i<5;i++){
		LNode* node = (LNode*)malloc(sizeof(LNode));
		node->data = i+1;
		node->next = L->next;
		L->next = node;
	}
	while(L->next != NULL){
		L = L->next;
		printf("%d",L->data);
		
	}
}

在这里插入图片描述

【算法步骤】
1.创建一个只有头结点的空链表。
2.根据待创建链表包括的元素个数n, 循环n次执行以下操作:
• 生成一个新结点p;
• 输入元素值赋给新结点
p的数据域;
• 将新结点*p插入到头结点之后。

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

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

相关文章

智慧矿山成行业新趋势,千寻位置助力企业数字化转型

随着政策推动和科技发展&#xff0c;智慧矿山已成为矿业行业的趋势和未来的方向。 智慧矿山就是以矿山数字化、信息化为前提和基础&#xff0c;对矿山生产、人员健康与安全、技术支持与后勤保障等进行主动感知、自动分析、快速处理&#xff0c;最终实现安全矿山、无人矿山、高效…

java设计模式之:访问者模式

前言 关于设计模式&#xff0c;我们得结合生活中的案例来学习&#xff1b;最近我在网上也看了不少文章&#xff0c;今天想跟大家分享一下关于访问者模式的一些知识&#xff0c;先来看一个简单的案例吧。 相信大家都去过医院&#xff0c;看完病&#xff0c;医生都会给我们开一…

Linux重定向和缓冲区理解

本文已收录至《Linux知识与编程》专栏&#xff01; 作者&#xff1a;ARMCSKGT 演示环境&#xff1a;CentOS 7 重定向和缓冲区理解 前言正文文件描述符重定向重定向原理重定向命令重定向函数 缓冲区缓冲区是什么&#xff1f;缓冲区刷新策略内核缓冲区与普通缓冲区 最后 前言 前…

5款超级好用的开发效率工具,建议收藏!

大家好&#xff01;高温天气切莫太累&#xff0c;注意防暑休闲开胃(&#xff5e; o &#xff5e;)~zZ 人口过剩的时代&#xff0c;劳动力也追求高性价比。好的工具&#xff0c;能够帮助我们更高效地完成工作&#xff0c;节省时间&#xff08;摸鱼时间&#xff09;和精力&#…

吐血整理,性能测试方法与步骤详细,进阶测试之路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 总体方向&#xf…

HTML5 sessionStorage会话存储

sessionStorage 是HTML5新增的一个会话存储对象&#xff0c;用于临时保存同一窗口(或标签页)的数据&#xff0c;在关闭窗口或标签页之后将会删除这些数据。本篇主要介绍 sessionStorage(会话存储)的使用方式。包括添加、修改、删除等操作。 目录 1. 介绍 1.1 说明 1.2 特点 1…

微信小程序实现一个文字展开收起功能

1.0 需求背景 需求很常见&#xff0c;就是当一行文字过多时&#xff0c;显示省略号&#xff0c;然后显示展开两个字&#xff0c;点击&#xff0c;文字完全展示开&#xff0c;点击收起&#xff0c;回到省略形式&#xff0c;如下图 2.0 需求分析 有了上图&#xff0c;应该能更好…

2023亚马逊云科技中国峰会之Serverless

序言 Amazon Web Services&#xff0c;是Amazon.com推出的一系列云计算服务。 它提供了一系列的基础设施服务、平台服务和软件服务&#xff0c;希望可以帮助我们更轻松地构建和管理基于云的应用程序。 今天来学习一下 Serverless 本文会介绍以下六个模块&#xff1a; 为什么会…

RocketMq 同组消费者 自动设置InstanceName

RocketMq 同组消费者 自动设置InstanceName 一、背景二、处理方法三、源码分析四、总结 一、背景 同组多于1个消费者&#xff0c;如果没单独设置instanceName,默认为DEFAULT。启动时会报如下错误&#xff1a; org.apache.rocketmq.client.exception.MQClientException: The co…

物联网工业触摸屏与防火墙的安全协作

1 前言 随着物联网技术的快速发展&#xff0c;物联网HMI不仅需要提供SCADA级功能库和控件库&#xff08;点击查看物联网HMI功能库和控件库的详细介绍&#xff09;&#xff0c;还需要具备强大的安全性能。虹科物联网HMI内置防火墙功能&#xff0c;识别和阻止未经授权的访问&…

PCI Express --- LTSSM

目录 1. 链路训练和状态机 1.1 Detect 状态 1.1.1 Detect.Quiet 子状态 1.1.2 Detect.Active 子状态 1.2 Polling 状态 1.2.1 Polling.Active 子状态 1.2.2 Polling.Compliance 子状态 1.2.2 Polling.Configuration 子状态 1.2.3 Polling.Speed 子状态 1.3 Configuration 状…

性能测试超细总结,如何才能做到有效压测?性能压测看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 目标制定以及业务…

java SSM 游戏资讯系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM 游戏资讯系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和 数据库&#xff0c;系统主要采用B…

3ds Max - Pivot Painter Tool

很久之前的笔记&#xff0c;整理归档&#xff1b; Pivot Painter Tool是3dsMax中的插件&#xff0c;主要是辅助将Mesh中每个Element生成自己的Pivot Position&#xff0c;方便如使用World Position Offset对每个Element进行精确控制&#xff0c;导入使用Pivot Painter Tool工具…

深入理解Linux虚拟内存管理(七)

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核 Linux 设备驱动程序 Linux设备驱动开发详解 深入理解Linux虚拟内存管理&#xff08;一&#xff09; 深入理解Linux虚拟内存管理&#xff08;二&#xff09; 深入理解Linux虚拟内存管理&#xff08;三&#xff09; 深入理…

Linux系统和Windows系统下Python2代码转换为Python3代码工具使用指南

简介 本文主要介绍Linux系统和Windows系统下Python2代码转换为Python3代码工具2to3.py或2to3指令使用指南。 项目场景及问题描述 Python2的最后一个版本是2.7&#xff0c;在2020年彻底停止支持。有些环境不方便同时安装Python2和Python3&#xff0c;或者在使用Python3的环境…

【JVM】JVM 垃圾回收算法

文章目录 前言标记清除&#xff08;Mark-Sweep&#xff09;介绍优缺点 复制&#xff08;拷贝 Copying&#xff09;介绍优缺点 标记整理&#xff08;Mark-Compact&#xff09;介绍优缺点 前言 目前JVM中有三种常见的垃圾回收算法&#xff0c;分别是&#xff1a;标记清除、标记整…

Matter实战系列-----1.软硬件开发环境搭建

一、硬件方面 我使用的是一套xG21 BRD4180B和两块xG24 BRD4187C,如下图&#xff1a; 1.1 RCP&#xff1a; 芯片型号EFR32MG21A020F1024IM32 1.2 Matter Light/Switch over Thread&#xff1a; 芯片型号EFR32MG24B220F1536IM48 1.3 蓝牙5.0 USB dongle 注意由于Linux对蓝牙…

阿里、字节、网易面试必考,黑马【爆火】微服务项目发布

最近&#xff0c;收到一位粉丝投稿&#xff0c;他说&#xff1a;“阿里三面凉凉了&#xff0c;输在了微服务上。” 在看到微服务的面试题后&#xff0c;整个人都是懵的&#xff0c;发现没有经验的自己&#xff0c;一窍不通。 如今&#xff0c;微服务已经成为Java开发者必备的…

深入篇【C++】string类的常用接口介绍:标准库中的string类 【万字总结】

深入篇【C】string类的常用接口介绍&#xff1a;标准库中的string类 Ⅰ.string类介绍Ⅱ.string类的常用接口①.string类对象的常用构造1.string()2.string(const char*ch)3.string(const string& str)4.string(size_t n,char c)5.string(const string& str,size_t pos,…