代码随想录算法训练营第3天 | 题目:203.移除链表元素、 707.设计链表 、206.反转链表
文章来源:代码随想录
题目名称:203.移除链表元素
题意:删除链表中等于给定值 val 的所有节点。
示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2: 输入:head = [], val = 1 输出:[]
示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]
第一想法:
首先理解链表的格式,单链表包含头节点head和值val。移除链表元素如在头节点位置,因为其没有前一个结点所以需要单独讨论。、
while (head != null && head.val == val) {
head = head.next;
}
其他的通过一个pre和cur节点,当cur节点删除,则pre.next=cur.next。
解答思路:
添加虚拟节点dummy节点,统一操作
ListNode dummy = new ListNode(-1, head);
困难:
注意,添加虚拟节点后不能return head,因为head有可能已经删除,需要return dummy.next作为头节点
收获:
添加dummy是帮助链表处理整体原则化的手法。
题目名称:707.设计链表
题意:
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
707示例
第一想法:
链表是没有下标的,只能通过链表前后顺序查找到。addindex可以实现尾部头部添加的功能。
解答思路:
基本的链表处理手法,在首部添加虚拟节点dummy进行处理,可以避免对头节点特殊处理。
困难:无
注意在处理时对整体size的加减。
收获:
对链表的指定下标只能通过循环找寻,注意加入虚拟节点后,在首部的插入变成在第二位插入。
题目名称:206.反转链表
力扣题目链接(opens new window)
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
第一想法:
使用新建一个链表来填入链表值进行反转
解答思路:
直接反转链表指向
1,双指针法
设置pre cur temp,通过移动这两个指针是得cur.next=pre,temp储存的是原先的cur.next。
// 双指针
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null) {
temp = cur.next;// 保存下一个节点
cur.next = prev;
prev = cur;
cur = temp;
}
return prev;
}
}
2,前向递归
与双指针法相似
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
private ListNode reverse(ListNode prev, ListNode cur) {
if (cur == null) {
return prev;
}
ListNode temp = null;
temp = cur.next;// 先保存下一个节点
cur.next = prev;// 反转
// 更新prev、cur位置
// prev = cur;
// cur = temp;
return reverse(cur, temp);
}
}
3, 反向递归
先递归到最后一项,在返回的过程中反转指针
// 从后向前递归
class Solution {
ListNode reverseList(ListNode head) {
// 边缘条件判断
if(head == null) return null;
if (head.next == null) return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode last = reverseList(head.next);
// 翻转头节点与第二个节点的指向
head.next.next = head;
// 此时的 head 节点为尾节点,next 需要指向 NULL
head.next = null;
return last;
}
}
困难:
反向递归理解比较抽象,这里举个例子
例子
假设链表为 1 -> 2 -> 3 -> 4 -> 5,我们逐步通过递归进行反转:
第一次递归:reverseList(1) 调用 reverseList(2)
第二次递归:reverseList(2) 调用 reverseList(3)
第三次递归:reverseList(3) 调用 reverseList(4)
第四次递归:reverseList(4) 调用 reverseList(5)
第五次递归:reverseList(5),此时 head.next == null,返回 5
返回后:
第四次递归返回时,将 4 -> 5 变为 5 -> 4,并将 4 的 next 设为 null。
第三次递归返回时,将 3 -> 4 变为 5 -> 4 -> 3,并将 3 的 next 设为 null。
第二次递归返回时,将 2 -> 3 变为 5 -> 4 -> 3 -> 2,并将 2 的 next 设为 null。
第一次递归返回时,将 1 -> 2 变为 5 -> 4 -> 3 -> 2 -> 1,并将 1 的 next 设为 null。
最终链表变为 5 -> 4 -> 3 -> 2 -> 1。
虽然最后还是不太懂。。。
收获:
反转链表的方法突出了链表指向的特点,通过反转next指针就能进行操作,注意如何在反转的过程中保证整体的向前运行,除了设置temp变量保存外,使用反向递归来处理也是一种方法。