单链表的经典题目练习

哈喽,小伙伴们,上一次我们学习了单链表的知识,这次我们就要运用学到的知识来做一些相关的题目。我们都知道,要学好数据结构与算法,一定要多刷相关的题目才能有所提高。所以我们一起来学习一些单链表的经典题目算法题。

1.移除元素

题目简介:

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

思路分析:

思路1:遍历原链表,遇到val就执行删除val节点的操作

思路2:定义新链表,遍历原链表找不为val的节点,尾插在新链表中。而尾插分为2种情况

链表为空:插入进来的节点就是链表的头节点和尾节点。

链表不为空:插入进来的节点就是链表的新的尾节点

代码实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) 
{
    //定义新链表的头尾指针
    ListNode *newHead,*newTail;
    newHead = newTail = NULL;

    ListNode *pcur = head;
    while(pcur)
    {
        //不是val,尾插到新链表
        if(pcur->val != val)
        {
            if(newHead == NULL)
            {
                //新链表为空
                newHead = newTail = pcur;
            }
            else
            {
                //新链表不为空
                newTail->next = pcur;
                newTail = newTail->next;//让尾节点走到新的尾节点
            }
        }
        pcur = pcur->next;
    }
    if(newTail)
    {
        newTail->next = NULL;
    }
    return newHead;
}

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

2.链表的中间节点

题目描述 :

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

思路分析:

思路1:统计链表中节点的个数,通过除2找到中间节点。

思路2:快慢指针法。

代码实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) 
{
    ListNode *slow,*fast;
    slow = fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return  slow;
}

 注意,如果是下面这种写可能会运行失败。就是把循环的2个条件交换位置,当是这种写法的话如果节点数为奇数没有问题,而当节点数是偶数时,因为fast指针已经指向NULL,所以fast->next就会错误。当是原顺序的时候,遇到偶数节点,fast为NULL时为假,就是“短路”,不会判断第二个条件。

 题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

3. 反转链表

题目描述:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

 思路分析:大多数人的想法应该是先找尾节点,从尾节点向前一步一步改next,我也是这么想的,但是题目描述的是单链表,依次往后,所以这种方法不可行。

思路一:创建新链表,遍历原链表的节点将其插入到新的链表中。(头插)

思路二:创建3个指针。分别记录前驱节点,当前节点,后继节点,改变原链表指针指向。n1指向NULL,n2指向第一个节点,n3指向下一个节点。方法是先判断n2是否为空,不为空,让n2节点的next指向n1,然后让n1=n2,n2=n3,n3=n3->next,就这样依次循环,直到n2为空,n2为空时,n3也为空,当赋值的时候n3不能继续往下走了。

代码实现:

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) 
{
    //处理空链表
    if(head==NULL)
    {
        return head;
    }
    ListNode *n1,*n2,*n3;
    n1 = NULL, n2 = head, n3 = head->next;

    while(n2 != NULL)
    {
        //修改n2的指向
        n2->next = n1;
        //修改指针位置
        n1 = n2;
        n2 = n3;
        if(n3)
        {
            n3 = n3->next;
        }   
    }
    return n1;
}

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 

 4.合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

思路分析:创建一个新链表,定义newHead和newTail。在定义l1和l2分别指向2个链表的头,对l1和l2进行比较,谁小就尾插在新链表的尾部,然后l1或者l2往后走,并且newTail指向尾节点,循环往复。只要有一个链表的尾节点指向NULL,就跳出循环,然后另一个链表的节点就尾插在新链表中。

 如果一个链表的节点数多于另一个链表,那么跳出循环时不指向空的链表不用继续写一个循环来进行尾插,因为链表的节点都是一个一个链接在一起,只需尾插“第一个”节点就可以,后面的节点可以跟在后面。

代码实现:

typedef struct ListNode ListNode;
//list1和list2是2个链表的头节点
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
    if(list1 == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }
    ListNode *l1,*l2;
    l1 = list1;
    l2 = list2;

    ListNode *newHead,*newTail;//创建新链表
    newHead = newTail =NULL;

    while(l1 && l2)
    {
        if(l1->val < l2->val)
        {
            if(newHead==NULL)
            {
                //链表为空,头尾指针都指向该节点
                newHead = newTail =l1;
            }
            else
            {
                //链表不为空
                newTail->next = l1;
                newTail = newTail->next;
            }
            l1 = l1->next;
        }
        else
        {
            if(newHead==NULL)
            {
                //链表为空
                newHead = newTail =l2;
            }
            else
            {
                //链表不为空
                newTail->next = l2;
                newTail = newTail->next;
            }
            l2 = l2->next;
        }
    }
    //跳出循环存在2种情况:要么l1走到空了,l2不为空,或者相反
    //不可能存在都为空的情况
    if(l1)
    {
        newTail->next = l1;
        //newTail = newTail->next;//可写可不写
    }
    else
    {
        newTail->next = l2;
        //newTail = newTail->next;
    }
    return newHead;
}

根据代码可以看出有许多代码是重复出现的,那么想一想是不是可以对代码进行优化?答案是可以的 。解决方法就是通过哨兵位来解决,即就是带头的单链表

typedef struct ListNode ListNode;
//list1和list2是2个链表的头节点
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
    if(list1 == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }
    ListNode *l1,*l2;
    l1 = list1;
    l2 = list2;

    ListNode *newHead,*newTail;//创建新链表
    newHead = newTail = (ListNode*)malloc(sizeof(ListNode));

    while(l1 && l2)
    {
        if(l1->val < l2->val)
        {
            newTail->next = l1;
            newTail = newTail->next;
            l1 = l1->next;
        }   
        else
        {
            newTail->next = l2;
            newTail = newTail->next;
            l2 = l2->next;
        }
    }
    //跳出循环存在2种情况:要么l1走到空了,l2不为空,或者相反
    //不可能存在都为空的情况
    if(l1)
    {
        newTail->next = l1;
        //newTail = newTail->next;//可写可不写
    }
   if(l2)
    {
        newTail->next = l2;
        //newTail = newTail->next;
    }
    //malloc了空间,但是这块空间用不上,应该释放掉
    ListNode *ret = newHead->next;
    free(newHead);
    return ret;
}

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

5. 环形链表的约瑟夫问题

小故事:

据说著名犹太历史学家 Josephus有过以下的故事:在罗⻢⼈占领乔塔帕特后,39 个犹太⼈与
Josephus及他的朋友躲到⼀个洞中,39个犹太⼈决定宁愿死也不要被⼈抓到,于是决定了⼀个⾃杀方式,41个⼈排成⼀个圆圈,由第1个⼈开始报数,每报数到第3⼈该⼈就必须⾃杀,然后再由下⼀个重新报数,直到所有⼈都⾃杀⾝亡为⽌。
然⽽Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与⾃⼰安排在
第16个与第31个位置,于是逃过了这场死亡游戏。
题目描述:

编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。下一个人继续从 1 开始报数。n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?

思路分析:根据n来创建不带头单向循环链表,逢m删除当前节点。可以画一个图根据代码自己演算一遍。

代码实现

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param n int整型 
 * @param m int整型 
 * @return int整型
 */

#include <stdlib.h>
typedef struct ListNode ListNode ;
ListNode* BuyNode(int x)
{
    ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    if(newNode == NULL)
    {
        exit(1);
    }
    newNode->val = x;
    newNode->next = NULL;
    return newNode;
}

ListNode* createList(int n)
{
    ListNode* phead = BuyNode(1);
    ListNode* ptail = phead;
    for(int i=2;i<=n;i++)
    {
        ptail->next = BuyNode(i);
        ptail = ptail->next;
    }
    //链表要首尾相连使其循环
    ptail->next = phead;
    return phead;
}

int ysf(int n, int m ) 
{
    // write code here
    ListNode* head = createList(n);
    ListNode* pcur = head;
    ListNode* prev = NULL;

    int count = 1;
    while(pcur->next != pcur)
    {
        if(count == m)
        {
            //删除当前节点pcur
            prev->next = pcur->next;
            free(pcur);
            //删除pcur节点之后,要让pcur走到新的位置,count置为初始值
            pcur = prev->next;
            count = 1;
        }
        else 
        {
            //pcur往后走
            prev = pcur;
            pcur = pcur->next;
            count++;
        }
    }
    return pcur->val;
}

也有人会想当m为1的时候,代码就会出现问题,就是当m=1时,第一次进入循环就要删除pcur,而此时prev还是为空。解决办法就是让createlist不返回头节点,而是返回尾节点。既然知道第一个节点的前驱节点,也能够通过尾节点找到第一个节点。需要改以下代码。

6.分割链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你不需要 保留 每个分区中各节点的初始相对位置。

思路分析:

思路1:定义2个新的链表,一个放小于x的,一个放大于等于x的,最后将2个链表连接起来。

思路2:定义两个链表,大链表和小链表,遍历原链表的节点将其放到对应的新链表中,最后将大链表和小链表的首尾相连。如图所示

 代码实现

typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x)
{
    if(head==NULL)
    {
        return head;
    }
    //创建带头的大小链表
    ListNode *lessHead,*lessTail;
    ListNode *greaterHead,*greaterTail;

    lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));
    greaterHead = greaterTail = (ListNode*)malloc(sizeof(ListNode));

    //遍历原链表,将节点放到对应的新链表中
    ListNode* pcur = head;
    while(pcur)
    {
        if(pcur->val < x)
        {
            //放到小链表中
            lessTail->next = pcur;
            lessTail = lessTail->next;
        }
        else
        {
            //放到大链表中
            greaterTail->next = pcur;
            greaterTail = greaterTail->next;
        }
        pcur = pcur->next;
    }
    greaterTail->next = NULL;

    //大小链表进行相连
    lessTail->next = greaterHead->next;
    ListNode *ret = lessHead->next;
    free(greaterHead);
    free(lessHead);
    return ret;
}

题目连接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

好了,小伙伴们,如果看不懂的话,一定要动手自己画图来进行理解,通过图形来帮助记忆,另外题目的解法不只这一种,有其他解法的话可以分享出来,一起进行学习。 感谢大家的观看。

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

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

相关文章

操作系统透视:从历史沿革到现代应用,剖析Linux与网站服务架构

目录 操作系统 windows macos Linux 服务器搭建网站 关于解释器的流程 curl -I命令 名词解释 dos bash/terminal&#xff0c;(终端) nginx/apache&#xff08;Linux平台下的&#xff09; iis&#xff08;Windows平台下的&#xff09; GUI(图形化管理接口&#xff…

Multisim14.0仿真(四十九)共阴极/阳极7段数码管驱动设计

一、74LS47/48简介: 74LS47/48芯片是一种常用的七段数码管译码器驱动器,常用在各种数字电路和单片机系统的显示系统中. 二、74LS47/48引脚说明及定义: 7段显示译码器74LS47/48是输出低/高电平有效的译码器,74LS47/48除了有实现7段显示译码器基本功能的输入(DCBA)和输出(Ya…

小程序<swiper/>组件详解及使用指南

目录 引言微信小程序的重要性Swiper组件的角色与功能简介Swiper组件基础Swiper组件的定义与使用场景如何在微信小程序中引入Swiper组件Swiper组件的基本结构与属性Swiper组件的高级应用自定义Swiper指示点样式实现Swiper的动态效果(如自动播放、循环播放)说明引言 微信小程序…

时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测

时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测 目录 时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现…

【C++】类和对象之运算符重载(三)

前言&#xff1a;在前面我们知道在类和对象中有六个默认成员函数&#xff0c;并学习了其中三个构造函数、析构函数、拷贝构造函数&#xff0c;今天我们将进一步的学习.赋值运算符重载。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质…

[SWPUCTF 2021 新生赛]ez_unserialize

根据下面的user_agent和Disallow可以判断这个是在robots.txt 我们看的出来这是一个反序列化需要我们adminadmin passwdctf construct 构造方法&#xff0c;当一个对象被创建时调用此方法&#xff0c;不过unserialize()时却不会被调用 destruct 析构方法&#xff0c;PHP将在对象…

【学网攻】 第(20)节 -- 网络端口地址转换NAPT配置

系列文章目录 目录 系列文章目录 文章目录 前言 一、NAPT是什么&#xff1f; 二、实验 1.引入 实验目的 技术原理 实验步骤 实验设备 实验拓扑图 实验配置 实验验证 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第…

JavaGUI之SWT框架【阶段练习】

文章目录 效果展示选项卡界面创建划分右侧区域填充右侧上方Composite填充右侧下方Composite填充左侧Composite完整代码 SWT基础部分的内容以全部写完&#xff0c;现在让我们将以前学到的知识综合到一起&#xff0c;写一个小demo&#xff08;无交互功能&#xff09; 效果展示 选…

『运维备忘录』之 Systemd 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

Java实现批量视频抽帧2.0

继上个版本 对其进行略微升级 &#x1f913; 上个版本仅对一个视频进行抽帧处理 此版本可对一个文件夹内的全部视频进行抽帧并对应的文件夹进行帧图片的保存 1️⃣配置pom.xml &#xff08;保持上次不变&#xff09; <dependencies><dependency><grou…

Jmeter组件执行顺序与作用域(超详细整理)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;薪资嘎嘎涨 一、Jmeter重要组件 1&#xff09;配置元件---Config Element&#xff1a; 用于初始化默认值…

缓存组件Caffeine的使用

caffeine是一个高性能的缓存组件&#xff0c;在需要缓存数据&#xff0c;但数据量不算太大&#xff0c;不想引入redis的时候&#xff0c;caffeine就是一个不错的选择。可以把caffeine理解为一个简单的redis。 1、导入依赖 <!-- https://mvnrepository.com/artifact/com.git…

Apache POI与easyExcel:Excel文件导入导出的技术深度分析

在处理Excel文件时&#xff0c;Java开发者经常会面临多种选择&#xff0c;其中Apache POI和easyExcel是两个非常受欢迎的选择。这两个库都提供了强大的Excel文件处理功能&#xff0c;但在性能、内存使用、API设计以及扩展性方面有所不同。本文将深入分析Apache POI和easyExcel在…

留学生乱用ChatGPT真的太致命!被认定学术不诚信直接被退学

01.ChatGPT留学生神器&#xff1f;作业论文全靠它&#xff1f; 近期留学圈内最火热的话题&#xff0c;肯定是关于ChatGPT。 “这个python作业我写不来&#xff0c;让ChatGPT帮我直接生成code就好了。” “论文英文的写不来&#xff0c;ChatGPT直接生成一篇essay&#xff0c;…

C语言实现跳表(附源码)

最近在刷一些链表的题目&#xff0c;在leetcode上有一道设计跳表的题目&#xff0c;也是通过查阅各种资料&#xff0c;自己实现出来&#xff0c;感觉这是种很神奇的数据结构。 一.简介 跳表与红黑树&#xff0c;AVL树等&#xff0c;都是一种有序集合&#xff0c;那既然是有序…

修复wordpress安全漏洞

1. 问题描述&#xff1a; 用wordpress建了一个网站&#xff0c;但是学校反映说存在安全漏洞&#xff0c;通过接口https://xxx.xxx.edu.cn/?rest_route/wp/v2/users/可以访问到一些内容&#xff0c;希望可以关闭这个接口。 2. 解决办法 一共两步 &#xff08;1&#xff09;在fu…

Linux网络编程——udp套接字

本章Gitee地址&#xff1a;udp套接字 文章目录 创建套接字绑定端口号读取数据发送数据聊天框输入框 创建套接字 #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);int domain参数&#xff1a;表面要创建套接字的域…

Leetcode刷题笔记题解(C++):99. 恢复二叉搜索树

思路&#xff1a; 二叉搜索树的中序遍历是递增序列&#xff0c;可以在中序遍历中记录两个需要交换的节点&#xff0c;直到遍历完毕之后&#xff0c;对两个节点的值进行交换即可得到正确的二叉搜索树 比如中序序列为 1 2 3 7 5 6 4&#xff08;7比5大记录7为x&#xf…

Text Mesh Pro图文混排如何对任何图片都能实现

1&#xff09;Text Mesh Pro图文混排如何对任何图片都能实现 2&#xff09;Unity iOS平台的小图占用特别大的内存 3&#xff09;只在编辑器内&#xff0c;纹理不开启Read&Write情况下&#xff0c;如何获取纹理所有颜色值 4&#xff09;准备在海外发行游戏&#xff0c;有哪些…

STM32TIM时钟(1)

文章目录 前言一、介绍部分TIM简介了解定时器类型基本定时器框图通用定时器框图高级定时器框图定时器级联关系 所需简化定时器中断流程图时序部分预分频器时序计数器时序无影子寄存器计数器时序有影子寄存器计数器时序 时钟树 二、实例部分使用定时器计数使用对射红外传感器来控…