数据结构--顺序表,链表,双向链表

数据结构的学习,从浅到深的学习,在了解他们的概念后,当然要明白他们分别是怎么实现的,同时通过对比来了解他们的异同点。

一.数据结构

1.1 什么是数据结构

所谓数据结构,拆开来讲,就是数据和结构。

数据:所有能输入到计算机中并且能被程序识别和处理的符号集合,包括数值数据(整数,实数),非数值型数据(图形,声音,文字)。

结构:数据之间的关系

数据对象:相同特性的数据元素的集合

数据元素:数据的基本单位

数据项:数据元素的最小单位

关系图:

最基础的数据结构:数组

1.2 数据结构三要素

1.2.1逻辑结构

集合:数据和元素之间没有关系

线性结构:数据和元素之间一对一

树结构:数据和元素之间一对多

图结构:数据和元素之间多对多

1.2.2 物理结构(存储结构)

顺序存储结构

链接存储结构

1.2.3 数据的运算

二.线性表

我们先引入一个概念:线性表。线性表是n个具有相同特性的数据元素的有限序列

常见的线性表:循序表,链表,栈,队列,字符串...

1. 顺序表

顺序表的底层结构是数组

1.1顺序表的实现

1.1.1静态顺序表和动态顺序表
静态顺序表
//给定的数组长度,如果不够,会导致后续的数据保存失败
#define N 100//长度通过宏的方式进行优化  使用100太过于麻烦,通过N代替100   
typedef int SLDataType;//存储的也不一定是整型   typedef a b;就是用b代替a//
//struct SeqList
//{
	//int a[100];//固定的长度    不代表是一个有效的数据   
	//int a[N];//优化上一行
	//SLDataType a[N];//优化上一行
	//int size;//有效的数据的个数
//};

 

动态顺序表
struct SeqList
{
	SLDataType* arr;//存储数据的底层结构
	int capacity;//记录顺序表的空间大小  即申请到的实际空间大小  有空间不一定有实际数据
	int size;//记录顺序表当前有效的数据个数
	//capacity是最大空间,size是已经使用了的空间
};

静态顺序表就是给定长度的数组int arr[10]

动态顺序表就是动态设置数组长度int *arr

1.1.2 初始化和销毁

这里的SL* ps是数组指针

头文件SeqList.h中定义

void SLInit(SL* ps);

 SeqList.c的代码

//初始化和销毁
void SLInit(SL* ps)
{
	ps->arr = NULL;//ps.arr = NULL;
	ps->size = ps->capacity = 0;//ps.size = ps`.capacity = 0;
}
void SLDestroy(SL* ps)
{
	assert(ps);
	if (ps->arr)
	{
		free(ps->arr);
	}
    ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

 初始化,将结构体里面的数组置为NULL,值变量(有效数据个数,空间大小)置为0

1.1.3打印

头文件SeqList.h中定义

void SLPrint(SL* ps);//打印  *ps是为了保持接口一致性

SeqList.c的代码

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

头文件SeqList.h中定义

//扩容
//void SLCheckCapacity(SL* ps)

 SeqList.c的代码

//void SLCheckCapacity(SL* ps)//扩容判断
//{
//	if (ps->size == ps->capacity)
//	{
//		//ps->arr = (SLDataType*)realloc(ps->arr, 2 * ps->capacity);有问题 在上面capacity已经赋值为0
//		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//创建一个新的变量newcapacity
//		//如果capacity是0,那么值是4 4是比特的意思		
//       //ps->arr = (SLDataType*)realloc(ps->arr, newcapacity);
//		SLDataType* tmp = ps->arr = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
//		//临时指针指向开辟的空间
//		if (tmp == NULL)
//		{
//			perror("realloc fail!");
//			exit(1);//扩容失败直接中断程序
//		}
//		//扩容成功
//		//free(ps->arr);//此时arr里面已经有了空间,要先释放
//		//不对,这是realloc做的事情
//		ps->arr = tmp;
//		ps->capacity = newcapacity;//此时容量已经变了,变成了newcapacity
//	}
//}
1.1.5头插/尾插

头文件SeqList.h中定义

//顺序表的头部/尾部插入
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);

SeqList.c的代码

//顺序表的头部/尾部插入
void SLPushBack(SL* ps, SLDataType x) {
	//断言--粗暴的解决方式
	//assert(ps != NULL);
	assert(ps);

	//if判断--温柔的解决方式
	//if (ps == NULL) {
	//	return;
	//}

	//空间不够,扩容
	SLCheckCapacity(ps);

	//空间足够,直接插入
	ps->arr[ps->size++] = x;
	//ps->size++;
}

void SLPushFront(SL* ps, SLDataType x) {
	assert(ps);

	//判断是否扩容
	SLCheckCapacity(ps);

	//旧数据往后挪动一位
	for (int i = ps->size; i > 0; i--) //i = 1
	{
		ps->arr[i] = ps->arr[i - 1]; //ps->arr[1] = ps->arr[0]
	}
	ps->arr[0] = x;
	ps->size++;
}

1.1.6头删/尾删

头文件SeqList.h中定义

//顺序表的头部/尾部删除
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);

SeqList.c的代码

//顺序表的头部/尾部删除
void SLPopBack(SL* ps) {
	assert(ps);
	assert(ps->size);

	//顺序表不为空
	//ps->arr[ps->size - 1] = -1;
	ps->size--;
}

void SLPopFront(SL* ps) {
	assert(ps);
	assert(ps->size);

	//不为空执行挪动操作
	for (int i = 0; i < ps->size-1 ;i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}
1.1.7在指定位置之前插入数据/删除指定位置数据

头文件SeqList.h中定义

/指定位置之前插入数据
//删除指定位置数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);

SeqList.c的代码

//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x) {
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	
	SLCheckCapacity(ps);

	//pos及之后的数据往后挪动一位,pos空出来
	for (int i = ps->size; i > pos ;i--)
	{
		ps->arr[i] = ps->arr[i - 1]; //ps->arr[pos+1] = ps->arr[pos]
	}
	ps->arr[pos] = x;
	ps->size++;
}

//删除指定位置数据
void SLErase(SL* ps, int pos) {
	assert(ps);
	assert(pos >= 0 && pos < ps->size); 

	//pos以后的数据往前挪动一位
	for (int i = pos;i < ps->size-1;i++)
	{
		ps->arr[i] = ps->arr[i + 1];//ps->arr[i-2] = ps->arr[i-1];
	}
	ps->size--;
}
1.1.8在顺序表中查找x

头文件SeqList.h中定义

//查找
int SLFind(SL* ps, SLDataType x);

SeqList.c的代码

在顺序表中查找x
//int SLFind(SL* ps, SLDataType x)
//{
//	for (int i = 0; i < ps->size; i ++)
//	{
//		if (ps->arr[i] == x)
//		{
//			return i;
//		}
//	}
//	return -1;
//}

2. 链表

链表是由一个一个节点组成的

一个节点包括数据和指针

2.1单向链表

typedef int SLTDataType;
//链表由节点组成
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
} SLTNode;

2.2.1头插/尾插
//尾插
SLTNode* SLTBuyNode(SLTDataType x)
{
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;

    return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);//*pphead是指向第一个节点的地址
    //SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    //newnode->data = x;
    //newnode->next = NULL;
//    //建立新节点
    SLTNode* newnode = SLTBuyNode(x);
//    //链表为空,新节点作为phead
    if (*pphead == NULL)
    {
        *pphead = newnode;
        return;
    }
//    //链表不为空,找尾节点
//    //遍历链表,建立一个临时指针

    SLTNode* ptail = *pphead;
    while (ptail->next)
    {
        ptail = ptail->next;
    }
    //跳出循环,说明此时的ptail就是尾节点
    ptail->next = newnode;
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);
    SLTNode* newnode = SLTBuyNode(x);

    //newnode *pphead
    newnode->next = *pphead;
    *pphead = newnode;
}

2.2.2头删/尾删
//尾删
void SLTPopBack(SLTNode** pphead)
{
    assert(pphead);
    assert(*pphead);//链表不能为空

    //链表只有一个节点,有多个节点
    if ((*pphead)->next == NULL)
    {
        free(*pphead);
        *pphead = NULL;
        return;
    }
    SLTNode* ptail = *pphead;
    SLTNode* prev = NULL;
    while (ptail->next)
    {
        prev = ptail;
        ptail = ptail->next;
    }
    prev->next = NULL;
    //销毁尾节点
    free(ptail);
    ptail = NULL;
}
/头删
void SLTPopFront(SLTNode** pphead)
{
    assert(pphead);
    //链表不能为空
    assert(*pphead);

    //让第二个节点成为新的头
    //把旧的头节点释放掉
    SLTNode* next = (*pphead)->next;
    free(*pphead);
    *pphead = next;
}

2.2.3查找
//查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);

    //遍历链表
    SLTNode* pcur = *pphead;
    while (pcur)//等价于pcur != NULL
    {
        if (pcur->data == x)
        {
            return pcur;
        }
        pcur = pcur->next;
    }
    //没有找到
    return NULL;
}
2.2.4在指定位置之前插入数据/在指定位置之后插入数据
//在指定位置之前插入数据
void SLTInsertBefore(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    assert(pphead);
    assert(pos);
    //要加上链表不能为空  pos不能为空,而pos在链表里面,因此链表也不能为空
    assert(*pphead);
    //pos刚好是头节点
    if (pos == *pphead)
    {
        //头插
        SLTPushFront(pphead, x);
        return;
    }
    //pos不是头节点
    SLTNode* prev = *pphead;//建立一个临时地址
    SLTNode* newnode = SLTBuyNode(x);//申请一个新节点
    while (prev->next != pos)
    {
        prev = prev->next;
    }
    //prev->next->newnode
    prev->next = newnode;
    newnode->next = pos;

}
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
    assert(pos);

    SLTNode* newnode = SLTBuyNode(x);//申请新节点

    //pos newnode pos->next
    newnode->next = pos->next;
    pos->next = newnode;
}

2.2.5删除pos节点/删除pos之后的节点
//删除指定位置的节点pos
void SLTEraseBefore(SLTNode** pphead, SLTNode* pos)
{
    assert(pphead);
    assert(*pphead);
    assert(pos);
    //pos刚好是头节点,没有前驱节点,执行头删
    if (*pphead == pos)
    {
        //头删
        SLTPopFront(pphead);
        return;
    }
        
    SLTNode* prev = *pphead;
    while (prev->next != pos)
    {
        prev = prev->next;
    }
    //prev pos pos->next
    prev->next = pos->next;
    free(pos);
    pos = NULL;


}

/删除指定位置之后的节点pos
void SLTEraseAfter(SLTNode* pos)
{
    assert(pos);
    assert(pos->next);
    //pos  pos->next  pos->next->next
    SLTNode* del = pos->next;

    pos->next = pos->next->next;
    free(del);
    del = NULL;
}
2.2.6打印/销毁链表
void SLTPrint(SLTNode* phead) 
{
    SLTNode* pcur = phead;//pcur是一个临时指针
    while (pcur)
    {
        printf("%d->", pcur->data);
        pcur = pcur->next;
    }
    printf("NULL\n");

}
//销毁链表
void SlistDesTroy(SLTNode** pphead)
{
    assert(pphead);
    assert(*pphead);

    SLTNode* pcur = *pphead;
    while (pcur)
    {
        SLTNode* next = pcur->next;
        free(pcur);
        pcur = next;
    }
    *pphead = NULL;
}

2.2 双向链表

//定义双向链表中节点的结构
typedef int LTDataType;
typedef struct ListNode
{
	int data;
	struct ListNode* prev;
	struct ListNode* next;
}LTNode;

带头双向循环链表

2.2.1头插/尾插
//尾插
void LTPushBack(LTNode* phead, LTDataType x) {
	assert(phead);
	LTNode* newnode = LTBuyNode(x);
	//phead phead->prev(ptail)  newnode
	newnode->next = phead;
	newnode->prev = phead->prev;

	phead->prev->next = newnode;
	phead->prev = newnode;
}
//头插
void LTPushFront(LTNode* phead, LTDataType x) {
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead newnode phead->next
	newnode->next = phead->next;
	newnode->prev = phead;

	phead->next->prev = newnode;
	phead->next = newnode;
}

void LTPrint(LTNode* phead) {
	//phead不能为空
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

2.2.2头删/尾删
//尾删
void LTPopBack(LTNode* phead) {
	assert(phead);
	//链表为空:只有一个哨兵位节点
	assert(phead->next != phead);

	LTNode* del = phead->prev;
	LTNode* prev = del->prev;

	prev->next = phead;
	phead->prev = prev;

	free(del);
	del = NULL;
}
/头删
void LTPopFront(LTNode* phead) {
	assert(phead);
	assert(phead->next != phead);

	LTNode* del = phead->next;
	LTNode* next = del->next;

	//phead del next
	next->prev = phead;
	phead->next = next;

	free(del);
	del = NULL;
}
LTNode* LTFind(LTNode* phead, LTDataType x) {
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x) {
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

2.2.3查找
LTNode* LTFind(LTNode* phead, LTDataType x) {
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x) {
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}
2.2.4在指定位置之前插入数据/在指定位置之后插入数据

//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x) {
	assert(pos);
	LTNode* newnode = LTBuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}
2.2.5删除pos节点/删除pos之后的节点
//删除pos位置的数据
void LTErase(LTNode* pos) {
	assert(pos);

	//pos->prev pos  pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}
2.2.6打印/销毁链表
void LTDesTroy(LTNode* phead) {
	//哨兵位不能为空
	assert(phead);

	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	//链表中只有一个哨兵位
	free(phead);
	phead = NULL;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

n皇后问题-java

本次n皇后问题主要通过dfs&#xff08;深度优先搜索&#xff09;实现&#xff0c;加深对深度优先搜索的理解。 文章目录 前言 一、n皇后问题 二、算法思路 三、使用步骤 1.代码如下 2.读入数 3.代码运行结果 总结 前言 本次n皇后问题主要通过dfs&#xff08;深度优先搜索&#…

部署Hyperledger Fabric测试区块链网络

一. 快速启动区块链测试网络 启动Fabric虚拟机 将 fabric-samples.zip 拷贝进虚拟机 ubzip fabric-samples.zip 解压并重命名为fabric-samples mv fabric-samples-main fabric-samples 拷贝bin和config目录 cd fabric-samples cp ~/fabric/bin bin -r cp ~/fabric/config …

民族运动饮料之父『健力宝』×企企通正式启动SRM项目,打造饮料行业采购数字化应用标杆

近日&#xff0c;为推进采购阳光化、数字化和智能化&#xff0c;提升管理效率与质量&#xff0c;企企通与中国电解质饮料的领军品牌广东健力宝股份有限公司&#xff08;以下简称“健力宝”&#xff09;成功签约并召开项目启动会。健力宝行政副总裁赵总、CIO李总、采购本部总监杨…

矿用连续式负压自动排渣放水器——YC型

从今天起&#xff0c;努力去做一个可爱的人&#xff0c;不羡慕谁&#xff0c;也不埋怨谁&#xff0c;在自己的道路上&#xff0c;欣赏自己的风景&#xff0c;遇见自己的幸福。 矿用连续式负压自动排渣放水器——YC型 【1-5-9】产品介绍 连续式式负压自动排渣放水器采用双罐体结…

web自动化系列-selenium的3种等待方式(十一)

在ui自动化测试中&#xff0c;几乎出现问题最多的情况就是定位不到元素 &#xff0c;当你的自动化在运行过程中 &#xff0c;突然发现报错走不下去了 。很大概率就是因为找不到元素 &#xff0c;而找不到元素的一个主要原因就是页面加载慢 &#xff0c;代码运行速度快导致 。 …

Redis的RedisObject和对外可见的5种数据结构

目录 RedisObject Redis的编码方式 对外可见的5种数据结构 1.string string结构的源码 为什么是小于44字节会采用embstr编码&#xff1f; embstr和raw区别 2.list list结构的源码 3.set set结构的源码 4.zset zset结构的源码 5.hash hash结构的源码 Redis中…

EtherCAT开发_2_SSC使用记录

SSC快速开始参考《EtherCAT Slave Design Quick Guide》 字段内容直接参考SSC工具右侧Description&#xff0c;本文未填写。中文也可直接参考:《https://blog.csdn.net/g360250466/article/details/129847081》 ① Select EL9800 | 8Bit Digital I/O, 16Bit Analog Input 一、S…

Intel性能分析工具Vtune安装和使用简介

一、介绍 Intel Vtune profiler是用于串行和多线程应用程序的性能分析工具&#xff0c;可以帮助软件开发人员对应用程序的性能问题进行分析&#xff0c;支持包括linux和windows在内的多种操作系统。主要功能包括&#xff1a; 性能分析&#xff1a;可以对应用程序进行深入的性…

如何将低分辨率的视频变高清,使用AI工具分辨率画质增强至1080P、4K或者8K(附工具)

环境&#xff1a; Topaz Video AI 5.0 问题描述&#xff1a; 如何将低分辨率的视频变高清&#xff0c;使用AI工具分辨率画质增强至1080P、4K或者8K 原视频 增强1080P 解决方案&#xff1a; 1.打开软件&#xff0c;导入要处理的视频&#xff08;工具在本文最后附上&#xf…

网络安全:绕过 MSF 的一次渗透测试

这次渗透的主站是 一个 Discuz!3.4 的搭建 违法招 piao 网站&#xff0c; 配置有宝塔 WAF 用 Discuz!ML 3.X 的漏洞进行攻击&#xff0c;但是没有成功 发现主站外链会有一个发卡网&#xff0c;引导人们来这充值&#xff0c;是 某某发卡网&#xff0c;而且域名指向也是主站的 ip…

Stable Diffusion 模型分享:CyberRealistic XL(真实)cyberrealisticXL_v11VAE.safetensors

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八下载地址模型介绍

计算机网络基础:宏观认识

目录 一、网络发展背景与基本概念 二、网络协议的意义与TCP/IP五层结构模型 三、网络传输的基本流程与封装分用 四、ip地址和mac地址 随着信息技术的飞速发展&#xff0c;计算机网络已经成为了现代社会不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;我们几乎都离…

Crossref

https://baijiahao.baidu.com/s?id1766583173146005960&wfrspider&forpc https://zhidao.baidu.com/question/1796197318615421547.html

Java垃圾回收2

垃圾回收的算法有哪些 通过可达性分析算法&#xff0c;我们已经可以找到需要回收的对象。现在需要通过垃圾回收算法&#xff0c;把垃圾回收&#xff0c;释放内存。 1.标记清除算法(使用较少) 标记清除算法&#xff0c;是将垃圾回收分为2个阶段&#xff0c;分别是标记和清除。…

面试官:来说说vue3是怎么处理内置的v-for、v-model等指令?

前言 最近有粉丝找到我&#xff0c;说被面试官给问懵了。 粉丝&#xff1a;面试官上来就问“一个vue文件是如何渲染成浏览器上面的真实DOM&#xff1f;”&#xff0c;当时还挺窃喜这题真简单。就简单说了一下先是编译成render函数、然后根据render函数生成虚拟DOM&#xff0c;…

国外GIS软件排名简介<30个>

简介 国外gisgeography网站进行了一次GIS软件排名&#xff0c;通过分析、制图、编辑等因素进行测试&#xff0c;具体规则如下&#xff1a; 分析&#xff1a;矢量/栅格工具、时态、地统计、网络分析和脚本。 制图&#xff1a;地图类型、坐标系、地图布局/元素、标注/注记、3D …

请勿假设你的用户都有管理员权限

有些人觉得自己很聪明&#xff0c;他们在程序中做了这样一项”优化”。 在程序的安装阶段&#xff0c;他们不会安装某些程序功能&#xff0c;而是等到用户第一次使用的时候才执行&#xff0c;也即所谓的 “按需加载”。 问题在于&#xff0c;第一次使用的时候&#xff0c;用户…

CSS-布局

display display 属性是用于控制 布局 的最重要的 CSS 属性。display 属性规定是否/如何显示元素。 每个 HTML 元素都有一个默认的 display 值&#xff0c;具体取决于它的元素类型。大多数元素的默认 display 值为 block 或 inline。 block block&#xff1a;块级元素。块级…

从二本调剂到上海互联网公司算法工程师:我的成长故事

探讨选择成为一名程序员的原因&#xff0c;是出于兴趣还是职业发展&#xff1f; 在这个科技飞速发展的时代&#xff0c;程序员这一职业无疑成为了许多人眼中的香饽饽。那么&#xff0c;是什么驱使着越来越多的人选择投身于这一行业呢&#xff1f;是出于对编程的热爱&#xff0…

三步教你怎么把icloud照片恢复至iphone!

“我手机里面照片被优化后&#xff0c;然后不小心把所有被优化的模糊照片从手机中删除了&#xff0c;但是iCloud还有&#xff0c;我应该怎样把iCloud的照片重新放回手机&#xff1f;谢谢。” 在使用iPhone时&#xff0c;iCloud照片库是一个非常方便的功能&#xff0c;它允许你在…