单链表经典OJ题(三)

news2024/10/7 1:29:19

目录

1、反转链表

2、合并两个有序链表

3、链表的中间结点

4、环形链表的约瑟夫问题

5、移除链表元素

6、移除元素


1、反转链表

206. 反转链表 - 力扣(LeetCode)

翻转链表的实质就是更改当前结点的前驱结点和后继结点

假设原链表为:1->2->3->4->5->NULL,具体解题思路如下: 

由题意得,反转后的链表为NULL<-1<-2<-3<-4<-5,那么我们假设三个指针用于实现这一反转过程,其中令cur指针指向原链表的第一个结点,pre指向链表最后一个结点的下一个结点也就是空结点NULL(既然是反转链表那就要全部反转完,即使是空结点也要反转,pre其实就相当于一个引路人的作用它告诉cur你要将你所指向结点的next指针指向我所指的对象),最后令tmp指针指向cur指针指向结点的下一个结点,具体情况如下图所示:

然后我们开始反转操作,首先要将链表第一个结点的后继结点变为NULL,所以要执行的操作就是cur->next = pre,此时原链表中的1->2就变成了NULL<-1 (与1->NULL一个意思只是为了方便理解写成了前者),而pre在完成NULL的引路工作后就要进行下一个引路工作了,它的下一个引路工作就是将1->2变为1<-2,所以他这时就要指向1,也就是cur此时指向的结点,所以此时令pre=cur即可,循环的最后为了防止链表只有一个结点就结束了,所以此时仍需令cur向前走一步然后再跳出循环,即令cur=tmp,这样就可以做到在下次循环开始时可以对下一个要操作的结点是否为空进行一个判断,如果不为空才能进入循环。

关于后续几次循环,具体操作过程不再过多赘述仅以以下图片展示每次循环后的结果:

 至此链表反转完成(虽然看着怪怪的但是的确是正确的)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* pre = NULL;
    struct ListNode* cur = head;
    while (cur) {
        struct ListNode* next = cur->next;
        cur->next = prev;
        pre = curr;
        cur = next;
    }
    return pre;
}

2、合并两个有序链表

21. 合并两个有序链表 - 力扣(LeetCode)​​​​​​​

具体解题思路如下: 

1、合并两个链表首先要保证两个链表都不为空,如果其中一个链表为空那么合并后的结果就是另一个非空链表:

//当传入的两个链表其中有一个为空,那么返回另一个链表即可
    if(list1 == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }

 2、如果两个链表都不为空,那么分别创建两个指针指向两个链表的头结点开始遍历两个链表,同时还要创建一个用于存放两个旧链表合并结果的新链表,我们这里创建一个带有哨兵位的单向链表这样后续进行尾插等操作就不需要考虑头结点是否为空的情况,减少重复代码

//当两个链表都不为空时,遍历链表
LSNode* cur1 = list1;
LSNode* cur2 = list2;

LSNode* newHead,*newTail;
newHead = newTail = (LSNode*)malloc(sizeof(LSNode));
newHead->val = -1;
newHead->next = NULL;

3、开始正式遍历两个旧链表,在遍历过程中需要注意的是只要有一个链表走到了头即cur1或cur2指向空那么就不能再循环了,在都没有走到头的时候,我们就判断cur1和cur2指向的结点的值的大小,如果cur1->val < cur2->val,就将此时cur1指向的结点尾插进新链表,如果cur1->val >= cur2->val,就将此时cur2指向的结点尾插进新链表(记得每次尾插过后新链表中的newTali也就负责指向新链表最后一个有效结点的指针要向后移动以便于下一次的尾插)

 //当两个结点有一个走到空就不能进行比较了
    while(cur1 && cur2)
    {
        //把值小的结点尾插到新的链表
        if(cur1->val < cur2->val)
            {
            newTail->next = cur1;
            newTail = newTail->next;
            cur1 = cur1->next;
            }
        //当cur2->val <= cur1->val时
        else
        {
            newTail->next = cur2;
            newTail = newTail->next;
            cur2 = cur2->next;
        }
    }

4、尾插结束后,如果cur2先指向空,那么就让cur1的后续结点尾插进新链表,如果cur1先指向空,那么就让cur2的后续结点尾插进新链表,如果cur1和cur2同时指向空,证明合并完成直接返回哨兵位的下一个结点作为新链表的头结点即可,同时记得释放为哨兵位申请的内存空间

if(cur1)
    newTail->next = cur1;
if(cur2)
    newTail->next = cur2;

return newHead->next;
free(newHead);

最终代码如下: 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 
typedef struct ListNode LSNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    //当传入的两个链表其中有一个为空,那么返回另一个链表即可
    if(list1 == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }

    //当两个链表都不为空时,遍历链表
    LSNode* cur1 = list1;
    LSNode* cur2 = list2;
    //创建新的空链表--带头(结点)单向不循环链表(后续进行尾插等情况就不需要考虑头结点是否为空的情况,减少重复代码)
    LSNode* newHead,*newTail;
    newHead = newTail = (LSNode*)malloc(sizeof(LSNode));
    newHead->val = -1;
    newHead->next = NULL;
    //当两个结点有一个走到空就不能进行比较了
    while(cur1 && cur2)
    {
        //把值小的结点尾插到新的链表
        if(cur1->val < cur2->val)
            {
            newTail->next = cur1;
            newTail = newTail->next;
            cur1 = cur1->next;
            }
        //当cur2->val <= cur1->val时
        else
        {
            newTail->next = cur2;
            newTail = newTail->next;
            cur2 = cur2->next;
        }
    }

if(cur1)
    newTail->next = cur1;
if(cur2)
    newTail->next = cur2;

return newHead->next;
free(newHead);
}

3、链表的中间结点

876. 链表的中间结点 - 力扣(LeetCode)

利用快慢指针的特性即可解决该题目:如果链表有效结点个数为单数,则当快指针指向的结点为空或快指针指向的下一个结点为空时,慢指针指向该链表的中间结点。如果链表有效结点个数为双数,则当快指针指向的结点为空或快指针指向的下一个结点为空时,慢指针指向该链表的中间两个结点的后一个结点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

typedef struct ListNode LSNode;
struct ListNode* middleNode(struct ListNode* head)
{   
    if(head == NULL)
        return NULL;
    
    //快慢指针
    LSNode* slow,*fast;
    slow = fast = head;
    //只要fast和fast->next有一个为空则停止循环
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    //当循环结束时,slow必定指向我们要找的链表中间结点
    return slow;
}

4、删除有序数组中的重复项

26. 删除有序数组中的重复项 - 力扣(LeetCode)

假设有序数组为{0,0,1,1,1,2,2,3,3,4},具体解题思路如下:

1、由于官方规定该题目的数组长度numsize大于等于1,所以不需要判0操作

2、初始化两个变量fast和slow为1,令它们两个充当数组下标(这里的fast和slow貌似虽然不是快慢指针但是作用类似),题目要求返回删除数组中重复项后新数组的长度,从官方给我们的多个实例中可以发现数组中的重复项都是相连的,因此我们可以通过覆盖重复项的操作来达到删除重复项的目的,及利用重复项后的数组元素覆盖掉重复的项(如果重复项后的元素也是重复项那就用它后面的数组元素继续覆盖它),而达成这一目的之前我们要保证的是这两个相邻的元素都不相同,如果相同那么就不能执行覆盖操作,需要继续寻找与之不同的元素将其覆盖掉才行

最终代码如下:

int removeDuplicates(int* nums, int numsSize)
{

    int fast = 1, slow = 1;

    while (fast < numsSize) 
    {
        if (nums[fast] != nums[fast - 1]) 
        {
            nums[slow] = nums[fast];
            ++slow;
        }
        ++fast;
    }
    return slow;
}

5、移除链表元素 

203. 移除链表元素 - 力扣(LeetCode)​​​​​​​

emm这道题很简单直接看注释即可......

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

typedef struct ListNode LSNode;
struct ListNode* removeElements(struct ListNode* head, int val){
    //还是申请哨兵位的老套路
    LSNode* newHead,*newTail;
    newHead = newTail = (LSNode*)malloc(sizeof(LSNode));
    newHead->val = -1;
    newHead->next = NULL;
    //令新指针pcur指向原链表的头结点head,遍历原链表
    LSNode* pcur = head;
    while(pcur)
    {
        //当不满足.val==val时,开始向新建的空链表中插入
        if(pcur->val != val)
            {
                //1、如果新建的链表为空,插入的新节点就是链表的头结点和尾结点
                if(newHead == NULL)
                    {
                        newHead = newTail = pcur;
                    }
                //2、如果新建的链表不为空,直接尾插,让新插进来的结点作为新的尾结点
                else
                    {
                        newTail->next = pcur;
                        newTail = newTail->next;
                    }
            }
        //当满足pcru->val = val,直接跳过进行下一个读取即可,这一点可以看题目中给的示例得到
        pcur = pcur->next;
    }
    //pcur指向NULL时跳出while循环,此时newTail->next指向的位置仍是旧链表存储数据6的结点,所以此时需要再判断newTail是否为空,如果不为空则让它最后指向的方向置为空,最后再返回哨兵位的下一个结点
    if(newTail)
        newTail->next = NULL;
    return newHead->next;
    free(newHead);
}

6、移除元素

27. 移除元素 - 力扣(LeetCode)

具体解题思路如下: 

1、声明并初始化两个变量 src 和 dst,分别表示源索引和目标索引。初始时,两者都为 0。

2、若当前位置上的元素等于目标值即 nums[src] == val,则增加源索引(跳过需要移除的元素)

3、若当前位置上的元素不等于目标值,则将当前位置上的元素复制到目标位置 nums[dst] = nums[src],同时增加源索引和目标索引(二者都向后走)

4、循环结束后,返回目标索引作为新数组长度

主要是通过遍历原始数组,将非目标值复制到新的位置来实现删除指定元素。它使用两个指针(其实也不算是指针)来追踪读取和写入操作,并根据是否遇到要删除的值来控制它们如何前进。最终达到将非目标值移到前面、覆盖掉要删除项并计算出新数组的长度的目标

具体代码如下:

int removeElement(int* nums, int numsSize, int val)
{
    int left = 0, right = numsSize;
    while (left < right) 
		{
        if (nums[left] == val) 
				{
            nums[left] = nums[right - 1];
            right--;
        } 
				else 
				{
            left++;
        }
    }
    return left;
}

~over~

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

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

相关文章

能链智电的野心,充电桩装不下

作者 | 张未 来源 | 洞见新研社 从油到电&#xff0c;从平台到资产运营方&#xff0c;从国内到国外&#xff0c;能链智电的野心&#xff0c;充电桩装不下。 “充电桩服务第一股”能链智电&#xff0c;曾作为平台型企业&#xff0c;连接了充电桩上下游&#xff0c;为充电桩制造…

王道数据结构课后代码题p19 第14题请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a,b,c) 中的最小距离。(c语言代码实现)

本题其实就是找a到c的最小值 有讲解p19 第14题 c语言实现王道数据结构课后代码题_哔哩哔哩_bilibili 下方有图&#xff1a; 本题代码如下 int abs(int a)//计算绝对值 {if (a < 0)return -a;elsereturn a; } int min(int a, int b, int c)//a是否为三个数中的最小值 {if …

hash算法

一、Hash散列算法介绍 1.引言 每个人在这个社会上生存&#xff0c;都会有一个属于自己的标记&#xff0c;用于区分不同的个体。通常使用名字就可以了。但是一个名字也并不能完全表示一个人&#xff0c;因为重名的人很多。所以我们可以使用一个身份证号或者指纹来表示独一无二…

当酱香碰上科技,茅台渴望的未来不仅仅是“加钱”

作者 | 曾响铃 文 | 响铃说 又涨价了。2023年11月1日起&#xff0c;贵州茅台宣布旗下53%vol茅台酒&#xff08;飞天、五星&#xff09;的出厂价格平均将上调20%&#xff0c;这也是茅台自2018年1月以来&#xff0c;近六年后再次迎来调整。 不过略有不同的是&#xff0c;本轮零…

专业的软件第三方检测机构如何做性能测试?收费标准是多少?

随着软件信息技术的飞速发展&#xff0c;人们对于软件产品越来越依赖&#xff0c;从而用户对软件产品的稳定性和质量问题愈发看重。软件系统性能的好坏将严重影响该软件的质量和软件开发者的利益&#xff0c;为了更好的保障软件产品质量&#xff0c;软件企业会将性能测试交由软…

爆火的迅雷网盘推广,一手云盘app拉新推广渠道必备项目 学习资料

迅雷网盘是目前几个主流网盘拉新推广之一 都可以通过”聚量推客“申请 目前主流的为&#xff1a;夸克网盘拉新、uc网盘推广、迅雷网盘&#xff0c;但是由于阿里的原因 夸克目前不对外开放名额&#xff0c;需要等待&#xff0c;取而代之主流的云盘推广就是迅雷网盘了 聚量推客…

第 1 章 概述 习题

1-1 因特网的前身是 1969 年创建的第一个分组交换网&#xff08;&#xff09;。 A. internet B. Internet C. NSFNET D. ARPANET 【答案】 D 【解析】 1-2 因特网上的数据交换方式是&#xff08;&#xff09;。 A. 电路交换 B. 报文交换 C. 分组交换 D. 光交换 【答…

uniapp项目笔记

1.生成二维码 import uqrCode from /static/erweima.js uqrCode.make({canvasId: qrcode,componentInstance: this,text: JSON.stringify(item.id),size: 150,margin: 0,backgroundColor: #ffffff,foregroundColor: #000000,fileType: jpg,errorCorrectLevel: uqrCode.errorCor…

阶段七-Day01-SpringMVC

一、Sping MVC的介绍 1. 使用Front(前端)设计模式改写代码 1.1 目前我们的写法 目前我们所写的项目&#xff0c;持久层、业务层的类都放入到Spring容器之中了。他们之间需要注入非常方便&#xff0c;只需要通过Autowired注解即可。 但是由于Servlet整个生命周期都是被Tomca…

RT-Thread STM32F407 五步完成OLED移植

这里使用RT-Thread Studio提供的IIC API驱动函数进行移植 第一步&#xff0c;进入RT-Thread Settings配置IIC驱动 第二步&#xff0c;进入board.h&#xff0c;定义IIC宏 第三步&#xff0c;进入STM32CubeMX&#xff0c;配置时钟及IIC 第四步&#xff0c;添加oled.c及oled…

AIGC大模型-初探

大语⾔模型技术链 1. ⾃然语⾔处理 2. 神经⽹络 3. ⾃注意⼒机制 4. Transformer 架构 5. 具体模型 - GPT6. 预训练&#xff0c;微调 7. ⼤模型应⽤ - LangChain 大语⾔模型有什么用&#xff1f; 利⽤⼤语⾔模型帮助我们理解⼈类的命令&#xff0c;从⽽处理⽂本分析…

gdb详解【Linux知识贩卖机】

你背朝太阳&#xff0c;就只能看到自己的影子。 --纪伯伦语录 文章目录 简介准备常用命令查看代码&#xff08;list&#xff09;运行&#xff08;run&#xff09;打断点&#xff08;break&#xff09;逐语句&#xff08;step&#xff09;逐过程&#xff08;next&#xff09;完成…

【FastCAE源码阅读9】鼠标框选网格、节点的实现

一、VTK的框选支持类vtkInteractorStyleRubberBandPick FastCAE的鼠标事件交互类是PropPickerInteractionStyle&#xff0c;它扩展自vtkInteractorStyleRubberBandPick。vtkInteractorStyleRubberBandPick类可以实现鼠标框选物体&#xff0c;默认情况下按下键盘r键开启框选模式…

程序员月入过万的秘密,赶快收藏史上最靠谱接单攻略!!!

近几年经济十分不景气&#xff0c;无论是哪一行总是面临着生活的不容易。不少人都选择多干几份工作来养家糊口&#xff0c;保证家人的生活。那么咱们程序员该如何是好呢&#xff1f;相信不少人已经有了答案&#xff0c;那就是网上接单&#xff01;那么本期就让小编带你一起来看…

如何设计开发一对一交友App吸引更多活跃用户

在当今社交媒体时代&#xff0c;一对一交友App开发正日渐成为发展热点。如何吸引更多活跃用户成为开发者们的首要任务。通过本文&#xff0c;我们将探讨一系列方法&#xff0c;助您设计开发一对一交友App&#xff0c;吸引更多用户的关注和参与&#xff0c;提升App的活跃度。 了…

【Linux】 ls -l 和 grep

语法:用于显示指定工作目录下之内容 ls [-alrtAFR] [name...]将 /bin 目录以下所有目录及文件详细资料列出: ls -lR /bin将 /usr/local/bin 目录以下所有有关python列出: ls -l /usr/local/bin/ | grep python在使用 ls -l 命令时&#xff0c;第一列的字符表示文件或目录的类…

基于单片机的塑料厂房气体检测系统设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、系统硬件设计三、软件设计实物 四、结论五、 文章目录 概要 本文首先分析了基于单片机的可燃…

树的概念及结构|树的三种表示方法

前言 以前我们学的线性结构是一对一的线性关系&#xff0c;但现实中&#xff0c;还有一对多的情况要处理&#xff0c;那就是树形结构。今天我们将学习树的概念及结构、和树的三种常见表示方法。 一、树的概念及结构 1、树的概念 树是一种非线性的数据结构&#xff0c;它是由n…

【NI-DAQmx入门】多通道数据采集

1.通道扩展解释 通道扩展是扩展数据采集设备的通道以包含另一个设备的通道的过程&#xff0c;从而有效地创建具有更多通道的任务。当使用通道扩展时&#xff0c;DAQmx 自动在 DAQmx 驱动程序级别路由触发器和时钟&#xff0c;以便多个设备同步。为了使设备作为一个整体运行&…

软件工程分析报告07测试计划书——基于Paddle的肝脏CT影像分割

目录 测试计划书 1. 引言 2. 测试目标 3. 测试方法 3.1 黑盒测试 (1)等价类划分&#xff1a; (2)边界值分析&#xff1a; (3)因果图&#xff1a; ​编辑&#xff08;4&#xff09;错误推测法 3.2 白盒测试 测试用例&#xff01;&#xff01; 4. 测试环境 5. 测试计划 6…