链表相关题目笔记
203. 移除链表元素
题意:删除链表中等于给定值 val
的所有节点。
解题思路
- 使用循环遍历链表,检查每个节点的值是否等于
val
。 - 处理头结点与普通节点时有所区别,头结点需要特别处理。
- 使用一个虚拟头节点
dumy
来避免处理头结点的特殊情况。
代码
方法一:直接删除(不使用虚拟头结点)
class Solution {
public ListNode removeElements(ListNode head, int val) {
while(head != null && head.val == val){
head = head.next;
}
ListNode curr = head;
while(curr != null && curr.next != null){
if(curr.next.val == val){
curr.next = curr.next.next;
} else {
curr = curr.next;
}
}
return head;
}
}
方法二:使用虚拟头结点
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dumy = new ListNode();
dumy.next = head;
ListNode curr = dumy;
while(curr != null && curr.next != null){
if(curr.next.val == val){
curr.next = curr.next.next;
} else {
curr = curr.next;
}
}
return dumy.next;
}
}
707. 设计链表
题意:在链表类中实现以下功能:
get(index)
: 获取链表中第index
个节点的值。如果索引无效,则返回-1
。addAtHead(val)
: 在链表的第一个元素之前添加一个值为val
的节点。插入后,新节点将成为链表的第一个节点。addAtTail(val)
: 将值为val
的节点追加到链表的最后一个元素。addAtIndex(index, val)
: 在链表中的第index
个节点之前添加值为val
的节点。如果index
等于链表的长度,则将节点附加到链表末尾。如果index
大于链表长度,则不会插入节点。如果index
小于0
,则在头部插入节点。deleteAtIndex(index)
: 如果索引index
有效,则删除链表中的第index
个节点。
解题思路
- 使用虚拟头节点,简化插入和删除的处理。
- 维护链表大小
size
方便判断索引有效性。
代码
class ListNode {
int val;
ListNode next;
ListNode() {};
ListNode(int val) {
this.val = val;
}
}
class MyLinkedList {
int size;
// 虚拟头结点
ListNode head;
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
public int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
ListNode curr = head;
for (int i = 0; i < index + 1; i++) {
curr = curr.next;
}
return curr.val;
}
public void addAtHead(int val) {
this.addAtIndex(0, val);
}
public void addAtTail(int val) {
this.addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
if (index < 0) {
index = 0;
}
size++;
ListNode newlist = new ListNode(val);
ListNode curr = head;
for (int i = 0; i < index; i++) {
curr = curr.next;
}
newlist.next = curr.next;
curr.next = newlist;
}
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
ListNode curr = head;
for (int i = 0; i < index; i++) {
curr = curr.next;
}
curr.next = curr.next.next;
}
}
206. 反转链表
题意:反转一个单链表。
示例:
输入: 1 -> 2 -> 3 -> 4 -> 5 -> NULL
输出: 5 -> 4 -> 3 -> 2 -> 1 -> NULL
解题思路
-
双指针法:
- 使用两个指针
prev
和cur
,遍历链表,将cur.next
指向prev
来实现链表的反转。
- 使用两个指针
-
递归法:
- 通过递归调用的回溯过程反转指针。
代码
双指针法
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;
}
}
递归法
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
public ListNode reverse(ListNode prev, ListNode cur) {
if (cur == null) {
return prev;
}
ListNode temp = cur.next;
cur.next = prev;
return reverse(cur, temp);
}
}
解题思路总结
- 双指针法:通过维护一个前驱节点
prev
和当前节点cur
,在遍历链表的过程中,将每个节点的next
指向prev
,从而实现链表的反转。 - 递归法:通过递归到链表尾部,然后逐个反转指针,并逐层返回。