单链表经典OJ题(三)

目录

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~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入理解强化学习——马尔可夫决策过程:随机过程和马尔可夫性质

分类目录&#xff1a;《深入理解强化学习》总目录 下图介绍了强化学习里面智能体与环境之间的交互&#xff0c;智能体得到环境的状态后&#xff0c;它会采取动作&#xff0c;并把这个采取的动作返还给环境。环境得到智能体的动作后&#xff0c;它会进入下一个状态&#xff0c;把…

【电子通识】USB端口颜色编码标识

不知道你有没有发现 USB 口有不同的颜色&#xff0c;黑色、蓝色、紫色、红色、黄色等等&#xff0c;你知道不同颜色的 USB 口各代表什么意思吗&#xff1f; 这些颜色不是USB规范所要求的&#xff0c;设备制造商之间也不一致。例如&#xff0c;Intel使用橙色表示充电端口&#…

Spring Cloud学习(八)【RabbitMQ 服务异步通讯】

文章目录 初识 MQ同步通讯异步通讯MQ 常见框架 RabbitMQ 快速入门RabbitMQ 单机部署RabbitMQ概述常见消息模型 SpringAMQPSimpleQueue 模型WorkQueue 模型发布订阅模型发布订阅-Fanout Exchange发布订阅-DirectExchange发布订阅-TopicExchange消息转换器 初识 MQ 同步通讯 同步…

007 Linux fork()函数

前言 本文将会以提问的形式展开向你介绍fork函数 文章重点 关于fork函数&#xff0c;本文重点在于解决以下疑问 疑问一&#xff1a; 为什么fork之前的代码只有父进程执行&#xff0c;然而fork之后的代码父子进程都要执行 疑问二&#xff1a; 1、既然fork之后父子进程会执行一…

微信小程序:页面跳转传参问题

今天后端大兄弟突然拿着一个反编译过来的小程序源码&#xff0c;问能不能改。我心里直道好家伙&#xff0c;WebGIS开发的岗位&#xff0c;前端的活儿真是一个不少。大致看了看有几处是调整页面和接口修改的&#xff0c;源码部分和Vue项目语法十分相像&#xff0c;就临阵磨枪&am…

【java面试题】Integer对象输出结果是?

/** Copyright (c) 2006, 2023, webrx.cn All rights reserved.**/package cn.webrx;/*** <p>Project: wxbili2mp4 - Test* <p>Powered by webrx On 2023-11-14 20:28:46* <p>描述&#xff1a;<p>** author webrx [webrx126.com]* version 1.0* since …

2.3.5 交换机的VRRP技术

实验2.3.5 交换机的VRRP技术 一、任务描述二、任务分析三、具体要求四、实验拓扑五、任务实施1.交换机的基本配置 六、任务验收七、任务小结 一、任务描述 某公司的网络核心层原来采用一台三层交换机&#xff0c;随着网络应用的日益增多&#xff0c;对网络的可靠性也提出了越来…

生信分析|基因组倍型鉴定

简介 基因组倍型通常指一个生物体细胞中染色体的组合&#xff0c;即染色体数目的倍数。在生物学中&#xff0c;主要有两种类型的基因组倍型&#xff1a;单倍体和多倍体。 「单倍体&#xff08;Haploid&#xff09;&#xff1a;」 单倍体生物体的细胞中只包含每一对同源染色体的…

凸包的学习之路

学习视频选择的是&#xff1a;清华大学邓俊辉教授的《计算几何》课程 关于我为什么学习 凸包&#xff08;Convex Hull&#xff09;&#xff1f; ——在学习过程中遇到了凸包问题&#xff0c;凸包在CV领域的基础性&#xff0c;使我觉得深入了解凸包是必要的。此外&#xff0c;…

交换机基础知识之安全配置

交换机在网络基础设施中扮演着重要角色&#xff0c;它促进了设备之间数据包的流动。正因此&#xff0c;采取适当的安全措施来保护网络免受未经授权的访问和潜在攻击至关重要。本文将全面解读交换机基础安全配置知识&#xff0c;并提供实践方案&#xff0c;以保证安全的网络环境…

【Linux】-再谈动静态库-手把手教你自己制作一个动静态库

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

计算机缺失vcruntime140.dll如何修复?超简单的5个解决方法

在我们日常使用电脑的过程中&#xff0c;可能会遇到各种各样的问题和错误提示。其中&#xff0c;一个比较常见的错误提示就是“vcruntime140.dll丢失”。这个错误通常发生在我们尝试运行某个程序或应用时&#xff0c;系统无法找到或加载所需的vcruntime140.dll文件。 vcruntime…

Ubuntu安装mysql(解决ubuntu Access denied for user ‘root‘@‘localhost‘报错)

1、安装mysql sudo apt-get install mysql-server 上述命令会安装以下包&#xff1a; apparmor mysql-client-5.7 mysql-common mysql-server mysql-server-5.7 mysql-server-core-5.7 因此无需再安装mysql-client等。安装过程会提示设置mysql root用户的密码&#xff0c;设…

Java —— 继承

目录 1. 为什么需要继承 2. 继承概念 3. 继承的语法 4. 父类成员访问 4.1 子类中访问父类的成员变量 1. 子类和父类不存在同名成员变量 2. 子类和父类成员变量同名 4.2 子类中访问父类的成员方法 1. 成员方法名字不同 2. 成员方法名字相同 5. super关键字 6. 子类构…

Istio学习笔记-体验istio

参考Istio 入门(三)&#xff1a;体验 Istio、微服务部署、可观测性 - 痴者工良 - 博客园 (cnblogs.com) 在本章中&#xff0c;我们将会学习到如何部署一套微服务、如何使用 Istio 暴露服务到集群外&#xff0c;并且如何使用可观测性组件监测流量和系统指标。 本章教程示例使用…

【JAVA学习笔记】70 - 反射

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter23/src 反射 一、反射的引出 package com.yinhai.reflection.question;import com.yinhai.Cat;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IO…

全链路自动化测试

背景 从 SOA 架构到现在大行其道的微服务架构&#xff0c;系统越拆越小&#xff0c;整体架构的复杂度也是直线上升&#xff0c;我们一直老生常谈的微服务架构下的技术难点及解决方案也日渐成熟&#xff08;包括典型的数据一致性&#xff0c;系统调用带来的一致性问题&#xff0…

vue day1(主要是指令)

1、引包 或者&#xff1a;cdn网址 2、创建实例&#xff0c;初始化渲染 3、插值表达式 {{}} 表达式&#xff1a;可以被求值的代码 4、响应式数据&#xff1a;数据发生变化&#xff0c;视图自动更新&#xff08;底层是dom操作&#xff09; data中数据会被添加到实例上&#x…

微信自动添加好友

简要描述&#xff1a; 添加微信好友 请求URL&#xff1a; http://域名地址/addUser 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId…

易点易动固定资产管理系统:RFID技术助力快速盘点数万固定资产

在当今的企业管理中&#xff0c;高效和准确的固定资产盘点是至关重要的。传统的资产盘点方法通常耗时且易出错&#xff0c;这在快节奏的商业环境中是企业难以承受的。易点易动固定资产管理系统通过采用射频识别&#xff08;RFID&#xff09;技术&#xff0c;为企业提供了一种革…