day4 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、 142.环形链表II

news/2024/11/20 19:25:52/

目录:

链接

题目链接:

https://leetcode.cn/problems/swap-nodes-in-pairs/

https://leetcode.cn/problems/remove-nth-node-from-end-of-list/

https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/

https://leetcode.cn/problems/linked-list-cycle-ii/

解题及思路学习

24. 两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WW55AwAZ-1685353234158)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f8f59f5e-54f6-4786-8fbf-16f18602a402/Untitled.png)]

自己想法:可以拆分为交换两个相邻节点。只有一个或者没有节点时,不交换。可以用两个指针来分别表示前后两个节点。应该还需要一个节点来暂存后面的值。

自己想法的代码实现:

(有点绕,下次遇见这种,可以拿张草稿纸画一下)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* swapPairs(ListNode* head) {ListNode* pre;ListNode* cur;ListNode* dummyHead = new ListNode(0);dummyHead -> next = head;pre = dummyHead;cur = head;ListNode* tmp;while(cur != NULL && cur -> next != NULL){pre ->next =cur -> next;tmp = cur -> next -> next;pre -> next -> next = cur;cur -> next = tmp;pre = cur;cur = cur -> next;}tmp = dummyHead -> next;delete dummyHead;return tmp;}
};

随想录想法:步骤差不多,不过是用一个指针cur和两个临时指针来分别表示cur前面一个和后面一个节点。

代码实现:

class Solution {
public:ListNode* swapPairs(ListNode* head) {ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作ListNode* cur = dummyHead;while(cur->next != nullptr && cur->next->next != nullptr) {ListNode* tmp = cur->next; // 记录临时节点ListNode* tmp1 = cur->next->next->next; // 记录临时节点cur->next = cur->next->next;    // 步骤一cur->next->next = tmp;          // 步骤二cur->next->next->next = tmp1;   // 步骤三cur = cur->next->next; // cur移动两位,准备下一轮交换}return dummyHead->next;}
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

其实两种方法的思路都差不多,不过代码随想录的方法更加简洁,不容易绕晕。一定要画图,不画图,操作多个指针很容易乱,而且要操作的先后顺序。

19. 删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lmqBLS3e-1685353234162)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ac022418-b4c3-488b-8f50-97ed27d7879a/Untitled.png)]

自己思路:删除倒数第n个,对于单链表来说,得先知道长度吧。先确定范围,n是要小于整个链表长度的。可以用双指针(当右指针移动了n个节点时,才生成左节点),左指针落后右指针n个长度。当后面一个指针的next为NULL时,表示到底了,这个时候删除前面一个指针的下一个节点。删除节点需要一个临时节点,用来记录删除节点的后一个节点(方便内存释放,如果不需要释放内存,则不需要临时节点)。

代码实现:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* dummyHead = new ListNode(0);dummyHead -> next = head;ListNode* right = dummyHead;ListNode* left = dummyHead; int count = 0;while(count < n && right != NULL){right = right -> next;count++;}right = right -> next;while(count >= n && right  != NULL){right = right -> next;left = left -> next;count++;}// left->next = left->next->next;   不释放内存的写法ListNode* tmp = left -> next;left->next = tmp->next;delete tmp;return dummyHead-> next;}
};

代码随想录思路:运用双指针,快慢指针。我的思路跟这个一样。不同之处在于我用了一个计数的变量,但是随想录直接用n计数,更加简洁。

class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* dummyHead = new ListNode(0);dummyHead->next = head;ListNode* slow = dummyHead;ListNode* fast = dummyHead;while(n-- && fast != NULL) {fast = fast->next;}fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点while (fast != NULL) {fast = fast->next;slow = slow->next;}slow->next = slow->next->next; // ListNode *tmp = slow->next;  C++释放内存的逻辑// slow->next = tmp->next;// delete nth;return dummyHead->next;}
};

我刚开始是没有调通的,因为我用来head作为快慢指针的头节点。但是这样,当要删除的就是头节点的时候就很不好操作。使用虚拟头节点的方式,更加高效。

面试题 02.07. 链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交**:**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IIs2Tp7E-1685353234163)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/777bc3bd-2996-4638-992b-10a85191e10f/Untitled.png)]

自己思路:给定两个单链表头节点,那我可以分别遍历两个链表。值相等不等于相交,交叉点的地址是相同的。链表不一定等长和相交。如果两个链表中,当前两个节点地址不等,但是这两个节点的下一个节点地址相等,则下一个节点就是相交点。暴力一点的解法:先遍历一个链表的所有节点并记录其地址。之后用另一个链表的每个节点去比对。

随想录思路:简单来说,就是求两个链表交点节点的指针。要注意,交点不是数值相等,而是指针相等。

求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4QXryNvN-1685353234163)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8c1662a5-2c35-4575-9dbd-d6c635bf90d6/Untitled.png)]

此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。否则循环退出返回空指针。

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode* curA = headA;ListNode* curB = headB;int lenA = 0, lenB = 0;//求链表长度while(curA !=NULL){curA = curA-> next;lenA++;}while(curB != NULL){curB = curB -> next;lenB++;}curA = headA;curB = headB;// 让curA为最长链表的头,lenA为其长度if(lenB > lenA){swap(lenA, lenB);swap(curA, curB);}//求长度差int gap = lenA -lenB;while(gap--){curA = curA->next;}//遍历curA和curB,遇到相同值则直接返回while(curA != NULL){if(curA == curB){return curA;}curA = curA->next;curB = curB->next;}return NULL;}
};
  • 时间复杂度:O(n + m)
  • 空间复杂度:O(1

这道题,我没有想到先去对齐,这样来简化一下。以后遇见这种长度不一,然后又要匹配中间某一个的题,都可以考虑先将长度对其,再来寻找中间值。

142.环形链表II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-991IVTfC-1685353234164)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/efbecd61-9d24-4ce9-a8ca-501bc2be8ec5/Untitled.png)]

自己思考:没啥好的思路。只想到暴力的,先记录所有已遍历点,遍历下一个点的时候就进行比对。

随想录思路:(牛逼),利用快慢指针来判断是否有环,以及确定有环后点的选取。具体思路:https://programmercarl.com/0142.环形链表II.html#思路

其中涉及到很多数学推理。主要思想是利用速度差来解决。

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode* fast = head;ListNode* slow = head;while(fast != NULL && fast->next != NULL){//两个指针移动的速度快慢不一样,一个1,一个2slow = slow -> next;fast = fast->next->next;// 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇//根据数学推理,从head和快慢指针相遇点到交点距离相等if(slow == fast){ListNode* index1 = fast;ListNode* index2 = head;while(index1 != index2){index1 = index1->next;index2 = index2->next;}return index2;}}return NULL;}
};

困难及收获

困难

1、有时候有思路,但是在代码细节上容易出现错误。以后多注意边界情况,可以的话画图来进行辅助

2、数学原理容易理解,但是没见过这道题的话,一时半会儿很难想到。多积累吧。

今日收获

链表总结:https://programmercarl.com/链表总结篇.html#链表的理论基础

链表的长度问题,最好找到其共同路段。


http://www.ppmy.cn/news/103498.html

相关文章

滑动窗口-单调队列模板

给定一个大小为 n≤106&#xfffd;≤106 的数组。 有一个大小为 k&#xfffd; 的滑动窗口&#xff0c;它从数组的最左边移动到最右边。 你只能在窗口中看到 k&#xfffd; 个数字。 每次滑动窗口向右移动一个位置。 以下是一个例子&#xff1a; 该数组为 [1 3 -1 -3 5 3…

【华为OD统一考试B卷 | 100分】 字符串变换最小字符串 (C++ Java JavaScript Pyhton )

文章目录 题目描述输入描述输出描述用例备注ACM输入输出模式机考代码查重c++javaScriptJavapython题目描述 给定一个字符串s,最多只能进行一次变换,返回变换后能得到的最小字符串(按照字典序进行比较)。 变换规则:交换字符串中任意两个不同位置的字符。 输入描述 一串小写…

数据库自研创新,星环科技发布国产化全面替代方案

核心技术是国之重器&#xff0c;加速推进核心领域关键技术突破&#xff0c;完成核心网络中的软硬件国产替代是国家的一项长期战略。 出品 | CSDN云计算 “聚力攻坚基础软件&#xff0c;加速分布式数据库/混合事务分析处理数据库等产品研发推广。”“十四五”规划明确&#xff0…

什么是低代码?国内常见的低代码平台有哪些?

一、什么是低代码开发&#xff1f; 低代码也称之为无代码或是 aPaas。要想了解低代码是什么&#xff0c;我们先来讨论低代码本质&#xff1f; 它是一种可视化软件开发方法&#xff0c;通过最少的编码更快地交付应用程序。 图形用户界面和拖放功能使开发过程的各个方面自动化…

史上最详细的测试用例写作规范

软件测试用例得出软件测试用例的内容&#xff0c;其次&#xff0c;按照软件测试写作方法&#xff0c;落实到文档中&#xff0c;两者是形式和内容的关系&#xff0c;好的测试用例不仅方便自己和别人查看&#xff0c;而且能帮助设计的时候考虑的更周。 一个好的测试用例必须包含…

全面理解:C++中的指针和迭代器,以及解引用操作符(*)和箭头操作符(->)的用法

指针与迭代器的基础概念 指针&#xff1a; 指针是一种变量&#xff0c;其值为另一种类型的对象在计算机内存中的地址。你可以使用指针来直接访问和操作它指向的对象。指针的使用非常强大&#xff0c;但也很危险&#xff0c;因为你有可能错误地操作内存&#xff0c;这可能会导致…

sql 优化----》1)分析与定位策略

https://www.cnblogs.com/cshaptx4869/p/10482500.html 1&#xff1a;通过 show status 了解各种的SQL的执行频率 2&#xff1a;定位执行频率低的SQL语句: 1):通过慢日志定位 慢日志&#xff1a;可以通过两个方式配置 方式一&#xff1a;配置文件&#xff0c;my.cnf show_query…

如何通过pytest进行更改自动化测试用例的执行顺序?

前言 在自动化测试中&#xff0c;自动化测试用例设计原则就是执行过程时不能存在依赖顺序&#xff0c;那么如果测试用例需要按照指定顺序执行&#xff0c;这个时候应该怎么做呢&#xff1f;目前单元测试框架中unittest没有办法改变测试用例的执行顺序&#xff0c;但是另一个单…