数据结构——链表专题1

news2024/11/15 13:40:30

文章目录

  • 一、移除链表元素
  • 二、反转链表
  • 三、合并两个有序链表
  • 四、链表的中间节点
  • 五、环形链表的约瑟夫问题
  • 六、分割链表

一、移除链表元素

原题链接:移除链表元素

在这里插入图片描述

在这里插入图片描述

一个解法是遍历原链表,将与val相等的结点抛弃,链接后一个结点

在这里插入图片描述

另一个解法是创建一个新的链表,不保存与val相等的结点

在这里插入图片描述

注意:新的链表如果还没有插入结点,首先应该先将目的结点成为首结点
并且最后的尾结点的next应该置空

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* cur =  head;
    struct ListNode* phead = NULL;
    struct ListNode* ptail = NULL;
    while(cur)
    {
        if(cur->val != val)
        {
            //设置头结点
            if(phead == NULL)
            {
                phead = ptail = cur;
            }
            else
            {
                //尾插
                ptail->next = cur;
                ptail = ptail->next;
            }
        }
        cur = cur->next;
    }

    if(ptail)
    {
        ptail->next = NULL;
    }
    return phead;
}

二、反转链表

原题链接:反转链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一种解法是创建一个新的链表,遍历原链表,倒着储存进新的链表

另一个解法比较巧妙,需要三个指针
在这里插入图片描述

struct ListNode* reverseList(struct ListNode* head)
{
    if(head == NULL)
    {
        return head;
    }
    struct ListNode* n1 = NULL;
    struct ListNode* n2 = head;
    struct ListNode* n3 = head->next;
    while(n2)
    {
        n2->next = n1;
        n1 = n2;
        n2 = n3;
        if(n3)
        {
            n3 = n3->next;
        }
        
    }
    return n1;
}

三、合并两个有序链表

原题链接:合并两个有序链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路:创建一个新的链表,将上面两个链表遍历,将小的结点先储存在新的链表中
最后要么list1先遍历完,要么list2先遍历完,在写一个判断,将没有遍历完的链表的结点直接尾插到新的链表的末尾

注意:我们可以先创建一个哨兵位,方便我们直接插入数据,不在判断新的链表是否为空
在这里插入图片描述

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{

    if(list1 == NULL)
    {
        return list2;
    }

    if(list2 == NULL)
    {
        return list1;
    }
    //创建哨兵位
    struct ListNode* phead = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* ptail = phead;
    struct ListNode* pcur1 = list1;
    struct ListNode* pcur2 = list2;
    while(pcur1 && pcur2)
    {
        if(pcur1->val < pcur2->val)
        {
            ptail->next = pcur1;
            ptail = ptail->next;
            pcur1 = pcur1->next;
        }
        else
        {
            ptail->next = pcur2;
            ptail = ptail->next;
            pcur2 = pcur2->next;
        }
    }
    if(pcur1)
    {
        ptail->next = pcur1;
    }
    if(pcur2)
    {
        ptail->next = pcur2;
    }
    struct ListNode* ret = phead->next;
    free(phead);
    phead = NULL;
    return ret;
}

四、链表的中间节点

原题链接:链表的中间节点

在这里插入图片描述
在这里插入图片描述

使用快慢指针可以很好的解决这个问题
在这里插入图片描述

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

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

原题链接:环形链表的约瑟夫问题
在这里插入图片描述
在这里插入图片描述
这个问题起源于一个故事:

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

解决这道题,可以创建一个环形链表(循环链表),将题目的数据储存在链表的结点里面

在这里插入图片描述
然后创建两个指针,一个指向头结点,一个指向尾结点

在这里插入图片描述
如果满足条件,遇到要删除的结点,先将prev->next指向pcur->next,然后释放pcur结点,再将pcur结点往后移动一次

在这里插入图片描述
如果不满足条件,移动pcur和prev一次即可

注意:最后的循环结束条件是pcur->next = pcur,即只剩下一个结点,而不是pcur->next = prev,因为它们两个可能在移动中重合

在这里插入图片描述

//创建结点
struct ListNode* BuyNode(int x)
{
    struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if(newnode == NULL)
    {
        exit(1);
    }
    newnode->val = x;
    newnode->next = NULL;
    return newnode;
}
//创建环形链表
struct ListNode* CreatCirle(int n)
{
    struct ListNode* phead = BuyNode(1);
    struct ListNode* ptail = phead;
    for(int i = 2;i <= n;i++)
    {
        ptail->next = BuyNode(i);
        ptail = ptail->next;
    }
    //将尾结点和头结点连接起来
    ptail->next = phead;
    return ptail;
}
int ysf(int n, int m )
{
   int count = 1;
   struct ListNode* prev = CreatCirle(n);
   struct ListNode* pcur = prev->next;
   
   while(pcur->next != pcur)
   {
        if(count == m)
        {
            prev->next = pcur->next;
            free(pcur);
            pcur = prev->next;
            count = 1;
        }
        else
        {
            prev = pcur;
            pcur = pcur->next;
            count++;
        }
   }
   return pcur->val;
}

六、分割链表

原题链接:分割链表

在这里插入图片描述
在这里插入图片描述

解法一:创建两个新的链表,小的放在一起,大的放在一起,最后将小的链表后面合并上大的链表
解法二:在原链表的基础上,遍历链表,将小的位置不变,将大的尾插到链表的最后,删除前面的大的结点
解法三:创建一个新的链表,将小的结点头插,大的结点尾插

下面我们围绕解法一来解决这道题

在这里插入图片描述

为方便在新的链表里面插入结点,我们可以创建哨兵位,最后释放哨兵位就行了
注意:在合并后的链表里面,最后的结点的next一定要置为空并且它的位置在合并链表之前,因为大的链表如果没有结点,则只有一个哨兵位,没有next的指向,交换位置后会使用next指针导致出现错误

struct ListNode* partition(struct ListNode* head, int x)
{
    //空链表
    if(head == NULL)
    {
        return head;
    }

    //创建小的链表
    struct ListNode* lessnode = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* lesstail = lessnode;

    //创建大的链表
    struct ListNode* greaternode = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* greatertail = greaternode;

    //遍历链表
    struct 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 = greaternode->next;
    
    struct ListNode* phead = lessnode->next;
    //释放哨兵位
    free(lessnode);
    lessnode = NULL;
    free(greaternode);
    greaternode = NULL;
    return phead;
}

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

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

相关文章

被问了n遍的小程序地理位置权限开通方法

小程序地理位置接口有什么功能&#xff1f; 在平时我们在开发小程序时&#xff0c;难免会需要用到用户的地理位置信息的功能&#xff0c;小程序开发者开放平台新规要求如果没有申请开通微信小程序地理位置接口( getLocation )&#xff0c;但是在代码中却使用到了相关接口&#…

【go项目01_学习记录06】

学习记录 1 使用中间件1.1 测试一下1.2 push代码 2 URI 中的斜杆2.1 StrictSlash2.2 兼容 POST 请求 1 使用中间件 代码中存在重复率很高的代码 w.Header().Set("Content-Type", "text/html; charsetutf-8")统一对响应做处理的&#xff0c;我们可以使用中…

鸿蒙内核源码分析(原子操作篇) | 谁在为原子操作保驾护航

基本概念 在支持多任务的操作系统中&#xff0c;修改一块内存区域的数据需要“读取-修改-写入”三个步骤。然而同一内存区域的数据可能同时被多个任务访问&#xff0c;如果在修改数据的过程中被其他任务打断&#xff0c;就会造成该操作的执行结果无法预知。 使用开关中断的方…

【目标检测】Deformable DETR

一、前言 论文&#xff1a; Deformable DETR: Deformable Transformers for End-to-End Object Detection 作者&#xff1a; SenseTime Research 代码&#xff1a; Deformable DETR 特点&#xff1a; 提出多尺度可变形注意力 (Multi-scale Deformable Attention) 解决DETR收敛…

力扣每日一题115:不同的子序列

题目 困难 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 109 7 取模。 示例 1&#xff1a; 输入&#xff1a;s "rabbbit", t "rabbit" 输出&#xff1a;3 解释&#xff1a; 如下所示, 有 3 种…

2024视觉与学习青年学者研讨会(VALSE 2024)热点推文预告

视觉与学习青年学者研讨会&#xff08;VALSE&#xff09;是国内人工智能领域顶尖学者一年一度的研讨会。该会议的特点是大、全、新。会议的规模大&#xff0c;参会者达到五千人以上&#xff1b;会议的主题全&#xff0c;全面覆盖人工智能的各大领域&#xff1b;会议的内容新&am…

CSS Web服务器、2D、动画和3D转换

Web服务器 我们自己写的网站只能自己访问浏览&#xff0c;但是如果想让其他人也浏览&#xff0c;可以将它放到服务器上。 什么是Web服务器 服务器(我们也会称之为主机)是提供计算服务的设备&#xff0c;它也是一台计算机。在网络环境下&#xff0c;根据服务器提供的服务类型不…

【arduino】库的安装方法

arduino 库的安装方法 假设你已经安装好 Arduino IDE 以 OneButton 为例来介绍几种安装方法 文章目录 arduino 库的安装方法方法一&#xff1a;直接安装法方法二&#xff1a;导入 .ZIP库方法三&#xff1a;将库文件夹直接复制到贡献库路径下方法四&#xff1a;将库文件夹直接…

JAVA学习14——异常

目录 异常&#xff1a; 1.异常基本介绍&#xff1a; 2.异常体系图&#xff1a; 3.五大运行时异常&#xff1a; &#xff08;1&#xff09;NullPointerException空指针异常&#xff1a; &#xff08;2&#xff09;AirthmetiException数字运算异常&#xff1a; &#xff0…

投资海外标的,首选跨境ETF!现在新开佣金低至万0.5!

全球资产配置的利器 随着经济的发展&#xff0c;全球资产配置成为中产阶级的关注方向。目前&#xff0c;全球资产配置的主要渠道包括直接开立境外账户、 QDII 基金、跨境 ETF 等。 现阶段通过跨境 ETF 投资境外股市是最便利、最具效率的方式之一。首先&#xff0c;与直接境外…

Gradle 基础学习(三) 认识Command-Line Interface

Gradle命令行接口 除了IDE外&#xff0c;我们主要通过Gradle命令行接口来运行Gradle任务和管理Gradle项目。 下面是Gradle命令行使用的一些参考&#xff0c;熟悉后建议实际项目中使用Gradle Wrapper&#xff0c;gradle用法都可以替换为gradlew (macOS / Linux) 或gradlew.bat…

LVGL移植到STM32F4

1、LVGL简介 LittlevGL是一个免费的开源图形库&#xff0c;提供了创建嵌入式GUI所需的一切&#xff0c;具有易于使用的图形元素、漂亮的视觉效果和低内存占用。 1.1、LVGL特点 强大的构建模组&#xff1a;按钮、图表、列表、滑块、图像等先进的图形&#xff1a;动画、反锯齿…

hadoop学习---基于Hive的数仓搭建增量信息拉链表的实现

拉链表就是SCD2&#xff0c;它的优点是即满足了反应数据的历史状态&#xff0c;又能在最大程度上节省存储。 拉链表的实现需要在原始字段基础上增加两个新字段&#xff1a; start_time(表示该条记录的生命周期开始时间——周期快照时的状态)end_time(该条记录的生命周期结束时…

家政保洁上门预约服务小程序源码系统 带完整的安装代码包以及搭建教程

随着社会的快速发展和人们生活节奏的加快&#xff0c;家政保洁服务已成为现代生活中不可或缺的一部分。为了满足广大用户的需求&#xff0c;罗峰给大家分享一款家政保洁上门预约服务小程序源码系统&#xff0c;该系统不仅提供完整的安装代码包&#xff0c;还附带详细的搭建教程…

ContEA阅读笔记

Facing Changes: Continual Entity Alignment for Growing Knowledge Graphs 面对变化&#xff1a;不断增长的知识图谱的持续实体对齐 Abstract 实体对齐是知识图谱(KG)集成中一项基本且重要的技术。多年来&#xff0c;实体对齐的研究一直基于知识图谱是静态的假设&#xff…

嵌入式学习——C语言基础——day14

1. 共用体 1.1 定义 union 共用名 { 数据类型1 成员变量1; 数据类型2 成员变量2; 数据类型3 成员变量3; .. }; 1.2 共用体和结构体的区别 1. 结构体每个成员变量空间独立 2. 共用体每个成员变量空间共享 1.3 判断内存大小端 1. 内存大端…

从零开始搭建Springboot项目脚手架2:配置文件、返回值、日志等

1、多个环境与配置文件 2、统一返回值 返回值包括两种场景&#xff1a;正常controller的返回、异常发生之后返回 正常controller的返回&#xff1a;通过在controller的默认返回Response实现 异常发生之后返回&#xff1a;通过全局异常处理统一捕获返回 首先创建类StatusCode…

php使用Canal监听msyql

canal需要java8 去官网下载java8 安装JAVA #创建目录 mkdir -p /usr/local/java/ #解压到目录 tar zxvf jdk-8u411-linux-x64.tar.gz -C /usr/local/java/配置环境变量在 /etc/profile 最后加入 export JAVA_HOME/usr/local/java/jdk1.8.0_411 export CLASSPATH.:$JAVA_HOM…

常用六大加密软件排行榜|好用加密文件软件分享

为了保障数据安全&#xff0c;越来越多的企业开始使用文件加密软件。哪款加密软件适合企业哪些办公场景呢&#xff1f; 今天就给大家推荐一下文件加密软件排行榜的前六名&#xff1a; 1.域智盾 这款软件专为企业和政府机构设计&#xff0c;提供全面的文件保护解决方案。 点…

typescript类型基础

typescript类型基础 枚举类型 enum Season {Spring,Summer,Fall,Winter }数值型枚举 enum Direction {Up,Down,Left,Right } const direction:Direction Direction.up每个数值型枚举成员都表示一个具体的数字&#xff0c;如果在定义一个枚举的时候没有设置枚举成员的值&…