203. 移除链表元素
解题方法: 虚拟头节点
图示(来源:代码随想录)
删除节点需要考虑两种情况:
-
删除头节点
-
删除非头节点
使用虚拟头节点的好处:可以针对不同情况以同一个规则进行删除/增加节点
具体解题步骤:
- 定义一个虚拟头节点(
dummyHead) - 让虚拟头节点的
next节点指向链表的头节点 - 在虚拟头节点处定义一个指针
cur用来遍历链表 - 遍历链表,判断
cur.next.val是否等于需要删除的目标值(target)- 相等,就让
cur指针的next指向cur指针的next的next,从而删除节点 - 不等,则继续遍历链表
- 相等,就让
- 最后返回新的头节点为虚拟头节点的
next节点
解题代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
//定义虚拟头节点并指定val和next节点
ListNode dummyHead = new ListNode(-1,head);
//dummyHead.next = head;
ListNode cur = dummyHead; //定义cur指针遍历链表
while(cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
}else{
//如果不是要删除的目标值,就继续遍历
cur = cur.next;
}
}
//最后返回的是虚拟头节点的next节点
return dummyHead.next;
}
}
707. 设计链表
解题方法:虚拟头节点、链表基础知识
个人感想:以前自己没有设计过链表,听完视频讲解后自己尝试写代码还是有点吃力,思想懂了但是代码实现的过程中还是出现各种问题,就把代码拉到idea中编写边思考调试(**建议多刷几遍 :) **)
解题代码:
//要会自己写
public class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) {
this.val = val;
}
}
class MyLinkedList {
//链表长度
int size;
//虚拟头节点
ListNode dummyHead;
//初始化链表
public MyLinkedList() {
size = 0;
dummyHead = new ListNode(0);
}
/**
* 获取链表中下标为 index 的节点的值,如果下标无效,则返回 -1
*/
public int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
//这里我和题解的不太一样,我是把 cur 设置成 dummyHead.next
// 所以下面的循环中 i < index 没有 =
ListNode cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.val;
}
/**
* 将一个值为 val 的节点插入到链表中第一个元素之前,在插入完成后,新节点会成为链表的第一个节点
*/
public void addAtHead(int val) {
addAtIndex(0, val);
}
/**
* 将一个值为 val 的节点追加到链表中作为链表的最后一个元素
*/
public void addAtTail(int val) {
addAtIndex(size, val);
}
// 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果 index 大于链表的长度,则返回空
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
if (index < 0) {
index = 0;
}
size++;
ListNode newNode = new ListNode(val);
ListNode cur = dummyHead;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
newNode.next = cur.next;
cur.next = newNode;
}
//如果下标有效,则删除链表中下标为 index 的节点
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
ListNode cur = dummyHead;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.next = cur.next.next;
}
}
温馨提醒:
- 要操作一个节点,一定需要确定它前面的一个节点 ( 非常重要!!!)
- 增加和删除后,要更新链表的size(注:自己写的时候就没注意,导致空指针异常,找了好久的错。。。)
206. 反转链表
解题思路:双指针/递归两种思路
实现前后效果:
实现动态图:
双指针实现步骤(重点):
- 定义一个
cur指针,指向头结点,再定义一个pre指针,初始化为null。 - 定义一个
temp指针,指向cur.next,目的是保存接下来cur指针的指向。 - 循环链表,移动
pre和cur指针,直到cur指针指向为null时返回新的头节点pre。
递归实现:递归实现的代码是根据双指针实现
注意!!!:递归中的传递参数到底填什么
解题代码:
双指针思路
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;
while(cur != null){
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
递归思路
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(head,null);
}
public ListNode reverse(ListNode cur,ListNode pre){
if(cur == null){
return pre;
}
ListNode temp = cur.next;
cur.next = pre;
return reverse(temp,cur);
}
}