当前位置:首页 > 编程笔记 > 正文
已解决

【数据结构初级(2)】单链表的基本操作和实现

来自网友在路上 159859提问 提问时间:2023-11-07 00:44:06阅读次数: 59

最佳答案 问答题库598位专家为你答疑解惑

文章目录

  • Ⅰ 概念及结构
    • 1. 单链表的概念
    • 2. 单链表的结构
  • Ⅱ 基本操作实现
    • 1. 定义单链表结点
    • 2. 创建新结点
    • 3. 单链表打印
    • 4. 单链表尾插
    • 5. 单链表头插
    • 6. 单链表尾删
    • 7. 单链表头删
    • 8. 单链表查找
    • 9. 在指定 pos 位置前插入结点
    • 10. 删除指定 pos 位置的结点
    • 11. 单链表销毁

本章实现的是不带头结点的单链表。

Ⅰ 概念及结构

1. 单链表的概念

  • 链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
  • 单链表的每个结点仅仅要存储该结点本身应该存储的数据,还要存储下一个与自己同类型结点的地址。

在这里插入图片描述

2. 单链表的结构

1. 物理结构

每个单链表结点都有一个指针域,用来存储下一个结点的地址。

在这里插入图片描述

2. 逻辑结构

为了方便理解和学习,使用箭头来表示每个结点的指针域所存储的是哪个结点的地址。

在这里插入图片描述

Ⅱ 基本操作实现

1. 定义单链表结点

  • 每个结点都应该包含数据域和指针域两部分内容。
  • 数据域的数据类型应该使用 typedef 来定义,防止以后更改数据域的数据类型。
typedef int SLTDataType;	//每个单链表结点数据域的数据类型typedef struct SListNode	//定义一个单链表结点
{SLTDataType data;		//单链表的数据域struct SListNode* next;	//单链表的指针域,用于存放下一个同类型结点的地址
}SLNode;					//单恋表的结点类型

2. 创建新结点

  • 开辟一个结点空间,将传过来的数据 x 放到结点的数据域,然后将该结点的指针域置 NULL。
SLNode* BuySListNode(SLTDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));	//开辟一个新结点空间if (NULL == newnode)	//如果开辟该结点失败{perror("malloc");exit(-1);}newnode->data = x;		//参数给新结点的数据域newnode->next = NULL;	//将新结点的指针域置空return newnode;			//返回开辟的新结点的地址
}

3. 单链表打印

1. 遍历链表

因为不能动指向首结点的指针,防止找不到首结点,所以此类操作都是利用额外的指针来实现的。

  • 使用一个指针 cur (current) 指向当前结点,如果 cur 不为空,则说明链表还没走到头。打印 cur 所指向的结点的数据域,然后让 cur 指针指向下一个结点。
  • 在打印的时候会有两种情况。
  1. 单链表为空:此时 cur 直接就指向 NULL,什么也不会打印。

在这里插入图片描述

  1. 单链表非空:打印 cur 指向结点的数据域的类容,然后将 cur 指向指向下一个结点,直到 cur 走到链表末尾后为止。

在这里插入图片描述

2. 代码实现

 //单链表打印
void SListPrint(SLNode* plist)
{SLNode* cur = plist;			//指向单链表的首结点while (cur != NULL)				//单链表还没走到尾{printf("%d->", cur->data);	//打印当前结点数据域的值cur = cur->next;			//继续指向下一个结点}printf("NULL\n");
}

3. 函数调用

SListPrint(plist);

4. 单链表尾插

1. 插入链表

将数据依次插入到单链表的尾部。
在进行单链表尾插时有两种情况:

  1. 单链表为空:直接创建一个新结点然后插入到单链表即可。

在这里插入图片描述

  1. 单链表非空:使用一个 tail 指针用来寻找单链表的尾部,要将新结点插入到 tail->next 为 NULL 的位置。

在这里插入图片描述

2. 代码实现

//传过来的是个指针变量的地址,需要使用二级指针 pplist 来接收
void SListPushBack(SLNode** pplist, SLTDataType x)
{assert(pplist);SLNode* newnode = BuySListNode(x);	//创建一个新的结点if (NULL == *pplist)				//单链表当前为空{*pplist= newnode;				//直接将新结点插入链表}else								//单链表非空{SLNode* tail = *pplist;			//tail 用于寻找单链表的尾结点while (tail->next != NULL)		//没有找到尾结点{tail = tail->next;			//指向下一个结点}tail->next = newnode;			//将新结点插入到尾结点之后,成为新的尾结点}
}

3. 函数调用

在这里插入图片描述

5. 单链表头插

1. 插入链表

  • 不管链表中有多少个结点,所有的数据都插入到链表的都一个位置

在这里插入图片描述

2. 代码实现

//要改变链表内容,实参要传指针变量的地址,形参用二级指针接收
void SListPushFront(SLNode** pplist, SLTDataType x)
{assert(pplist);						//传过来的不能是 NULL 指针SLNode* newnode = BuySListNode(x);	//创建新结点newnode->next = *pplist;			//将首结点地址交给新结点指针域*pplist= newnode;					//让新结点成为新的首结点
}

3. 调用函数

在这里插入图片描述

6. 单链表尾删

  • 每次将单链表尾部的的一个结点删除掉。

1. 实现方法

  1. 链表中只一个结点:直接释放链表指针指向的那个结点。

在这里插入图片描述

  1. 链表中有多个结点:使用一个 tail 指针寻找尾结点的前一个结点,如果 tail 所指向结点的下个结点的指针域为空,则说明 tail->next 指向的结点为尾结点,直接释放即可。

在这里插入图片描述

2. 实现代码

void SListPopBack(SLNode** pplist)
{assert(pplist);							//pplist 指向的 plist 不是 NULLassert(*pplist);						//plist 指向的链表不为空if (NULL == (*pplist)->next)			//1.一个结点{free(*pplist);						//释放链表中唯一的一个结点*pplist= NULL;}else									//2.多个结点{SLNode* tail = *pplist;				//使用 tail 找尾结点while (tail->next->next != NULL)	//当前结点的下下个结点不为空{tail = tail->next;				//tail 指向链表下一个结点}free(tail->next);					//释放 tail->next 指向的尾结点tail->next = NULL;					//将 tail->next 的值置空}
}

3. 函数调用

在这里插入图片描述

7. 单链表头删

  • 每次删除都只删除单链表的首结点。

1. 实现方法

  1. 链表有多个结点:先用一个临时指针变量保存首结点的地址,再让链表指针指向第二个结点,最后释放首结点。

在这里插入图片描述

  1. 链表只一个结点:步骤和上面相同,只不过只有一个结点时,链表指针在第二步会指向 NULL 而已。

2. 实现代码

void SListPopFront(SLNode** pplist)
{assert(pplist);				//传过来的不是 NULLassert(*pplist);			//链表不为空SLNode* tmp = *pplist;		//先保存链表首结点*pplist= (*pplist)->next;	//让链表指针指向第二个结点free(tmp);					//释放首结点
}

3. 函数调用

在这里插入图片描述

8. 单链表查找

  • 在单链表中查找数据域的值等于形参 x 的结点,并返回该结点的地址。
  • 如果链表中不存在 x 则返回 NULL。

实现代码

SLNode* SListFind(SLNode* plist, SLTDataType x)
{assert(plist);				//链表不为空SLNode* cur = plist;		//cur 用来访问当前结点while (cur != NULL)			//cur 没有走到链表末尾{if (cur->data == x)		//当前结点数据域的值等于形参 x{return cur;			//返回当前结点的地址}else					{cur = cur->next;	//继续往后找数据域等于 x 的结点}}return cur;					//链表中没有 x,返回 NULL
}

调用函数

在这里插入图片描述

9. 在指定 pos 位置前插入结点

1. 实现方法

  • 定义一个 cur (current) 指针指向当前结点,如果当前结点的下一个结点的地址等于 pos 的地址,则将 pos 指向的结点插入到新结点后,再将新结点插入到 cur 结点后。

在这里插入图片描述

2. 实现代码

void SLTInsert(SLNode** pplist, SLNode* pos, SLTDataType x)
{assert(pplist);						//传过来的不能是空指针assert((*pplist) && (pos));			//要么同时不为空assert(!(*pplist) && !(pos));		//要么同时为空SLNode* newnode = BuySListNode(x);	//创建新结点if (*pplist == pos)					//链表只有一个结点 - 头插{SListPopFront(pplist,x)			//新结点作首结点}else								//找 pos 位置的前一个{SLNode* cur = *pplist;			//cur 是 pos 位置的前一个结点while (cur->next != pos)		//当前结点的下个结点不是 pos {cur = cur->next;			//cur 向后寻找 pos 结点}newnode->next = pos;		//让新结点指针域指向 pos 结点cur->next = newnode;			//当前结点的指针域指向新结点}
}

调用函数

在这里插入图片描述

10. 删除指定 pos 位置的结点

1. 实现思路

  1. 在删除 pos 位置的结点前,先用一个前趋指针 pre 保存 pos 结点的前一个结点。
  2. 让 pre 指向 pos 的下一个结点。
  3. 销毁 pos 指向的结点。

在这里插入图片描述

2. 实现代码

void SLTErase(SLNode** pplist, SLNode* pos)
{assert(pplist);assert((*pplist) && (pos));		//两个指针同时不为空SLNode* pre = NULL;				//当前位置的前一个结点SLNode* cur = *pplist;			//用来寻找 pos 结点while (cur != NULL){if (cur != pos)			{pre = cur;cur = cur->next;}else						//cur 找到了 pos 指向的结点{pre->next = cur->next;	//pos 的前一个结点指向 pos 的后一个结点free(cur);				//删除 pos 指向的结点cur = NULL;}}
}

11. 单链表销毁

  • 对单链表连续执行头删。

实现代码

void SLTDestroy(SLNode** pplist)
{assert(pplist);SLNode* cur = *pplist;while (cur)						//链表不为空则执行头删{SLNode* next = cur->next;	//保存当前结点的下一个结点free(cur);					//释放当前结点cur = next;					//对下一个结点执行以上操作}*pplist = NULL;
}
查看全文

99%的人还看了

猜你感兴趣

版权申明

本文"【数据结构初级(2)】单链表的基本操作和实现":http://eshow365.cn/6-34077-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!