数据结构刷题训练——链表篇(一)

news2024/9/24 11:21:41

目录

前言

题目一:链表的中间节点

思路

分析

题解

 题目二:链表中倒数第k个结点

思路

分析

 题解

题目三:合并两个有序链表

思路

分析

题解

 方法二

题解

 题目四:链表的回文结构

思路

分析

题解

总结


前言

        今天我将开启一个新的专栏,数据结构与算法刷题训练营,题目从基础简单题目开始逐步进阶,以便于初学者巩固和运用所学的知识。


题目一:链表的中间节点

 题目描述:

 示例与提示:

 题目链接

链表的中间节点icon-default.png?t=N6B9https://leetcode.cn/problems/middle-of-the-linked-list/description/

 思路

        题目中的链表属于单链表,我们要怎么计算中间节点呢?先遍历一遍链表统计链表节点个数,然后计算出中间节点,再遍历到需要返回的节点。这或许是大多数人能想到的方法。但是这种方法效率太低。今天我向大家介绍一种新的做题思路,这种方法在其他题目中也是适用。

快慢指针法:

        我们可以创建两个指针,一个快指针一次走两步,一个慢指针一次走一步。当快指针走到尾时,返回慢指针就是中间节点。

分析

情况一:

节点为奇数个。

         假设节点有5个,那需要返回的节点就是第3个节点。初始时,两指针都指向第一个节点,慢指针一次走一步,快指针一次走两步。执行一次情况如上图。当快指针走到最后一个节点时就要结束如下图:

 情况二:

节点为偶数个。

         这是已经执行两次后的状态,接下来fast指针继续走:

         fast指向NULL,而slow指向要返回的节点。

题解

         了解完整体思路,我们依据分析中的情况进行编写代码:

struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* fast=head,*slow=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;

}

 

 题目二:链表中倒数第k个结点

 题目描述:

 题目链接

倒数第k个节点icon-default.png?t=N6B9https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&&tqId=11167&rp=2&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

思路

         这道题目依然可以使用快慢指针的方法来寻找倒数第k个节点。先让快指针走k步,然后让两指针同时向后移动,知道快指针遍历完链表结束。

分析

         初始情况下,两指针都指向第一个节点,先让fast指针走k步:

        我们假设要找倒数第3个节点,fast走3步就指向了第四个节点。

         然后两指针开始同时移动:

         当fast指针指向NULL时就结束,此时slow指向的就是倒数第k个节点。

 题解

 根据上述的分析,我们进行编写代码:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode* fast=pListHead,*slow=pListHead;
    
        
    for(int i=0;i<k;i++)
    {
        if(fast==NULL)
        {
            return NULL;
        }
        fast=fast->next;
    }
    while(fast)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}

        在代码实现时要注意特殊情况,如果链表为NULL或者k大于链表长度,传进来就要进行特殊处理。

题目三:合并两个有序链表

 题目描述:

 示例:

 题目链接:

合并两个有序链表icon-default.png?t=N6B9https://leetcode.cn/problems/merge-two-sorted-lists/description/

思路

        题目中给的是升序数组,解题思路就是比较链表元素,取小的进行尾插。思路较为简单。

分析

 接下来我们对整体规划进行分析假设初始时:

         把一个链表的元素插入到另一个链表这样操作太麻烦,所以我们可以重新创建一个头指针,将两个链表上的节点插入到新的链表中,创建新链表时,初始化头和尾都为NULL。

 

         然后进行比较,第一步两个大小相同,任取一个插入:

 

         然后再拿着list2中的第一个节点与list1节点进行比较,插入:

         以此类推不断进行比较尾插。

 

        结束条件就是一个链表为NULL结束 

 

         最后将剩余节点的链表尾插到新链表中,返回新链表的头。

题解

        理解了思路就根据分析的内容进行编写代码:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    if(list1==NULL)                //考虑原链表中有空链表的情况。
        return list2;
    if(list2==NULL)
        return list1;
    struct ListNode* head=NULL,*tail=NULL;
    while(list1&&list2)
    {
        if(list1->val > list2->val)//比较大小取小的尾插
        {
            if(head==NULL)         //考虑特殊情况新建链表为NULL时进行特殊处理
            {
                tail=head=list2;
                
            }
            else                   //尾插
            {
                tail->next=list2;
                tail=tail->next;
            }
            list2=list2->next;    //尾插后继续向后
        }
        else
        {
             if(head==NULL)
            {
              tail=head=list1;
               
            }
           else
           {
                tail->next=list1;
                tail=tail->next;
           }
           
            list1=list1->next;
        }
    }
    if(list2==NULL)            //一个链表为NULL时将另一个链表剩余的节点尾插到新链表。
        tail->next=list1;
    else
        tail->next=list2;
    return head;
}

 虽然思路非常简单,但是代码实现却很不容易,需要很多要考虑的特殊情况。

 

 方法二

        和思路一相同,也是比较大小,将小的尾插到新的链表。但是可以使用带头结点的链表,这样再插入时就不需要考虑新链表为NULL时的情况,进行特殊处理。可以更好的简化代码。

题解

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    if(list1==NULL)
        return list2;
    if(list2==NULL)
        return list1;
    struct ListNode* head=NULL,*tail=NULL;
    head=tail=(struct ListNode*)malloc(sizeof(struct ListNode));
    while(list1&&list2)
    {
        if(list1->val > list2->val)
        {
           
            
            tail->next=list2;
            tail=tail->next;
            list2=list2->next;
        }
        else
        {
             
            tail->next=list1;
            tail=tail->next;
           
            list1=list1->next;
        }
    }
    if(list2==NULL)
        tail->next=list1;
    else
        tail->next=list2;
    struct ListNode* del=head;
    head=head->next;
    free(del);
    return head;
}

         注意:原链表中不带头节点,返回时不能返回头节点,需要将头节点释放掉,返回头节点的下一个节点。

 

 题目四:链表的回文结构

题目描述:

 题目链接:

链表的回文结构icon-default.png?t=N6B9https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking

思路

        有人可能会想,这不很简单吗?把这个链表反转一下,再和原链表数据依次比较不就解决了,但是这里注意题目要求。

        题目要求时间复杂度为O(N),空间复杂度为O(1),上述思路需要将原链表复制一份后才可以与逆置是链表进行比较,空间复杂度为O(N)。

        这道题的思路是这样的,可以先找到链表的中间节点,然后从中间节点开始,对后部分的链表进行逆置,然后从中间开始与开头的节点对比,看两个值是否相同。

分析

 情况一:

         节点为偶数个,把后半部分节点逆置,然后依次比较,这里注意其实前半部分的2节点的next这时依然指向的是后半部分的2,后半部分的2节点的next指向NULL。如下图:

 逆置之后,返回的是后半部分1节点的地址,然后进行遍历,直到为NULL结束。

情况二:

        节点数量为奇数个

 实际情况和上述一样:

         但这里在比较的时候就要注意一下3节点,这里不需要把前半部分2节点的next置为NULL,当遍历到最后时,都遍历到了3节点一定是相同的

题解

        当然这些逆置、找中间节点的接口我们已经写过了,可以CV一下前边的代码,这样写代码还是很舒服的Ctrl+C、Ctrl+V

        当然借此我再介绍一种新的逆置链表的方法,我们可以使用头插来实现链表逆置。将原链表的节点依次头插到新的节点,这样就轻松实现了逆置。

代码如下:

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* cur=head;
    struct ListNode* newhead=NULL;
    while(cur)
    {
        struct ListNode* next=cur->next;
    //头插
        cur->next=newhead;
        newhead=cur;

        cur=next;
    }
    return newhead;
}

 整体题解:

class PalindromeList {
public:
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* cur=head;
    struct ListNode* newhead=NULL;
    while(cur)
    {
        struct ListNode* next=cur->next;
        cur->next=newhead;
        newhead=cur;

        cur=next;
    }
    return newhead;
}

struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* fast=head,*slow=head;
    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        
    }
    return slow;
}

    bool chkPalindrome(ListNode* A) {
        struct ListNode* mid=middleNode(A);//中间节点
        struct ListNode* rmid=reverseList(mid);//反转后的中间节点

       while(A && rmid)    
       {
        if(A->val!=rmid->val)
        {
            return false;
        }
        else
        {
            rmid=rmid->next;
            A=A->next;
        }
       }
       return true;
    }
};

 


 

总结

        好的本期内容到此结束,后续我将会分享更多数据结构相关的题目,通过画图逐步分析,来帮助大家刷题,这些题目建议大家先做一遍,然后看思路与分析,一定要动手敲一敲代码。最后,感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/854634.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

家政小程序开发制作

家政小程序是一种基于移动互联网的工具&#xff0c;旨在为用户提供方便快捷的家政服务。下面是家政小程序的功能介绍&#xff1a; 1. 家政服务展示&#xff1a;家政小程序可以展示各类家政服务的详细信息&#xff0c;包括清洁、保姆、月嫂、保洁等多种服务项目&#xff0c;以及…

【vue3】基础知识点-pinia

学习vue3&#xff0c;都会从基础知识点学起。了解setup函数&#xff0c;ref&#xff0c;recative&#xff0c;watch、computed、pinia等如何使用 今天说vue3组合式api&#xff0c;pinia 戳这里&#xff0c;跳转pinia中文文档 官网的基础示例中提供了三种写法 1、选择式api&a…

性能测试场景分析并设计?超细案例讲解

前言 性能测试场景&#xff0c;其实和功能测试没什么区别&#xff0c;只是侧重点不同。 我们在功能测试中经常用到的等价类边界值等分析和设计测试case的方法&#xff0c;目的是为了尽可能的覆盖业务场景&#xff0c;避免遗漏导致的功能逻辑缺失或者未达到预期。 而在性能测试…

【C++】初识模板

C模板入门 一、泛型编程 二、函数模板1. 函数模板的概念2. 函数模板格式3. 函数模板的原理4. 函数模板的实例化5. 模板参数的匹配原则 三、类模板 一、泛型编程 假设我们想实现一个交换函数&#xff0c;并且支持不同类型的参数实现&#xff0c;我们可以用 typedef 将类型进行重…

V2MOM工作法

V2MOM分别代表愿景&#xff08;vision&#xff09;、价值&#xff08;values&#xff09;、方法&#xff08;methods&#xff09;、障碍&#xff08;obstacles&#xff09;、衡量指标&#xff08;measurement&#xff09;。 第一&#xff0c;我真正想要的是什么呢&#xff1f;这…

ubuntu磁盘管理

show partition information 挂载设备在这 显示文件系统信息 build file system mkfs -t ext4 /dev/nvme0n1p4命令作用&#xff1a;将/dev/nvme0n1p4 格式化为 ext4 建立交换分区 mkswap -c -v1 /dev/nvme0n1p4 102400-c&#xff1a;check -v1&#xff1a;新版交换分区 -v0&…

四、web应用程序技术——HTTP

文章目录 1 HTTP请求2 HTTP响应3 HTTP方法4 URL5 HTTP消息头5.1 常用消息头5.2 请求消息头5.3 响应消息头 6 cookie7 状态码8 HTTP代理9 HTTP身份验证 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是访问万维网使用的核心通信协议&…

Linux 1.2.13 -- IP分片重组源码分析

Linux 1.2.13 -- IP分片重组源码分析 引言为什么需要分片传输层是否存在分段操作IP分片重组源码分析ip_createip_findip_frag_createip_doneip_glueip_freeip_expireip_defragip_rcv 总结 本文源码解析参考: 深入理解TCP/IP协议的实现之ip分片重组 – 基于linux1.2.13 计网理论…

2023Android面试,如果想卷请继续。备战金九银十

随着移动互联网的快速发展&#xff0c;Android开发岗位竞争也越来越激烈。作为一名Android程序员&#xff0c;面试是进入理想公司的重要一步。本文将分析市场对Android开发岗位的需求&#xff0c;分析2022年的Android开发岗位面试情况&#xff0c;并总结出历年来常见的面试题目…

MySQL中同比和环比语句如何写?

营收表如下&#xff08;表名&#xff1a;a&#xff09;如下图&#xff1a; 营收表 year month money 2021 1 1000 2021 2 1200 2022 1 1300 2022 2 1500 需要算出2022年营收同比与环比&#xff1a; 同比&#xff1a;和去年同月相比&#xff08;1300-1000/1000*100%&#xff0…

Python类的设计

Python类的设计 # 定义一个闹钟类 class Clock:__cureen_keyNone # 私有成员不能改变和使用def __init__(self, id, price): # 类对象是立即自动执行self.id idself.price pricedef ring(self):import winsound # 内置声音方法winsound.Beep(2000,3000)clock1 Clock(…

自然语言处理学习笔记(六)————字典树

目录 1.字典树 &#xff08;1&#xff09;为什么引入字典树 &#xff08;2&#xff09;字典树定义 &#xff08;3&#xff09;字典树的节点实现 &#xff08;4&#xff09;字典树的增删改查 DFA&#xff08;确定有穷自动机&#xff09; &#xff08;5&#xff09;优化 1.…

Python基础--序列操作/函数

Python基础 1.序列的操作 2.函数 1. 数据类型的具体操作 1.1 序列操作--列表具体操作&#xff1a; #定义列表 listA [] #定义一个空列表 listB [1,2.8,"你好",listA,[1,2,3]] # 访问列表 print(listB)#查看整个列表 print(listB[2])#查看单个…

docker 安装mongodb 虚拟机安装mongodb

生产环境直接安装比较好&#xff0c;以及使用集群环境&#xff0c;本文仅测试交流使用&#xff0c;我用来写分布式im测试使用&#xff1a; nami-im: 分布式im, 集群 zookeeper netty kafka nacos rpc主要为gate&#xff08;长连接服务&#xff09; logic &#xff08;业务&…

MySQL:内置函数、复合查询和内外连接

内置函数 select 函数; 日期函数 字符串函数 数学函数 其它函数 复合查询&#xff08;多表查询&#xff09; 实际开发中往往数据来自不同的表&#xff0c;所以需要多表查询。本节我们用一个简单的公司管理系统&#xff0c;有三张 表EMP,DEPT,SALGRADE来演示如何进行多表查询…

设计模式行为型——访问者模式

目录 访问者模式的定义 访问者模式的实现 访问者模式角色 访问者模式类图 访问者模式举例 访问者模式代码实现 访问者模式的特点 优点 缺点 使用场景 注意事项 实际应用 访问者模式的定义 访问者模式&#xff08;Visitor Pattern&#xff09;属于行为型设计模式&am…

总结950

7:00起床 7:30~8:00复习单词300个&#xff0c;记忆100个 8:10~9:30数学660&#xff0c;只做了10道题&#xff0c;发现对各知识点的掌握程度不一。有些熟练&#xff0c;有些生疏 9:33~10:25计算机网络课程1h 10:32~12:02继续660&#xff0c;也不知道做了几道 2:32~4:00数据…

Node.js |(四)HTTP协议 | 尚硅谷2023版Node.js零基础视频教程

学习视频&#xff1a;尚硅谷2023版Node.js零基础视频教程&#xff0c;nodejs新手到高手 文章目录 &#x1f4da;HTTP概念&#x1f4da;窥探HTTP报文&#x1f4da;请求报文的组成&#x1f407;HTTP请求行&#x1f407;HTTP请求头&#x1f407;HTTP的请求体 &#x1f4da;响应报文…

阔别三年,领先回归!别克LPGA锦标赛申城十月再启高球盛会

2023年8月4日——2023年金秋十月&#xff0c;阔别中国赛场已久的别克LPGA锦标赛将强势归来&#xff0c;于10月12日至15日在上海旗忠花园高尔夫俱乐部再次拉开帷幕。作为三年来首个回归、同时也是今年国内唯一开赛的国际顶级高尔夫职业赛事&#xff0c;别克LPGA锦标赛将吸引全世…

零代码集成融云连接更多应用

融云是安全、可靠的全球互联网通信云服务商&#xff0c;向开发者和企业提供即时通讯和实时音视频通信云服务。 场景描述&#xff1a; 基于融云的开放api能力&#xff0c;无代码集成融云平台的音视频通话、即时通信、聊天室、短信等业务&#xff0c;使融云连通其它应用。通过A…