数据结构——单链表OJ题(下)

news2024/11/13 8:40:55

目录

一、链表的回文结构

思路一:数组法

(1)注意

(2)解题

思路二:反转链表法

(1) 注意

(2)解题

二、相交链表

(1)思路:快慢指针

(2)注意

(3)解题

 三、环形链表1

(1)思路:快慢指针

(2)解释

(3)注意

(4)解题

①快指针走两步

② 快指针走三步

四、环形链表2

(1)思路:快慢指针

(2)解释

(3)注意

(4)解题

五、随机链表的复制

(1)思路

(2)解题

六、写在最后 


一、链表的回文结构

思路一:数组法

将链表中结点的数据存储在数组中,创建两个指针分别从左右两边遍历并比较,如果左右对称则说明是回文结构。

(1)注意

①为什么要创建数组存储数据?因为链表的结点不是连续的,不能进行逆向访问;

②从左右两端进行遍历时,需满足left<right的条件;

③该方法只适用于链表长度小于等于900的条件下,因为数组的长度最大为900。

(2)解题

typedef struct ListNode ListNode;
bool chkPalindrome(ListNode* A) 
    {
        //创建数组存储数据
        int arr[900] = {0};
        //数据的个数为i
        int i = 0;
        ListNode* pcur = A;
        //遍历链表,将数据存储在数组中
        while(pcur)
        {
            arr[i++] = pcur->val;
            pcur = pcur->next;
        }
        int left = 0;
        int right = i - 1;
        while(left < right)
        {
            //若有不相等,说明不是回文结构
            if(arr[left] != arr[right])
            {
                return false;
            }
            left++;
            right--;
        }
        //跳出循环,说明left和right指向的数据一直相等,即回文结构
        return true;
        
    }

思路二:反转链表法

使用快慢指针法找到原链表的中间结点,将中间结点之后的链表进行反转,最后将原链表的前半段与新链表存储的数据进行比较。

(1) 注意

①寻找中间结点和反转链表在上节已练习,可将它们封装成两个函数;

②由于原链表的长度大于新链表(反转后的),因此循环结束的判定条件是p2==NULL。

(2)解题

class PalindromeList {
    //寻找中间结点的函数
    ListNode* midNode(ListNode* head)
    {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
    //将链表进行反转的函数
    ListNode* reverse(ListNode* head)
    {
        ListNode* n1, * n2 , *n3;
        n1 = NULL;
        n2 = head;
        n3 = head->next;
        while(n2)
        {
            n2->next = n1;
            n1 = n2;
            n2 = n3;
            if(n3)
            {
                n3 = n3->next;
            }
        }
        return n1;
    }
public:
    bool chkPalindrome(ListNode* A) 
    {
        //原链表的中间结点
        ListNode* mid = midNode(A);
        //将中间结点之后的链表进行反转
        ListNode* p2 = reverse(mid);
        ListNode* p1 = A;
        //遍历两个链表的值,比较是否相等
        while(p2)
        {
            //存在不相等的,说明不是回文结构
            if(p1->val != p2->val)
            {
                return false;
            }
            p1 = p1->next;
            p2 = p2->next;
        }
        //跳出循环,说明left和right指向的数据都相等,即为回文结构
        return true;
    }
};

二、相交链表

(1)思路:快慢指针

首先找到两个链表长度的差值k,创建两个指针指向头结点,快指针先走k步,接着快慢指针一起走,若快慢指针指向同一个结点,则说明该结点为相交结点。

(2)注意

①计算差值时无法确定两个长度谁大谁小,可使用abs()绝对值函数;

②通过比较两个长度的大小来确定快指针指向哪个链表的头结点;

③判断快慢指针指向的结点是否相同,而非结点指向的数据!!

(3)解题

typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    //计算两个链表的长度
    int len1 = 0;
    int len2 = 0;
    ListNode* pcur = headA;
    while(pcur)
    {
        pcur = pcur->next;
        len1++;
    }
    pcur = headB;
    while(pcur)
    {
        pcur = pcur->next;
        len2++;
    }
    //两个链表长度的差值k
    int k = abs(len1 - len2);
    //快指针指向长链表的头结点
    ListNode* fast = headA;
    ListNode* slow = headB;
    if(len1 < len2)
    {
        fast = headB;
        slow = headA;
    }
    //快指针先走k步
    while(k--)
    {
        fast = fast->next;
    }
    //比较两个指针指向的结点是否相同
    while(fast && slow)
    {
        if(fast == slow)
        {
            return fast;
        }
        fast = fast->next;
        slow = slow->next;
    }
    return NULL;
}

 三、环形链表1

(1)思路:快慢指针

创建快慢指针并指向头结点,快指针一次经过两个结点,慢指针一次经过一个结点,若最终快指针与慢指针相遇,则说明该链表是环形链表。

(2)解释

①为什么在起点相同的情况下,快指针走两步,慢指针走一步,两者可以相遇,会不会遇不上?

假设此时fast进入环,slow走完入环前的距离准备入环,此时它们之间的距离为N。在接下来的追逐中,它们每追击(变换位置)一次,两者之间的距离缩小一步,直至相遇。(参考下图理解)

 

②如果慢指针一次走一步,快指针一次走3、4、5...n步,快慢指针还能再相遇吗?

可以!以快指针一次走3步为例:

slow入环后,slow和fast在环内进行追逐,假设两者之间的距离为N,那么在之后的过程中,每追逐一次两者之间的距离就缩小两步。

若N为偶数,最终距离可缩小为0,即相遇;若N为奇数,假设环的长度为C,会出现套圈情况(即slow走在fast前面),此时两者之间的长度为C-1。

此时需要讨论C-1是奇数还是偶数,若C-1为偶数,则可以相遇。若C-1为奇数,两者还会错过,继续套圈,那么fast和slow还会再相遇吗?

假设slow走完入环前的距离L,刚准备入环;fast在环里走了x圈,且此时fast和slow之间的距离为N,那么两者走过的长度分别为:slow : L     fast : L + xC + (C - N)。又因为fast的速度是slow的3倍,因此3L =  L + xC + (C - N),化简得:2L = (x + 1)C - N

上述条件中:N为奇数,C为偶数。因为:偶数= 偶数-偶数,偶数=奇数-奇数。那么(x+1)C为奇数时,会相遇。因此,如果慢指针一次走一步,快指针一次走3步,快慢指针还能再相遇。

同理可得其它步数的情况下也可相遇。

(3)注意

①循环结束的判定条件是fast && fast->next:若fast为空则说明不是环形链表;

②不能写成fast->next && fast,要先判断fast,因为如果fast为空,就不能取next;

③虽然证明了快指针不论走多少步都可以满足在带环链表中相遇,但是在编写代码的时候会有额外的步骤引入,涉及到快慢指针的算法题中通常习惯使用慢指针走一步、快指针走两步的方式。

(4)解题

①快指针走两步
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) 
{
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            return true;
        }
    }
    return false;
}
② 快指针走三步
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) 
{
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        //快指针一次走三步
        int n = 3;
        while(n--)
        {
            if(fast->next)
            {
                fast = fast->next;
            }
            else 
            {
                return false;
            }
        }
        if(fast == slow)
        {
            return true;
        }
    }
    return false;
}

四、环形链表2

(1)思路:快慢指针

创建两个指针,一个从链表的头结点开始遍历链表,另一个从快慢指针的(判断是否为环形链表的)相遇点开始绕环运行,两个指针都是一次走一步,最终会在入口点的位置相遇。

(2)解释

①为什么相遇点和头结点到入环结点的距离是相等的?

假设环的长度为R,入环之前的距离为L,入环节点到相遇点的距离为X,那么相遇点到入环结点之间的距离为R-X。

在判环时(此时在相遇点),快慢指针走的路程为:slow : L+X     fast :L+nR+X(其中n≥1)。

由于判环时,快指针一次走两步,慢指针一次走一步,则快指针的路程时慢指针的两倍。且慢指针入环之后,快指针一定会在一圈之内追上慢指针(因为慢指针入环之后,两者之间的距离最大为环的长度,而每追击一次,两者之间的距离缩短一步,因此在一圈之内,快指针一定能追上慢指针)。

那么:2*(L+X)= L+nR+X,化简为:L = nR - X,即L = (n -1)R+(R-X)。在极端情况下:假设n=1,则:L=R-X,即头结点到和相遇点到入环结点之间的距离相等。

(3)注意

①fast && fast->next位置的注意事项同上。

(4)解题

typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) 
{
    ListNode *fast = head;
    ListNode *slow = head;
    while(fast && fast->next)
    {
        //fast一次走两步,slow一次走一步
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            ListNode* pcur = head;
            //判断两者何时相遇
            while(pcur != slow)
            {
            pcur = pcur->next;
            slow = slow->next;
            }
            return slow;
        }
    }
    //链表不带环
    return NULL;
}

五、随机链表的复制

 

(1)思路

在原链表的基础上继续复制链表,置random指针(copy->random = pcur->random->next),最后将复制链表和原链表断开,得到复制链表。

(2)解题

typedef struct Node Node;
Node* buyNode(int x) {
    Node* newnode = (Node*)malloc(sizeof(Node));
    newnode->val = x;
    newnode->next = newnode->random = NULL;
    return newnode;
}
void AddNode(Node* phead) {
    Node* pcur = phead;
    while (pcur) 
    {
        // 存储pcur的下一个结点
        Node* Next = pcur->next;
        // 创建新结点,尾插到pcur
        Node* newnode = buyNode(pcur->val);
        pcur->next = newnode;
        newnode->next = Next;
        pcur = Next;
    }
}
struct Node* copyRandomList(struct Node* head) {
    if(head == NULL)
    {
        return NULL;
    }
    // 1.在原链表上复制结点
    AddNode(head);
    // 2.置random
    Node* pcur = head;
    while (pcur) 
    {
        Node* copy = pcur->next;
        if (pcur->random != NULL) 
        {
            copy->random = pcur->random->next;
        }
        pcur = copy->next;
    }
    // 3.断开链表
    pcur = head;
    Node *newHead, *newTail;
    newHead = newTail = pcur->next;
    while (pcur->next->next) 
    {
        pcur = pcur->next->next;
        newTail->next = pcur->next;
        newTail = newTail->next;
    }
    return newHead;
}

六、写在最后 

 链表既然包括单链表,就还有其他种类啦~

敬请期待“双向链表”~

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

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

相关文章

音频合成之百度飞浆微调保姆级教程

&#x1f3af; 这里我们使用&#xff0c;百度飞浆产品进行操作 至少需要提供一分钟的原声视频&#xff0c;越清晰越好&#xff0c;用于分析人物音色、声纹等特点。预防针&#xff1a;这块稍微有点难度&#xff0c;涉及代码&#xff0c;不过不用担心&#xff0c;照着操作即可。…

加载chatglm3模型时出现ValueError: too many values to unpack (expected 2)的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

MATLAB仿真:数字信号处理IIR数字滤波器设计

目录 1&#xff0e;实验目的 2&#xff0e;实验原理 3&#xff0e;实验仪器及设备 4. 实验内容及步骤 5&#xff0e;信号产生函数mstg清单 6.实验程序及波形如下&#xff1a; 1&#xff0e;实验目的 &#xff08;1&#xff09;熟悉用双线性变换法设计IIR数字滤波器的原理…

独立开发者系列(35)——python环境的理解

新手阶段&#xff0c;为了快速入门&#xff0c;基本都是直接开始写python代码实现自己想要的效果&#xff0c;类似搭建博客&#xff0c;写个web服务器&#xff0c;搭建简易聊天室&#xff0c;偶尔也写些爬虫&#xff0c;或者使用pygame写个简单小游戏&#xff0c;也有tk库做点简…

Android AI应用开发:移动检测

基于Google ML模型的Android移动物体检测应用——检测、跟踪视频中的物体 A. 项目描述 ML Kit物体检测器可以对视频流进行操作&#xff0c;能够检测视频中的物体并在连续视频帧中跟踪该物体。 相机捕捉视频时&#xff0c;检测到移动物体并为其生成一个边界框&#xff0c;并分…

【算法/训练】:动态规划(线性DP)

一、路径类 1. 字母收集 思路&#xff1a; 1、预处理 对输入的字符矩阵我们按照要求将其转换为数字分数&#xff0c;由于只能往下和往右走&#xff0c;因此走到&#xff08;i&#xff0c;j&#xff09;的位置要就是从&#xff08;i - 1&#xff0c; j&#xff09;往下走&#…

2024第八届全国职工职业技能大赛“网络与信息安全管理员”赛项技术文件及任务书

2024第八届全国职工职业技能大赛“网络与信息安全管理员”赛项技术文件及任务书 一、赛项概述&#xff1a;二、竞赛形式&#xff1a;三、竞赛规则四、竞赛样题4.1、第一场4.1.2、实操闯关赛4.2、第二场4.3、第三场 需要培训可以私信博主 欢迎交流学习&#xff01; [X] &#x1…

Tuxera NTFS for Mac 2023安装教程+2024年软件Tuxera安装包下载

‌在数字化时代&#xff0c;文件格式的兼容性问题一直是用户头疼的问题。尤其是在Mac和Windows两大操作系统之间&#xff0c;由于文件系统的不统一&#xff0c;使得文件在不同平台之间的传输和访问变得困难。然而&#xff0c;随着Tuxera NTFS for Mac 2023的出现&#xff0c;这…

好书推荐 -- 《精通推荐算法》

新书发布&#xff0c;京东限时15天内5折优惠&#xff0c;半天即可送到。 图书封底有读者微信群&#xff0c;作者也在群里&#xff0c;任何技术、offer选择和职业规划的问题&#xff0c;都可以咨询。 《精通推荐算法》&#xff0c;限时半价&#xff0c;半日达https://u.jd.com…

[CISCN2019 华东南赛区]Web11

进来先做信息收集&#xff0c;右上角显示当前ip&#xff0c;然后有api的调用地址和请求包的格式以及最重要的是最下面的smarty模版&#xff0c;一看到这个就得想到smarty模版注入 测试了一下两个api都无法访问 直接切到数据包看看能不能通过XFF来修改右上角ip 成功修改&#x…

Unity横版动作游戏 -瓦片地形和动画瓦片

(规则瓦片)瓦片地形和动画瓦片 准备阶段 在Tilemap中创建一个新的文件夹起名叫做Rule Tile&#xff0c;创建一个Rule Tile&#xff0c;用来设置瓦片地形&#xff0c;我们将用他来绘制地形图&#xff0c;类似于Godot中的瓦片地形。 这里给他取名为了Ground 1&#xff0c;用于创…

ARM32开发——PWM蜂鸣器案例

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 需求原来的驱动移植操作替换初始化 更新Play函数完整代码 需求 通过控制PB9来播放音乐&#xff0c;PB9对应的定时器通道&#xff1…

CTF之网站被黑

简单看一下网页和源码没发现什么明显漏洞 那就扫描一下目录 发现了/shell.php文件&#xff0c;访问一下&#xff0c;发现是一个后台管理登录页面 别无他法只能爆破喽&#xff0c;爆破后发现密码是hack flag{25891d9e9d377f006eda3ca7d4c34c4d}

2024第三届钉钉杯大学生大数据挑战赛【A题】完整分享

2024第三届钉钉杯大学生大数据挑战赛已经开赛&#xff0c;小编给大家带来非常实用的助力【A题】完整&#xff0c;&#xff08;看图片下方的说明&#xff09;&#xff0c;资料预览&#xff1a; 微信公众号

【python】批量读取Word文档中的特定表格并保存为Excel文件

批量读取Word文档中的特定表格并保存为Excel文件 在工作中&#xff0c;我们常常需要从多个Word文档中提取数据&#xff0c;然后将这些数据汇总到一个Excel文件中进行分析。下面&#xff0c;我将分享一个Python脚本&#xff0c;它可以从多个Word文档中读取特定的表格数据&#…

暑期c++ 命名空间

有任何不懂的问题可以评论区留言&#xff0c;能力范围内都会一一回答 今天是暑期第一天开始写c笔记&#xff0c;新起点&#xff0c;新开始加油 我们先来看两串代码 这串代码编译没有问题 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int rand 14; int main(…

手机图片如何转化为word文档?分享3种好用的软件。

在数字化时代&#xff0c;手机已经成为我们生活中不可或缺的一部分。随着手机拍照功能的日益强大&#xff0c;我们常常用手机记录下重要的信息和瞬间。但你有没有遇到过这样的烦恼&#xff1a;如何将手机里的图片快速转化为可编辑的Word文档呢&#xff1f;今天&#xff0c;就为…

从工艺到性能:模具3D打印材料不断革新

在模具3D打印领域&#xff0c;材料性能的持续优化与创新是推动模具3D打印的关键因素&#xff0c;近年来&#xff0c;各种3D打印新材料不断涌现&#xff0c;模具3D打印材料也开始重工艺导向逐步向性能导向发展&#xff0c;如毅速公司推出的ESU-EM191/191S及ESU-EM201不锈钢粉末、…

C语言 | Leetcode C语言题解之第299题猜数字游戏

题目&#xff1a; 题解&#xff1a; #define MIN(a, b) ((a) < (b) ? (a) : (b))char * getHint(char * secret, char * guess){int A 0;int B 0;int dicS[10] {0};int dicG[10] {0};int i 0;while(secret[i]){if(secret[i] guess[i]){//同位置且相等&#xff08;完…

【React】useState:状态管理的基石

文章目录 一、什么是 useState&#xff1f;二、useState 的基本用法三、useState 的工作原理四、高级用法五、最佳实践 在现代前端开发中&#xff0c;React 是一个非常流行的库&#xff0c;而 useState 是 React 中最重要的 Hook 之一。useState 使得函数组件能够拥有自己的状态…