单链表专题

1.链表的概念及结构

概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
链表的基本结构如下图所示:

与顺序表不同的地方是链表里的每一个小部分都是独立申请下来的空间 ,我们称这小部分为“结点”

节点的组成部分有两种:当前节点所保存的数据和下一节点的地址。

图中指针变量 plist保存的是第⼀个节点的地址,我们称plist此时“指向”第⼀个节点,如果我们希
望plist“指向”第⼆个节点时,只需要修改plist保存的内容为0x0012FFA0.
为什么还需要指针变量来保存下一个节点的位置?
因为节点都是独立的空间,只有保存了下一节点的位置才能够找到下一节点。
以下为每一个节点对应的结构体代码:
假设当前保存的节点为整型:
struct SListNode
{
 int data; //节点数据
 struct SListNode* next; //指针变量⽤保存下⼀个节点的地址
};

以下是实现节点从头到尾的打印:
 

要想让保存的数据类型是字符型,浮点型或者其他自定义类型时可以把整形改成对应的类型,为了方便也可以这样写:

 typedef int SListNode

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

这样可以快速更改类型。 

补充说明

1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续
2、节点⼀般是从堆上申请的
3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

2.单链表的实现

以下为SLish.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);

以下为SLish.c

 
 

#include"SList.h"

void SLTPrint(SLTNode* phead)

{

SLTNode* pcur = phead;

while (pcur)//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 fail!");

exit(1);

}

newnode->data = x;

newnode->next = NULL;

return newnode;

}

//尾插

void SLTPushBack(SLTNode** pphead, SLTDataType x)

{

assert(pphead);

//*pphead 就是指向第一个节点的指针

//空链表和非空链表

SLTNode* newnode = SLTBuyNode(x);

if (*pphead == NULL)

{

*pphead = newnode;

}

else

{

//找尾

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;

}

//尾删

void SLTPopBack(SLTNode** pphead)

{

//链表不能为空

assert(pphead && *pphead);

//链表只有一个节点

if ((*pphead)->next == NULL) //-> 优先级高于*

{

free(*pphead);

*pphead = NULL;

}

else {

//链表有多个节点

SLTNode* prev = *pphead;

SLTNode* ptail = *pphead;

while (ptail->next)

{

prev = ptail;

ptail = ptail->next;

}

//prev ptail

free(ptail);

ptail = NULL;

prev->next = NULL;

}

}

//头删

void SLTPopFront(SLTNode** pphead);

以下为test.c

 

 
 

include"SList.h"

void SListTest01()

{

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

//创建几个节点

SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));

node1->data = 1;

SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));

node2->data = 2;

SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));

node3->data = 3;

SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));

node4->data = 4;

//将四个节点连接起来

node1->next = node2;

node2->next = node3;

node3->next = node4;

node4->next = NULL;

//调用链表的打印

SLTNode* plist = node1;

SLTPrint(plist);

}

void SListTest02()

{

SLTNode* plist = NULL;

SLTPushBack(&plist, 1);

SLTPushBack(&plist, 2);

SLTPushBack(&plist, 3);

SLTPushBack(&plist, 4);

SLTPrint(plist);

//SLTPushBack(NULL, 5);

//

//测试头插

//SLTPushFront(&plist, 6);

//SLTPrint(plist);

//SLTPushFront(&plist, 7);

//SLTPrint(plist);

//SLTPushFront(&plist, 8);

//SLTPrint(plist);

//测试尾删

SLTPopBack(&plist);

SLTPrint(plist);

SLTPopBack(&plist);

SLTPrint(plist);

SLTPopBack(&plist);

SLTPrint(plist);

SLTPopBack(&plist);

SLTPrint(plist);

}

int main()

{

//SListTest01();

SListTest02();

return 0;

}

3.链表的分类 

链表的结构非常多样,以下情况组合起来就有8种:

以下为链表的说明:

 

虽然有这么多的链表的结构,但是我们实际中最常⽤还是两种结构: 单链表 双向带头循环链表
1. ⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结
构的⼦结构,如哈希桶、图的邻接表等等。另外这种结构在笔试⾯试中出现很多。
2. 带头双向循环链表:结构最复杂,⼀般⽤在单独存储数据。实际中使⽤的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使⽤代码实现以后会发现结构会带
来很多优势,实现反⽽简单了,后⾯我们代码实现了就知道了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值