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

【数据结构】单链表及单链表的实现

文章目录

  • 1.单链表
    • 1.1 概念与结构
      • 1.1.1 结点
      • 1.1.2 链表的性质
      • 1.1.3 链表的打印
    • 1.2 实现单链表

1.单链表

1.1 概念与结构

概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
在这里插入图片描述

淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只需要将⽕⻋⾥的某节⻋厢去掉/ 加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的。

在链表⾥,每节“⻋厢”是什么样的呢?

在这里插入图片描述

1.1.1 结点

与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“结点/结点”

火车有一节一节车厢组成

链表由一个一个结点组成

结点有两个组成部分:

​ 1)保存的数据

​ 2)指针:始终保存下一个结点的地址

图中指针变量plist保存的是第⼀个结点的地址,我们称plist此时“指向”第⼀个结点,如果我们希望 plist“指向”第⼆个结点时,只需要修改plist保存的内容为0x0012FFA0。

链表中每个结点都是独⽴申请的(即需要插⼊数据时才去申请⼀块结点的空间),我们需要通过指针 变量来保存下⼀个结点位置才能从当前结点找到下⼀个结点。

1.1.2 链表的性质

1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续

2、结点⼀般是从堆上申请的

3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

结合前⾯学到的结构体知识,我们可以给出每个结点对应的结构体代码:

假设当前保存的结点为整型:

struct SListNode 
{ int data; //结点数据 struct SListNode* next; //指针变量⽤保存下⼀个结点的地址 
}; 

当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数据,也需要保存下⼀个结点的地址(当下⼀个结点为空时保存的地址为空)。

1.1.3 链表的打印

在这里插入图片描述

给定的链表结构中,如何实现结点从头到尾的打印?

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

思考:当我们想保存的数据类型为字符型、浮点型或者其他⾃定义的类型时,该如何修改?

typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;

1.2 实现单链表

#include"SList.h"void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur != NULL){printf("%d ->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);//pphead是一级指针的地址,是二级指针//如果pphead为空,我们无法通过pphead来访问头结点指针,也无法修改头结点指针//*pphead是指向第一个结点的地址//申请新空间SLTNode* newnode = SLTBuyNode(x);//特殊情况,链表为空if(*pphead == NULL)//*pphead获取pphead头指针内容{*pphead = newnode;}else{//找链表的尾结点SLTNode* ptail = *pphead;while (ptail->next != NULL){ptail = ptail->next;//这是在往后走}//找到了尾结点ptail->next = newnode;}
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{//形参为SLTNode** pphead,是因为要对plist(存放的地址)本身进行修改assert(pphead);//给新结点申请空间SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;//赋值*pphead本身*pphead = newnode;//此时newnode是第一个结点
}
//尾删
void SLTPopBack(SLTNode** pphead)//第一个结点可能会被删,会发生改变,所以是二级指针
{//链表为空不能删除,也就是第一个结点的地址不能为空//也就是说pphead不能为空,因为不能传空指针,第一个结点的地址也不能为空assert(pphead&&*pphead);//链表只有一个结点的情况if ((*pphead)->next == NULL)//先解引用{free(*pphead);*pphead = NULL;}else{SLTNode* prev = NULL;//最后指向尾结点的前一个结点,为了将前一个结点的next置为空SLTNode* ptail = *pphead;//最后指向尾结点while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}	
}
//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);//要先让*pphead指向第二个结点,再删除第一个结点SLTNode* next = (*pphead)->next;//先将第二个结点的地址存下来,释放掉第一个结点后,给*pphead赋值free(*pphead);*pphead = next;
}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{//不会改变第一个结点的内容,用*phead接收就行SLTNode* pcur = phead;while (pcur){if (pcur->data == x)return pcur;pcur = pcur->next;}return NULL;
}
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) 
{assert(pphead && pos);SLTNode* newnode = SLTBuyNode(x);//先创建一个新结点if (pos == *pphead){//头插SLTPushFront(pphead, x);}else{//pos的前一个结点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}
}
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//先连后断newnode->next = pos->next;//先让newnode指向pos的下一个结点,再让pos->next=newnodepos->next = newnode;
}//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead && pos);SLTNode* prev = *pphead;if (pos == *pphead)//pos是头结点SLTPopFront(pphead);//头删else {while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{assert(pos&&pos->next);SLTNode* del = pos->next;//pos的下一个结点//一旦调用 free(pos->next),pos->next 指向的内存就被系统回收了//后续再访问 pos->next->next 就是访问已释放内存,会导致未定义行为,所以用del保存pos->next = del->next;free(del);del = NULL;
}//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;//先保存下一个结点,再释放free(pcur);pcur = next;}//*pphead始终保存第一个结点的地址,但是这个地址已经还给操作系统了//*pphead此时是个野指针了,要置为空*pphead = NULL;
}
#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);
//申请一个节点的空间
SLTNode* SLTBuyNode(SLTDataType x);//尾插一个
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);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);//销毁链表
void SListDestroy(SLTNode** pphead);
#include"SList.h"void test01()
{//创建一个链表SLTNode* node1 =(SLTNode*) malloc(sizeof(SLTNode));SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));//存数据node1->data = 1;node2->data = 2;node3->data = 3;node4->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;//打印链表SLTNode* plist = node1;SLTPrint(plist);
}
void test02()
{SLTNode* plist = NULL;//尾插//SLTPushBack(plist, 1);传的是plist存的数据(也就是NULL的地址),而不是plist本身//要让phead(形参)的改变影响实参,就要传plist的地址//此时形参phead改为pphead,表示phead的地址,数据类型由SLTNode*变为SLTNode**//除了特殊情况(数组等)都用&取地址//SLTPushBack(&plist, 1);//SLTPushBack(&plist, 2);//SLTPushBack(&plist, 3);//SLTPushBack(&plist, 4);//SLTPrint(plist);//尾删//SLTPopBack(&plist);//SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);//头插SLTPushFront(&plist, 1);SLTPrint(plist);SLTPushFront(&plist, 2);SLTPrint(plist);SLTPushFront(&plist, 3);SLTPrint(plist);SLTPushFront(&plist, 4);SLTPrint(plist);//头删//SLTPopFront(&plist);//SLTPrint(plist);//SLTPopFront(&plist);//SLTPrint(plist);//SLTPopFront(&plist);//SLTPrint(plist);//SLTPopFront(&plist);//SLTPrint(plist);//查找SLTNode* pos = SLTFind(plist, 2);if (pos)printf("找到了\n");elseprintf("没找到\n");//在pos之前的位置删除一个数据//SLTInsert(&plist, pos, 100);//SLTPrint(plist);//删除pos结点//SLTErase(&plist, pos);//SLTPrint(plist);//删除pos之后的结点SLTEraseAfter(pos);SLTPrint(plist);//销毁SListDestroy(&plist);
}
int main()
{//test01();test02();return 0;
}
//当只剩下一个结点时,调用这个函数
void SLTPopBack(SLTNode** pphead)
{assert(pphead&&*pphead);SLTNode* prev = NULL;SLTNode* ptail = *pphead;while (ptail->next)//不执行{prev = ptail;ptail = ptail->next;}prev->next = NULL;//当只剩下一个结点时,prev已经是NULL,不能对next进行赋值free(ptail);ptail = NULL;
}
void SLTEraseAfter(SLTNode* pos)
{assert(pos);SLTNode* del = pos->next;//pos的下一个结点
//一旦调用 free(pos->next),pos->next 指向的内存就被系统回收了
//后续再访问 pos->next->next 就是访问已释放内存,会导致未定义行为,所以先用del保存pos->next = del->next;free(del);del = NULL;
}
//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;//先保存下一个结点,再释放free(pcur);pcur = next;}//*pphead始终保存第一个结点的地址,但是这个地址已经还给操作系统了//*pphead此时是个野指针了,要置为空*pphead = NULL;
}

销毁完之后的调试

在这里插入图片描述

尾插和尾删的时间复杂度O(N)

头插和头删的时间复杂度O(1)

总结:头插和头删用的多使用链表

尾插和尾删用的多使用顺序表

在指定位置之前插入数据时间复杂度O(n)

在指定位置之前插入数据时间复杂度O(1)

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

相关文章:

  • 深圳外贸公司招聘信息关键词网站优化平台
  • 检查部门网站建设商业计划书模板范文
  • Java EE - 线程的状态
  • wordpress图片下一页整站优化系统
  • AlmaLinux 部署 Samba 服务:文件共享快速实现
  • 做网站需要用到什么技术网站会员权限
  • 江西中联建设集团有限公司网站企业网站内容模块
  • 易企秀微网站如何做文字链接WordPress 磁力
  • 扬州市住房和建设局网站网站获取客户信息需要备案吗
  • dedecms做的网站_网站中的图片总是被同一ip恶意点击网站建设管理情况自查报告
  • 维修网站建设wordpress和代码
  • Java 代理模式全解析:静态代理、JDK 动态代理与 CGLIB 代理实战
  • 做免费外贸网站册域名滨州医学院做计算机作业的网站
  • 保山网站建设哪家好校园推广活动策划方案
  • 专业的会议网站建设民用网络架构
  • 多用户商城网站方案天津建设银行公积金缴费网站
  • 有趣网站之家无锡网站建设培训
  • 太原有网站工程公司吗it培训机构学费一般多少
  • 毕设做网站太简单网站 开发 工具
  • 截图按钮图标素材网站百度一wordpress
  • 长宁区网站建设网北京网站建设代理
  • 网站 的空间定制化开发是什么意思
  • 亿唐网不做网站做品牌原因网络市场调研
  • 专做机酒的网站北京附近做网站的公司
  • 【大型Qt工程国际化动态更新语言不成功】
  • 做的网站怎样适配手机屏幕定制网站制作报价
  • 使用cursor-free-vip时出现的错误及其解决方案
  • 【Hot100|9-LeetCode 438. 找到字符串中所有字母异位词】
  • 自已电脑做网站服务器广州平面设计
  • 个人备案网站做购物网站可以不网站建设合同应注意什么