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

数据结构(3)单链表

1、链表的引入

        顺序表带来了很多方便,但是又带来了以下几个问题:(1)中间/头部的插入和删除,时间复杂度为O(N) ;(2)增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗 ; (3)增容一般是呈二倍增长,势必会有一定的空间浪费 。

        想要解决以上的问题,就需要一个新的数据结构——链表

2、概念与结构

        链表是一种物理存储结构上非连续、非顺序的存储结构。链表由结点组成,结点的组成主要有两个部分:当前结点要保存的数据 和 保存下一个结点的地址(指针变量)。

struct SListNode
{//存储的数据int data;//指向下一个节点的指针struct SListNode* next;
};
SLTNode* SLTbuyNode(SLTDataType x)
{//根据x创建结点SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}

 

3、单链表的实现

 

//尾插
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->next = *pphead;*pphead = newnode;
}

//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个结点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//找prev和ptailSLTNode* prev = NULL;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);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}

首先要找到pos位置的前一个节点prev,遍历整个链表,循环条件prev->next != pos。如果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->next = newnode;newnode->next = pos;}
}

 

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

 这里为什么函数参数部分只有两个参数?因为我们可以直接通过pos->next来找到下一个节点,就不用像前面那样传头结点的指针来遍历链表。

//删除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->next = pos->next;free(pos);pos = NULL;}
}

4、完整参考代码

SList.h
#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);//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);//删除pos位置之后的结点
void SLTEraseAfter(SLTNode* pos);//销毁链表
void SListDestroy(SLTNode** pphead);
SList.c
#define  _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"//链表的打印
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}SLTNode* SLTbuyNode(SLTDataType x)
{//根据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->next = *pphead;*pphead = newnode;
}//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个结点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//找prev和ptailSLTNode* prev = NULL;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);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;
}//在指定位置之前插入数据
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->next = newnode;newnode->next = pos;}
}//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTbuyNode(x);newnode->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->next = pos->next;free(pos);pos = NULL;}
}//删除pos位置之后的结点
void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* 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;
}
test.c
#define  _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"void test01()
{//创建空链表SLTNode* plist = NULL;//SLTPrint(plist);SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);//SLTPopFront(&plist);//SLTPrint(plist);SLTNode* find = SLTFind(plist, 3);SLTInsertAfter(find, 100);SLTPrint(plist);SListDestroy(&plist);
}int main()
{test01();return 0;
}

http://www.lryc.cn/news/600107.html

相关文章:

  • [尚庭公寓]14-找房模块
  • Canal 1.1.7的安装
  • 习题5.6 “数学黑洞“
  • PHP插件开发中的一个错误:JSON直接输出导致网站首页异常
  • 纸板留声机:用ESP32和NFC打造会唱歌的复古装置
  • 手语式映射:Kinova Gen3 力控机械臂自适应控制的研究与应用
  • 秒收蜘蛛池解析机制的原理
  • PPIO上线阿里旗舰推理模型Qwen3-235B-A22B-Thinking-2507
  • ATR2652SGNSS全频段低噪声放大器
  • PostgreSQL对象权限管理
  • GPU 驱动安装升级测试
  • [NPUCTF2020]ReadlezPHP
  • CSS 盒子模型学习版的理解
  • C语言第 9 天学习笔记:数组(二维数组与字符数组)
  • ODFM(正交频分复用)系统中加入汉明码(Hamming Code)的主要目的是增强抗误码能力,通过**前向纠错(FEC)**机制提高传输可靠性
  • KNN算法:从原理到实战全解析
  • Kubernetes深度解析:企业级容器编排平台的核心实践
  • 内存 管理
  • LeetCode 233:数字 1 的个数
  • ACL:访问控制列表
  • 大数据中心——解读60页IDC云数据中心机房运维服务解决方案【附全文阅读】
  • MMRotate ReDet ReFPN 报错 `assert input.type == self.in_type`
  • Linux的磁盘存储管理实操——(下二)——逻辑卷管理LVM的扩容、缩容
  • ComfyUI中运行Wan 2.1工作流,电影级视频,兼容Mac, Windows
  • 一些常见的网络攻击方式
  • 与 TRON (波场) 区块链进行交互的命令行工具 (CLI): tstroncli
  • 关闭chrome自带的跨域限制,简化本地开发
  • 【Chrome】下载chromedriver的地址
  • 中国航天集团实习第一周总结
  • 低速信号设计之 SWD 篇