当前位置: 首页 > news >正文

数据结构之链表

单链表

概念:链表是⼀种物理存储结构非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

 与顺序表不同的是,链表申请的每个空间称为节点,结点的组成主要有两个部分:当前结点要保存的数据保存下⼀个结点的地址(指针变量)。

链表中每个结点都是独立申请的(即需要插入数据时才去申请⼀块结点的空间),我们需要通过指针变量来保存下⼀个结点位置才能从当前结点找到下⼀个结点。
当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数
据,也需要保存下⼀个结点的地址(当下⼀个结点为空时保存的地址为空)。

定义链表结构

//定义链表的结构---节点的结构typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;//存储的数据struct SListNode* next;//指向下一个节点的指针,不能用简写,程序还未定义
}SLTNode; 

打印链表

void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}

pcur=pcur->next:
1)pcur指针变量保存第一个节点的地址

2)对pcur解引用拿到next指针变量中的地址(下一个节点的地址)

3)赋值给pcur,此时pcur保存的地址为0x0012FFAO,即pcur“指向了下一个节点”

 实现单链表

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//定义链表的结构---节点的结构typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;//存储的数据struct SListNode* next;//指向下一个节点的指针,不能用简写,程序还未定义
}SLTNode; //只有看不到&,就是传值操作
// //链表的打印
void SLTPrint(SLTNode* phead);//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);//查找
SLTNode * SLTFind(SLTNode * phead, SLTDataType x);//在指定位置之前插⼊数据void SLTInsert(SLTNode * *pphead, SLTNode * pos, SLTDataType x);//删除pos结点
void SLTErase(SLTNode * *pphead, SLTNode * pos);
//在指定位置之后插⼊数据void SLTInsertAfter(SLTNode * pos, SLTDataType x);
//删除pos之后的结点void SLTEraseAfter(SLTNode * pos);
//销毁链表
// void SListDestroy(SLTNode * *pphead);

创建x节点

//根据x创建节点
SLTNode* SLTbuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}

 尾插

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = SLTbuyNode(x);if (*pphead == NULL){*pphead = newnode;}else{//找尾SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newnode;}}

头插

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTbuyNode(x);//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}

尾删

//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个结点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else {SLTNode* prev = NULL;SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}//prev ptailprev->next = NULL;free(ptail);ptail = NULL;}
}

头删

//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}

查找

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

指定位置前插入数据

//在指定位置pos之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && pos);//当pos指向第一个节点,头插if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* newnode = SLTbuyNode(x);//找pos的前一个节点SLTNode* prev = *pphead;//需要从头遍历while (prev->next != pos){prev = prev->next;}//prev newnode pos联系起来prev->next = newnode;newnode->next = pos;/*newnode = prev->next;pos = newnode->next;*/}}

*1.prev->next = newnode; newnode->next = pos; 

链表插入新节点的典型操作:

prev->next = newnode;:让 prev 原本指向的节点(假设是 A),改为指向新节点 newnode,断开 prev 到 pos 的旧连接。

newnode->next = pos;:让新节点 newnode 指向 pos,把 pos 接在新节点后面,完成插入

这一步的核心是先改变 prev 的指向,再用新节点连接原后继 pos,最终链表结构会变成 ... -> prev -> newnode -> pos -> ...

*2.newnode = prev->next; pos = newnode->next; 则是单纯的指针赋值,没有修改链表结构:

newnode = prev->next;:把 prev 原本指向的后继节点(比如 A)的地址,赋给 newnode,此时 newnode 和 prev->next 指向同一个节点

pos = newnode->next;:再把 newnode 原本的后继(即 prev 原来的后继的后继)赋给 pos,本质是用指针 “抄” 了一遍链表的连接关系,但没改变链表实际结构

指定位置后插入数据

//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)//不需要找pos前面的指针,故只需要两个变量
{assert(pos);SLTNode* newnode = SLTbuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;pos->next = newnode;}

删除指定位置节点

//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos);//pos就是头节点if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev pos pos->nextprev->next = pos->next;free(pos);pos = NULL;}
}

删除指定位置后一个节点

//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);//pos del del->nextSLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}

销毁链表

//销毁链表
void SListDestroy(SLTNode** pphead)
{SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

http://www.dtcms.com/a/313528.html

相关文章:

  • sublime text2配置
  • 设备维护计划制定指南:基于数据驱动的全流程技术实现
  • 接口测试用例的编写
  • solidworks打开step报【警告!可用的窗口资源极低】的解决方法
  • Kubernetes中ingress,egress,slb等概念的含义
  • 电路设计(电容)设计细节
  • 【华为OD机试】从小桶里取球
  • 嵌入式分享合集13
  • io_destroy系统调用及示例
  • 【AI】文档理解
  • 关于assert()函数,eval()函数,include
  • Java中手动床架一个线程池
  • 【OD机试题解法笔记】文件缓存系统
  • 第 10 篇:深度学习的“军火库”——CNN、RNN与Transformer,AI如何看懂世界?
  • pod的创建流程
  • [Linux入门] 从 iptables 到 nftables:初学者入门指南
  • 大数据之路:阿里巴巴大数据实践——元数据与计算管理
  • 【分析学】Hilbert 空间的分离性
  • 分布式事务Seata AT模式设计分析篇
  • 41.安卓逆向2-frida hook技术-过firda检测(五)-利用ida分析app的so文件中frida检测函数过检测
  • 关于Manus AI与多语言手写识别的技术
  • Linux驱动学习(六)一些函数
  • 【canvas】
  • 从WebShell 与 ShellCode 免杀技术 打造适合自己的免杀技术链
  • 设计模式 - 组合模式:用树形结构处理对象之间的复杂关系
  • 攻防世界-web-csaw-mfw
  • 【C++】封装,this指针
  • C++高阶笔记第四篇:STL-函数对象
  • 【Leetcode】2106. 摘水果
  • Yakit热加载魔术方法模版插件语法JSRpc进阶调用接口联动