单链表的链式存储总结

基本数据结构之-单链表的链式存储

  链表是一种插和删除元素很便捷的数据结构,原因是它存储的数据在内存中不连续,可以根据需要自己动态开辟。

和数组比较起来主要的缺点就是不能随机访问,如果是单链表,那么要访问一个数据,必须从头开始遍历一次!

对于基本数据结构围绕增删查改等操作,单链表的改可以通过删和增的结合来操作。因此就不用代码来表示了!

任何一个数据结构都有它的基本的数据结构,这儿给出一种用void *代替的写法

基本的数据结构的定义:

// 定义指针的结构体

typedef struct _LINKLISTNODE

{

struct _LINKLISTNODE *Next;

}LinkListNode;

// 定义链表的结构体

typedef struct _LINKLIST

{

LinkListNode *head;

int LinkListLen;

}LinkList;

基本结构体有了,可以初始化一个变量了,但是需要相关的函数的支持

这个可以定义一个一个LinkList类型的变量,让后把地址传递过去

// 初始化

int Init_LinkList(void ** LINKLIST)

{

// 对传入的参数进行检查

if (LINKLIST == NULL)

{

exit(-1);

}

// 开辟空间

LinkList *linklist =(LinkList *) malloc(sizeof(LinkList));

if (linklist == NULL)

{

// 空间开辟是否成功

exit(-2);

}

// 赋初始值

linklist->head = NULL;

linklist->LinkListLen = 0;

// 指针间的间接赋值

*LINKLIST = (void *)linklist;

return 0;

}

头结点有了,接下来就是增加这个链表的数据域的指针,三个函数,指定位置插入,头插,尾插

// 指定位置插入

int InsertByPos_LinkList(void * LINKLIST, int Pos, void * Data)

{

if (LINKLIST == NULL)

{

return -1;

}

if (Data == NULL)

{

return -2;

}

// 对传入的参数进行类型强转

LinkList *linklist = (LinkList *)LINKLIST;

// 对 Pos 合理化处理

if (Pos < 0)

{

Pos = 0;

}

if (Pos > linklist->LinkListLen)

{

Pos = linklist->LinkListLen;

}

/* 这个为什么是取地址了?

* 当程序初始化成功后,直接调用InsertByPos_LinkList(void * LINKLIST, int Pos, void * Data),

* 此时 head 为空,后面读取不到next指针

* 当其他时候调用改函数时,我们将这个值赋值给current的到的是linklist 的地址

* 但是是怎么包含到next的了

* linklist 中包含两个数据,一个时head 一个时 LinkListLen 当在解析current的next的时候编译器

* 默认帮你解释到head->next; ????

*/

LinkListNode *Current = &(linklist->head);

for (int i = 0; i < Pos; ++i)

{

Current = Current->Next;

}

// 转化指针类型

LinkListNode *data = (LinkListNode *)Data;

// 将传入的值加入到链表中

data->Next = Current->Next;

Current->Next = data;

++ linklist->LinkListLen;

return 0;

}

int FrontInsert_LinkList(void * LINKLIST, void * Data)

{

if (LINKLIST == NULL)

{

return -1;

}

if (Data == NULL)

{

return -2;

}

// 直接调用函数 实现

InsertByPos_LinkList(LINKLIST, 0, Data);

return 0;

}

int BackInsert_LinkList(void * LINKLIST, void * Data)

{

if (LINKLIST == NULL)

{

return -1;

}

if (Data == NULL)

{

return -2;

}

LinkList *linklist = (LinkList *)LINKLIST;

// 直接调用函数实现

InsertByPos_LinkList(LINKLIST, linklist->LinkListLen, Data);

return 0;

}

能插入元素不行,还要能显示

因为通用性的:所以需要定义一个函数指针,也就是回调函数

// 用于打印时调用用户自己定义的打印函数,

typedef void(*Print)(void *);

int Print_LinkList(void * LINKLIST, Print print)

{

if (LINKLIST == NULL)

{

return -1;

}

if (print == NULL)

{

return -2;

}

LinkList *linklist = (LinkList *)LINKLIST;

LinkListNode *Current = linklist->head;

while (Current != NULL)

{

print(Current);

Current = Current->Next;

}

printf("……………………………………………………\n");

return 0;

}

打印有了,插入有了,现在我想删除

删除也给了三个函数,指定位置的删除,头删,尾删

int EraseByPos_LinkList(void * LINKLIST, int Pos)

{

if (LINKLIST == NULL)

{

return -1;

}

LinkList *linklist = (LinkList *)LINKLIST;

// 检测删除位置的合理性

if (Pos < 0 || Pos>linklist->LinkListLen)

{

return -2;

}

LinkListNode *Current =&(linklist->head);

/* 为什么需要检测Current->Next->Next!=NULL不为空,因为后面需要直接将Current->Next->Next赋值给Current->Next

* 如果为空的话,访问会出错

*/

for (int i = 0; i < Pos && Current->Next->Next!=NULL ; ++i)

{

Current = Current->Next;

}

// 直接越过需要删除的值

Current->Next = Current->Next->Next;

// 维护链表的长度

--linklist->LinkListLen;

return 0;

}

int FrontErase_LinkList(void * LINKLIST)

{

if (LINKLIST == NULL)

{

return -1;

}

// 调用函数实现

EraseByPos_LinkList(LINKLIST, 0);

return 0;

}

int BackErase_LinkList(void * LINKLIST)

{

if (LINKLIST == NULL)

{

return -1;

}

// 调用函数实现

LinkList *linklist = (LinkList *)LINKLIST;

EraseByPos_LinkList(LINKLIST, linklist->LinkListLen);

return 0;

}

增删有了,改就可以实现了,基本思路就是先把要修改的数据删除,让后把要系修改的数据插入。

查找函数这儿就不给出了,可以根据下面的排序改写;

突然想看看我的链表有多长了,不想看了,要等数据被遍历一次才能知道,还好教我的老师给我说了先自己维护一个变量来记录当前链表的长度,需要的时候直接返回这个值就好了

// 返回链表数据的长度

int Len_LinkList(void * LINKLIST)

{

if (LINKLIST == NULL)

{

return -1;

}

LinkList *linklist = (LinkList *)LINKLIST;

return linklist->LinkListLen;

}

下面的代码不一定你常用,但是了解一下也是不错的

单链表的逆置,前面的那种写法比较简单时间和空间效率都比较高,后面的那种办法是我看到这个问题第一时间的反应

// 将链表的数据逆置

int Retrograde_LinkList(void * LINKLIST)

{

if (LINKLIST == NULL)

{

return -1;

}

LinkList *linklist = (LinkList *)LINKLIST;

#if 0

LinkListNode *pPre = linklist->head;

LinkListNode *Current = linklist->head->Next;

/*

* 将 linklist->head->nex赋值为空,

* link->head->next 不就是 Current 吗!下面的循环不是都进不去,错了,

* LinkListNode *Current = linklist->head->Next;这段代码,

* 已经将 linklist->head->Next 赋值给Current,此时修改原来的数据对它是没有影响的,

* 因为 current 和 linklist->head->Next 用的不是同一个内存空间

*/

pPre->Next = NULL;

/*

* 逆置简单的理解就是将所有的数据的next指向他们的直接前驱,让后把最后一个数据的地址给 head

*/

while (Current!= NULL)

{

// 用一个临时变量来缓存pPre;

LinkListNode *Temp = pPre;

// 让pPre指向current

pPre = Current;

// current 向前移动到下一个元素

Current = Current->Next;

// 将next指针指向它原来的直接前驱

pPre->Next = Temp;

}

// 最后将 linklist 的 head 指向原来的最后一个数据的地址

linklist->head = pPre;

#else

// 现将最后一个数据的位置缓存下来

LinkListNode *kpBack= &(linklist->head);

while (kpBack->Next != NULL)

{

kpBack = kpBack->Next;

}

int flag = 1;

while (flag)

{

/*

* 第二种方式比较耗时间和内存,因为每次都需要循环遍历找到当前的末尾,让后将它的前驱置空,后继指向它的前驱

*/

LinkListNode *pPre = &(linklist->head);

LinkListNode *Current = pPre->Next;

// 循环遍历找到最后一个的前驱

while (Current->Next != NULL)

{

pPre = pPre->Next;

Current = Current->Next;

}

if (pPre == &(linklist->head))

{

flag = 0;

break;

}

// 前驱置空

pPre->Next = NULL;

// next 指向前驱

Current->Next = pPre;

}

// 最后将head指向原来的最后一个元素

linklist->head = kpBack;

#endif

return 0;

}

无聊的我还想给单链表排个序,排序就意味着需要比较,所以需要定义一个比较的函数指针

(某培训机构的助教说我这代码有问题,请各位帮我看看这个排序算法的问题!真心的感谢!)

// 比较函数指针

typedef int(*COMPARE)(void *, void *);

// 排序

int SortExchangePos_LinkList(void * LINKLIST,COMPARE compare)

{

if (LINKLIST == NULL)

{

return -1;

}

LinkList *linklist = (LinkList *)LINKLIST;

LinkListNode *Pre = &(linklist->head);

int IsSort=1;

// 这儿借鉴了冒泡排序的有标记位的办法

while (Pre->Next!= NULL && IsSort)

{

IsSort = 0;

for (LinkListNode *pCurrent = &(linklist->head); pCurrent!= NULL && pCurrent->Next != NULL ; pCurrent = pCurrent->Next)

{

if(compare(pCurrent,pCurrent->Next)< 0) // 需要自己写比较函数,比较函数待优化

{

LinkListNode *TempCurrent = &(linklist->head);

while (TempCurrent!=NULL && TempCurrent->Next!=NULL && TempCurrent->Next->Next!= NULL && TempCurrent->Next->Next != pCurrent->Next )

TempCurrent=TempCurrent->Next;

LinkListNode *Temp = pCurrent->Next->Next;

TempCurrent->Next = pCurrent->Next;

pCurrent->Next->Next = pCurrent;

pCurrent->Next = Temp;

IsSort = 1;

}

}

Pre = Pre->Next;

}

return 0;

}

有点时候需要你把你自己开辟的空间释放了,这儿没有去释放每一个数据域,是因为我根本不知道我要把数据放到那,还是不要想了!等到自己写测试的时候,自己在释放吧!

int Destroy_LinkList(void * LINKLIST)

{

if (LINKLIST == NULL)

{

return -1;

}

LinkList *linklist = (LinkList *)LINKLIST;

if (linklist != NULL)

{

free(linklist);

}

return 0;

}

时间: 2024-12-29 07:08:48

单链表的链式存储总结的相关文章

单链表的链式存储的两种插入方式

/* 头插法建立单链表示例 */ void CreateListHead(LinkList *L, int n){ LinkList p; int i; srand(time(0)); // 初始化随机数种子 *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL; for( i=0; i < n; i++ ) { p = (LinkList)malloc(sizeof(Node)); // 生成新结点 p->data = rand(

线性表带头结点的单链表的链式表示和实现

带有头结点的单链表的12个基本操作 #define DestroyList ClearList//DestroyList()和ClearList()的操作是一样的 void InitList(LinkList &L){ L = NULL; } void ClearList(LinkList &L){ LinkList p; while (L){ p = L; L = L->next; free(p); } } Status ListEmpty(LinkList L){ if (L)r

对于链表的链式存储的初始化操作,遇到的有关“二级指针”的知识小结

首先解释一下“二级指针”: 一级指针所关联的是其值(一个地址)名下空间里的数据,这个数据可以是任意类型并做任意用途,但二级指针所关联的数据只有一个类型一个用途,就是地址. 指针就是两个用途:提供目标的 读取 或 改写, 那么二级指针就是为了提供对于内存地址的读取或改写. 指针的表现形式是地址,核心是指向关系指针,运算符“*”的作用是按照指向关系访问所指向的对象. 如果存在A指向B的指向关系,则A是B的地址,“*A”表示通过这个指向关系间接访问B. 如果B的值也是一个指针,它指向C,则B是C的地址

单链表的链式结构表示

from:shiyanlou 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define TRUE 1 5 #define FALSE 0 6 #define OK 1 7 #define ERROR 0 8 #define OVERFLOW -2 9 10 typedef int ElemType; 11 typedef int Status; 12 13 /* 14 * 存储结构 15 */ 16 typedef struct

2-5-归并链式存储的单链表-线性表-第2章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第2章  线性表 - 归并单链表(链式存储) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? SinglyLinkedList.c        相关测试数据下载

04.线性表(三)链式存储结构.单链表2

链式存储结构.单链表2 顺序存储结构的创建实质是一个数组的初始化,存储空间连续且其大小和类型已经固定:单链表存储空间不连续,是一种动态结构且它所占用空间的大小和位置是不需要预先分配划定的,可以根据系统的情况和实际的需求即时生成. 一.单链表的整表创建 创建单链表的过程就是一个动态生成链表的过程,即从"空表"的初始化起,依次建立各元素结点,并逐个插入链表. 1.算法思路 (1)声明一个结点p和计数器变量i; (2)初始化一空链表L (3)让链表L的头结点的指针指向NULL,即建立一个带头

【数据结构】链式存储单链表

数据结构之单链表的链式存储实现 //====================================================================== // // Copyright (C) 2014-2015 SCOTT // All rights reserved // // filename: List.c // description: a demo to display SeqList // // created by SCOTT at 01/28/2015

简要比较线性表的顺序存储结构和链式存储结构

我们分别从存储分配方式.时间性能.空间性能三方面来做对比. 存储分配方式 顺序存储结构用一段连续的存储单元依次存储线性表的数据元素. 单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素. 时间性能 <1>查找 顺序存储结构O(1) 单链表O(n) <2>插入和删除 顺序存储结构需要平均移动表长一半的元素,时间为O(n) 单链表在计算出某位置的指针后,插入和删除时间仅为O(1) 空间性能 顺序存储结构需要预分配存储空间,分大了,容易造成空间浪费,分小了,容易发生溢出. 单链

数据结构开发(5):线性表的链式存储结构

0.目录 1.线性表的链式存储结构 2.单链表的具体实现 3.顺序表和单链表的对比分析 4.小结 1.线性表的链式存储结构 顺序存储结构线性表的最大问题是: 插入和删除需要移动大量的元素!如何解决? 链式存储的定义: 为了表示每个数据元素与其直接后继元素之间的逻辑关系:数据元素除了存储本身的信息外,还需要存储其直接后继的信息. 链式存储逻辑结构: 基于链式存储结构的线性表中,每个结点都包含数据域和指针域 数据域:存储数据元素本身 指针域:存储相邻结点的地址 专业术语的统一: 顺序表 基于顺序存储