所属专栏:【LeetCode题解(持续更新中)】
作 者:我是夜阑的狗
个人简介:一个正在努力学技术的码仔,专注基础和实战分享 ,欢迎咨询!
欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信
您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
大家好,又见面了,我是夜阑的狗,本文是专栏【LeetCode题解】专栏的第16篇文章,主要讲解是LeetCode206. 反转链表(双指针-头插法)、203. 移除链表元素、328. 奇偶链表(双指针)和234. 回文链表(双指针-快慢指针)。
专栏地址:【LeetCode题解(持续更新中)】, 此专栏是我是夜阑的狗对LeetCode问题的讲解,希望能够加深自己的印象,以及帮助到其他的小伙伴。
如果文章有什么需要改进的地方还请大佬不吝赐教。
1.题目描述
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 LeetCode题目链接。
2.示例1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
3.示例2:
输入:head = [1,2]
输出:[2,1]
4.示例3:
输入:head = []
输出:[]
5.提示:
- 链表中节点的数目范围是 [0, 5000]
- -5000 <= Node.val <= 5000
6.进阶:
- 链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
可以根据滑动窗口的思想来解决本题;
这题采用双指针,本题主要关键点为:
解决方法1(个人想法):
看不懂解释的话,直接看算法图解比较容易理解点
时间复杂度:O(L),其中L是链表的长度。
空间复杂度:O(1)
灰色部分代表头指针head(注:本人不会做成流程动画,希望会的朋友可以私信我指点一二,说个软件名字也可以,谢谢)
每个代码块都写了注释,方便理解,代码还可以改进;
class Solution {
public ListNode reverseList(ListNode head) {
ListNode temp, last = head;
while(last!=null){
if(last.next != null){
temp = last.next;
last.next = temp.next;
temp.next = head;
head = temp;
}else break;
}
return head;
}
}
提交结果:
1.题目描述
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。 LeetCode题目链接。
2.示例1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
3.示例2:
输入:head = [], val = 1
输出:[]
4.示例3:
输入:head = [7,7,7,7], val = 7
输出:[]
5.提示:
- 列表中的节点数目在范围 [0, 104] 内
- 1 <= Node.val <= 50
- 0 <= val <= 50
这题比较简单,只要遍历链表找到相同值的前驱节点进行操作即可。这里对第一个节点的处理可分为两种:是否带有头节点;
解决方法1(个人想法):
1、不带头结点
2、带头结点
看不懂解释的话,直接看算法图解比较容易理解点
时间复杂度:O(L),其中L是链表的长度。
空间复杂度:O(1)
红色部分代表删除的节点,灰色部分代表头节点(不带虚拟头节点)(注:本人不会做成流程动画,希望会的朋友可以私信我指点一二,说个软件名字也可以,谢谢)
每个代码块都写了注释,方便理解,代码还可以改进;
不带头结点
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 不带头结点
if(head == null) return head;
ListNode temp = head;
while(temp.next != null){
if(temp.next.val == val)
temp.next = temp.next.next;
else temp = temp.next;
}
return head.val == val ? head.next : head;
}
}
带头结点
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 带头结点解决
ListNode first = new ListNode(0, head);
ListNode temp = first;
while(temp.next != null){
if(temp.next.val == val)
temp.next = temp.next.next;
else temp = temp.next;
}
return first.next;
}
}
1.题目描述
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。LeetCode题目链接。
2.示例1:
输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]
3.示例2:
输入: head = [2,1,3,5,6,4,7]
输出: [2,3,6,7,1,5,4]
5.提示:
- n == 链表中的节点数
- 0 <= n <= 104
- -106 <= Node.val <= 106
这题可以将奇数节点和偶数节点抽离为奇数链表和偶数链表,然后将偶数链表合并在奇数链表即可;
这题采用双指针,本题主要关键点为:
解决方法1(个人想法):
看不懂解释的话,直接看算法图解比较容易理解点
时间复杂度:O(L),其中 L 是链表的节点数。需要遍历链表中的每个节点,并更新指针。
空间复杂度:O(1)
红色部分代表偶数链表,蓝色部分代表奇数链表(注:本人不会做成流程动画,希望会的朋友可以私信我指点一二,说个软件名字也可以,谢谢)
每个代码块都写了注释,方便理解,代码还可以改进;
class Solution {
public ListNode oddEvenList(ListNode head) {
if(head == null) return head;
ListNode temp = new ListNode(0);
// ListNode first = new ListNode(0, head);
ListNode first = head, second = temp;
while(first != null){
// 对当前偶数链表进行添加操作
second.next = first.next;
// 切换到偶数链表的最后一个节点
second = second.next;
// 判断偶数节点和下一个奇数节点是否存在
if(first.next != null && second.next != null){
// 将奇数节点进行链接
first.next = second.next;
first = first.next;
}else break;
}
first.next = temp.next;
return head;
}
}
提交结果:
1.题目描述
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。LeetCode题目链接。
2.示例1:
输入:head = [1,2,2,1]
输出:true
3.示例2:
输入:head = [1,2]
输出:false
5.提示:
- 链表中节点数目在范围[1, 105] 内
- 0 <= Node.val <= 9
6.进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
这题采用快慢指针和反转链表来进行解决;
这题采用双指针,本题主要关键点为:
解决方法1(个人想法):
看不懂解释的话,直接看算法图解比较容易理解点
时间复杂度:O(n),其中 n 指的是链表的大小。
空间复杂度:O(1)。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)。
快慢指针找中间节点的前驱节点(红色部分代表快指针,灰色部分代表慢指针)(注:本人不会做成流程动画,希望会的朋友可以私信我指点一二,说个软件名字也可以,谢谢)
反转后半链表
注:下面代码中第一次反转并没有将中间节点的前驱节点与反转后头节点进行连接,也就是打印出来是1-2-2,而不是1-2-1-2
每个代码块都写了注释,方便理解,代码还可以改进;
class Solution {
public boolean isPalindrome(ListNode head) {
if(head.next == null) return true;
boolean result = true;
ListNode fast = head, slow = head, even;
// 要找中间节点的前驱节点
slow = searchMiddle(head);
// 根据中间结点反转后边的链表相与前面的链表进行对比
even = reverseList(slow.next);
fast = head;
while(even != null){
if(fast.val != even.val) result = false;
even = even.next;
fast = fast.next;
}
// 恢复链表的可以在将后半在反转一次
slow.next = reverseList(slow.next);
return result;
}
private ListNode searchMiddle(ListNode head) {
// 先用快慢指针找到链表中间节点的前驱节点
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
private ListNode reverseList(ListNode head){
ListNode temp = null;
ListNode curr = head;
while (curr.next != null) {
temp = curr.next;
curr.next = temp.next;
temp.next = head;
head = temp;
}
return head;
}
}
提交结果:
1、可以同时使用多个指针。
有时,当你为链表问题设计算法时,可能需要同时跟踪多个结点。您应该记住需要跟踪哪些结点,并且可以自由地使用几个不同的结点指针来同时跟踪这些结点。
2、在许多情况下,你需要跟踪当前结点的前一个结点。
你无法追溯单链表中的前一个结点。因此,您不仅要存储当前结点,还要存储前一个结点。这在双链表中是不同的。
感谢观看,如果有帮助到你,请给题解点个赞和收藏,让更多的人看到。
也欢迎你,关注我。
原创不易,还希望各位大佬支持一下,你们的点赞、收藏和留言对我真的很重要!!! 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!
更多专栏订阅:
- 【LeetCode题解(持续更新中)】
- 【Java Web项目构建过程】
- 【数字图像处理】
- ⚽ 【JavaScript随手笔记】
- 【大数据学习笔记(华为云)】
- 【程序错误解决方法(建议收藏)】
- 【软件安装教程】
订阅更多,你们将会看到更多的优质内容!!