数据结构——单链表(下)

这篇具有很好参考价值的文章主要介绍了数据结构——单链表(下)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据结构——单链表(下),数据结构,数据结构

🌇个人主页:_麦麦_

📚今日名言:年轻时候的我以为坚持是永不动摇,到这个年纪明白了坚持就是犹疑着,退缩着,心猿意马着,一步三停着,还在往前走。——《十二月历》

数据结构——单链表(下),数据结构,数据结构

目录

一、引言

 二、单链表剩余功能的实现

        1.单链表的查找

        2. 单链表指定位置(pos)的前插

        3. 单链表指定位置(pos)的删除

        4.单链表指定位置(pos)的后插

        5.单链表指定位置(pos)的后删

         6.单链表的销毁

三、 与链表相关的练习题

1.删除表中等于给定值val的所有结点(力扣)

2.链表的中间结点(力扣)

 3.链表的倒数第k个结点(牛客)

四、结语


一、引言

         许久未更,甚是想念。今日为大家带来的是单链表剩余的功能实现以及一些有关链表的代码练习,希望能够为读者带来一点点收获,便心满意足了。

数据结构——单链表(下),数据结构,数据结构

 二、单链表剩余功能的实现

        1.单链表的查找

        在这期博文中我们会对单链表指定位置的插入和删除进行实现,熟悉单链表的小伙伴一定知道无论是插入还是删除,我们都需要寻找到指定数值所对应的位置指针,因此我们将寻找指定位置指针的过程封装成一个函数,避免了代码的重复化

        那么该如何根据指定值找到对应的指针呢?其实并不复杂,只需要对链表进行遍历并对每个结点的值进行判断,若是与指定值相符,便将该结点的地址返回。

        具体代码实现如下:

//寻找函数的声明
SListNode* SListFind(SListNode* phead, SLTDataType x);

//寻找函数的具体实现
SListNode* SListFind(SListNode* phead, SLTDataType x)
{
	SListNode *cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

        2. 单链表指定位置(pos)的前插

        由于单链表的查找函数只能返回指定位置的地址,而通过这个地址我们并不能访问它的前一个结点,自然也就不能在其之前插入一个新的结点,因此只能通过遍历链表的方式找到指定位置的前一个结点,再进行新结点的插入

        不过要注意的一点是如果pos是单链表的首个结点,那么其实现逻辑与单链表的头插相同,所以只需调用头插函数即可。

数据结构——单链表(下),数据结构,数据结构

//前插函数声明
void SLTInsert(SListNode** pphead, SListNode*pos, SLTDataType x);

//前插函数具体实现
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead);
	SListNode* newnode = BuySLTNode(x);
	SListNode* cur = *pphead;
	if (pos ==*pphead)
	{
		SLTPushFront(pphead, x);
	}

	while (cur)
	{
		if (cur->next == pos)
		{
			cur->next = newnode;
			newnode->next = pos;
		}
		cur = cur->next;
	}
}

        3. 单链表指定位置(pos)的删除

        单链表指定位置的删除与前插类似,需要通过遍历链表找到指定位置的之前的一个结点,并通过其将指定位置的节点删除。

        不过若是pos为链表的首个结点其实现就与单链表的头删相同,因此只需调用单链表的头删函数即可。

数据结构——单链表(下),数据结构,数据结构

//SLTErase函数的声明
void SLTErase(SListNode** pphead, SListNode* pos);

//SLTErase函数的实现
void SLTErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(pos);
	SListNode* prev = *pphead;
	//头插
    if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
        //找到pos的前一个位置
		while (prev)
		{
			if (prev->next == pos)
			{
				SListNode* Erase = prev->next;
				prev->next = prev->next->next;
				free(Erase);
				Erase = NULL;
			}
			prev = prev->next;
		}
	}
	
}

        4.单链表指定位置(pos)的后插

        相比于前插,后插在单链表中更为常见,也更为简单。只需要将pos所指向的下一个结点更改为新插入的结点,而新插入的结点则指向原pos的下一个结点。 

数据结构——单链表(下),数据结构,数据结构

//SListInsertAfter的声明
void SListInsertAfter(SListNode* pos, SLTDataType x);

//SListInsertAfter的实现
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	SListNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

        5.单链表指定位置(pos)的后删

         单链表指定位置的后删相较于指定位置的删除也更为简单,大体思路就是将指定位置的结点指向要删除的结点的下一个结点,并将要删除的结点释放

数据结构——单链表(下),数据结构,数据结构

//SListEraseAfter函数的声明
void SListEraseAfter(SListNode* pos);

//SListEraseAfter函数的实现
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	SListNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}

         6.单链表的销毁

        在销毁这个功能的实现上,采取的是一边遍历链表,一边对链表的结点进行释放。不过要注意的一点是如果传来的是NULL指针,就不需要进行销毁的操作了。

//SListDestroy函数的声明
void SListDestroy(SListNode** pphead);

//SListDestroy函数的实现
void SListDestroy(SListNode** pphead)
{
	assert(pphead);
	SListNode* cur=*pphead;
	SListNode* next = *pphead;
	//判断是否为空链表
	if (cur == NULL)
	{
		return;
	}
	else
	{
		while (cur->next)
		{
			next = cur->next;
			free(cur);
			cur = next;
		}
		printf("链表已销毁\n");
		return;
	}
}

         至此为止,但单链表基础功能的实现已告一段落,有兴趣的小伙伴可以继续深入,根据自己的需求写出另外的功能。接下来是几个与链表有关的编程题目讲解,以供大家学习和参考。

三、 与链表相关的练习题

1.删除表中等于给定值val的所有结点(力扣)

数据结构——单链表(下),数据结构,数据结构

         本文采取一种较为传统的方法来对此题进行解答,即双指针法。通过“prev”和“cur”两个指针对链表进行遍历,若是cur的val值与所给值相符,即将cur的空间释放,并进行两个指针的移动。

        不过若是按照以上思路来编写代码,会发现当链表的首个结点的val值就是所给值时,会对prev指针进行访问,而此时的prev指针还是空指针,就会出现对空指针的访问,显然是会出现问题的。这时候采取的方案就和我们在单链表中实现的头删类似,具体的代码如下:

数据结构——单链表(下),数据结构,数据结构

//结构体定义
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* cur=head;
    struct ListNode* prev=NULL;
    while(cur)
    {
        if(cur->val!=val)
        {
            prev=cur;
            cur=cur->next;
        }
        else
        {
            //头删
            if(prev==NULL)
            {
                head=cur->next;
                free(cur);
                cur=head;
            }
            else
            {
                prev->next=cur->next;
                free(cur);
                cur=prev->next;
            }
        }
    }
    return head;
}

2.链表的中间结点(力扣)

数据结构——单链表(下),数据结构,数据结构

         这道题较为传统的解法是先对链表进行遍历来记录下链表的结点个数,再根据结点个数进行第二次链表遍历来寻找到中间结点。但倘若只能对链表进行一次遍历,小伙伴们有什么好的想法吗?

        对此,我们采用了一个非常巧妙的“快慢指针”法,即设立两个指针,一个每次走两个结点,而另一个每次走一个结点,当走的快的指针走到末尾,慢的指针此时就走到了链表的中间结点。

        不过由于链表的结点的个数可能为奇数也可能为偶数,因此小伙伴们在对指针移动的条件加以补充,这里采取的是快指针为空指针或或者快指针的下一个结点为空来作为循环结束的条件。具体代码如下:

//结构体的定义
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode*fast=head;    //快指针
    struct ListNode*slow=head;    //慢指针
    while( fast && fast->next)   
    {
        fast=fast->next->next;
        slow=slow->next;
    }
    return slow;
}

 3.链表的倒数第k个结点(牛客)

数据结构——单链表(下),数据结构,数据结构

        本题其实也可以采取“快慢指针”的思路,只不过是空间上的快慢,即设立两个指针,让其中一个指针先行移动 k-1步,继而两个指针同时移动,当先行移动的指针来到链表的最后一个结点时,后移动指针就来到了倒数第k个结点。

        不过本题要注意的一个特殊情况就是当k值大于链表的结点个数的时候,便会出现快指针已经来到链表的最后一个结点时,仍旧要往后移动,这时候就导致了对空指针的访问,所以我们要对快指针移动后的位置进行判断,当来到最后一个结点时就返回NULL指针。

//结构体的定义
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k )
{
    struct ListNode*fast=pListHead;
    struct ListNode*slow=pListHead;
    //空链表
    if(pListHead==NULL)
    {
        return NULL;
    }
    while(--k)
    {
        fast=fast->next;
        //k大于链表的节点数
        if(fast==NULL)
        {
            return NULL;
        }
    }
    while(fast->next)
    {
       
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}

四、结语

         到此为止,有关单链表剩余的功能实现以及链表相关题目的讲解已经结束了。

         关注我 _麦麦_分享更多干货:_麦麦_的博客_CSDN博客-领域博主
         大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下期见!

数据结构——单链表(下),数据结构,数据结构文章来源地址https://www.toymoban.com/news/detail-798811.html

到了这里,关于数据结构——单链表(下)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构——单链表(上)

    🌇个人主页:_麦麦_ 📚今日名言:“生活总是让我们遍体鳞伤,但到后来,那些受伤的地方一定会变成我们最强壮的地方。”        ——海明威《永别了武器》 目录 ​编辑 一、前言 二、正言  3.1链表的概念及结构 3.2链表的分类 3.3链表的实现【无头单向非循环】 3.

    2024年02月02日
    浏览(40)
  • 【数据结构】单链表(超全)

    前言:  上一次我们分享了线性表中的一种结构顺序表,它存在着一些其缺点,比如:在中间位置或者头部进行元素插入或者删除的时候时间复杂度是 O ( N ) O(N) O ( N ) 效率比较低,并且顺序表在扩容的时候也存在时间和空间上的消耗,由于我们每次都是按照二倍来扩的,那

    2024年02月11日
    浏览(40)
  • 数据结构—单链表

    目录 1.前言 2.了解单链表 3.单链表代码实现       3.1 单链表结构体实现       3.2 创建节点       3.3  打印单链表       3.4 尾插        3.5  头插        3. 6 头删       3.7  尾删       3.8  查找       3.9  插入            3.9.1 在pos位置之前插入             3.9.

    2023年04月20日
    浏览(104)
  • 【数据结构】单链表(详解)

    所属专栏:初始数据结构 博主首页:初阳785 代码托管:chuyang785 感谢大家的支持,您的点赞和关注是对我最大的支持!!! 博主也会更加的努力,创作出更优质的博文!! 关注我,关注我,关注我,重要的事情说三遍!!!!!!!! 前面我们已经用顺序表方式来实现接口

    2023年04月24日
    浏览(52)
  • 数据结构:单链表

    单链表的全称为\\\"不带头单向不循环链表\\\" 注意:         下文提到的头节点与带头链表时两个概念,文中的头节点指的是第一个有效节点,二带头节点中的头指的是第一个无效节点 链表和顺序表一样,都是线性表,逻辑结构上是线性的,但不同的是,链表在物理结构上不是线性的

    2024年01月21日
    浏览(40)
  • 数据结构——单链表

    我们需要在程序中存储一系列的数据,这些数据不是一成不变的,需要满足随时的动态增删查改。 顺序表,虽然能满足这些要求,可是顺序表在头插或者中间插入数据时,需要将数据一个一个挪动,效率较低;而且需要频繁的扩容,扩容也是需要付出一定的代价。 为有效解

    2023年04月17日
    浏览(53)
  • 数据结构之单链表

    目录 1.什么是链表。 2.链表的优点 3.单链表的实现 1.单链表的结构体 2.节点的创建 3.参数的调用 4.头插  5.尾插 7.头删 8.尾删 8.在pos节点前插入x 9.在pos节点前插入x 10.删除pos位置节点  对于顺序表我们发现,在此基础上仍存在了些许不足: 1.中间或头部的插入时间复杂度为O

    2024年02月05日
    浏览(40)
  • 【数据结构】单链表(一)

    作者:日出等日落 专栏:数据结构 想变成仲夏夜的一只萤火虫,只要抓住你的注意力,就已经满足了。 目录 前言:  单链表的结构 :  逻辑结构: 物理结构: BuySLTNode(动态申请一个结点):   CreateSList(//循环创建结点): SLTPrint(单链表打印):  单链表的功能:  SL

    2024年02月01日
    浏览(44)
  • 数据结构·单链表

            不可否认的是,前几节我们讲解的顺序表存在一下几点问题:         1. 中间、头部的插入和删除,需要移动一整串数据,时间复杂度O(N)         2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗         3. 增容一般是2倍的增长,这势

    2024年01月25日
    浏览(75)
  • 【数据结构】单链表详解

    当我们学完顺序表的时候,我们发现了好多问题如下: 中间/头部的插入删除,时间复杂度为O(N) 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们 再继续插入了

    2024年02月09日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包