LeetCode 203
思路
原链表法
- 直接在原链表上优先处理完head的情况
- 如果节点为目标值,那么就用cur.next = cur.next.next删除该节点,因为此时head已经保证不是目标值了,所以从cur.next开始删除判断
- 如果节点不是目标值,那么就正常移动节点
虚拟头节点法
- 创建一个哨兵虚拟头节点dummyhead
- 所有节点都以cur.next = cur.next.next的逻辑进行处理,无需单独处理头节点
注意点
原链表法
- 返回值就为head
- 循环结束条件为cur != null && cur.next != null
虚拟头节点法
- 返回值为dummyhead.next,因为此时head可能已经被删除了
- 循环结束条件为cur.next != null
代码
原链表法
class Solution {
public ListNode removeElements(ListNode head, int val) {
//原链表法
if(head == null) {
return head;
}
while(head != null && head.val == val) { //处理头节点为目标值的情况
head = head.next;
}
ListNode cur = head;
while(cur != null && cur.next != null) {
if(cur.next.val == val) { //处理当前节点为目标值的情况
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
}
虚拟头节点法
class Solution {
public ListNode removeElements(ListNode head, int val) {
//虚拟头节点法
if(head == null) {
return head;
}
ListNode dummyhead = new ListNode();
dummyhead.next = head; //引入虚拟头节点
ListNode cur = dummyhead;
while(cur.next != null) {
if(cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return dummyhead.next; //注意此处的返回值
}
}
LeetCode 206
思路
头插法
- 保存head.next的值
- 置空head
- 保存cur.next的值
- 将cur头插法插入head处
- 更新head, cur的值
双指针法
- 引入虚拟头节点pre
- 保存head,cur.next的值
- 将head指向pre,实现逆置
- 更新pre, cur的值
递归法
- 根据双指针法确定递归结束条件
- 递归步骤于双指针法while循环中类似的逆置
- 将更新操作改为递归操作
注意点
头插法
- 一定要记得使用临时节点curN保存cur.next的值,否则会造成链表节点的丢失
- 返回的值为head
双指针法
- pre不可以一开始就指向head,意思就是说不可以写成pre.next = head,因为这样会导致一个环的出现,我们在此方法下进行的是逆置而非头插
- 返回的值为pre,此时pre为链表的最后一个节点
- 图示:链表应该发生下面的变化
递归法
- 递归结束时返回的时pre
- 循环开始的条件时null, cur
代码
头插法
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) {
return head;
}
ListNode cur = head.next; //注意此时cur的值时head.next而非head
head.next = null; //置空head
while(cur != null) {
ListNode curN = cur.next;
cur.next = head;
head = cur;
cur = curN;
}
return head;
}
}
双指针法
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) {
return head;
}
ListNode pre = null;
ListNode cur = head;
while(cur != null) {
ListNode curN = cur.next;
cur.next = pre;
pre = cur;
cur = curN;
}
return pre;
}
}
递归法
class Solution {
//递归
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
public ListNode reverse(ListNode pre, ListNode cur) {
if(cur == null) {
return pre;
}
ListNode curN = cur.next;
cur.next = pre;
return reverse(cur, curN);
}
}
LeetCode 707
因为是链表的基本操作,这里就不讲思路了
注意点
- 注意判断Index的合法性
- 注意size大小的更新
代码
class MyLinkedList {
class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
public ListNode() {
}
}
ListNode dummyhead;
int size;
//初始化
public MyLinkedList() {
dummyhead = new ListNode();
size = 0;
}
//获取index处节点的大小
public int get(int index) {
//合法性判断
if(index < 0 || index > size-1) {
return -1;
}
ListNode cur = dummyhead;
while(index > 0) {
cur = cur.next;
index--;
}
return cur.next.val;
}
//头插法
public void addAtHead(int val) {
ListNode node = new ListNode(val);
node.next = dummyhead.next;
dummyhead.next = node;
size++;
}
//尾插法
public void addAtTail(int val) {
ListNode cur = dummyhead;
while(cur.next != null) {
cur = cur.next;
}
ListNode node = new ListNode(val);
cur.next = node;
size++;
}
//在Index处插入元素
public void addAtIndex(int index, int val) {
//合法性判断
if(index > size) {
return;
}
if(index == size) {
addAtTail(val);
return;
}
ListNode cur = dummyhead;
while(index > 0) {
cur = cur.next;
index--;
}
ListNode node = new ListNode(val);
node.next = cur.next;
cur.next = node;
size++;
}
//在Index处删除元素
public void deleteAtIndex(int index) {
if(index < 0 || index > size-1) {
return;
}
ListNode cur = dummyhead;
while(index > 0) {
cur = cur.next;
index--;
}
cur.next = cur.next.next;
size--;
}
}
总结
本次的算法练习主要是针对了一些关于链表的基本操作,重点是关于链表的反转,对于虚拟头节点的应用。