代码随想录算法训练营第三天打卡| Leetcode 203、Leetcode 707、Leetcode 206
打卡
Leetcode 203 移除链表元素
题目连接
关键点:
-
移除链表元素的逻辑&虚拟头节点的方案
逻辑上移除链表元素只需要让前一个节点的指针指向下一个节点(注意内存释放),由此引申出移除节点的两种类型:- 非头节点:前一个节点指向下一个节点
- 头节点:头节点指针后移【头节点没有前一个节点】
对头节点特殊的原因做出分析之后,发现在头节点前引入一个虚拟头节点dummy head就可以统一操作
-
移除元素是while操作
在链表中找到target后,有可能后面几点节点都是相同的元素,因而移除元素是一个持续不断的过程,使用while循环 -
单向列表移除元素时如果不额外使用pre指针记录上一个节点,那么cur指针要从头节点开始(判断:
cur.next== target
;移除:cur.next = cur.next.next
)【不然怎么寻找上一个节点呢】 -
不能用头节点进行遍历,需要使用临时指针遍历;最后需要return原先列表的头节点
-
避免操作空指针:在取值前先判断节点是否为空
虽然要注意的点很多,但是实际实现还是很简单的。
解答
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) return head;
ListNode dummy = new ListNode(-1, head);
ListNode pre = dummy;
ListNode cur = head;
while (cur != null){
if (cur.val == val){
pre.next = cur.next; //没找到继续遍历
}else {
pre = cur; //找到则让上一个节点指向下一个节点
}
cur = cur.next;
}
return dummy.next; //原先的头节点可能被移除,新的头节点已经不是head
}
}
Leetcode 707 设计链表
题目链接【节点从0开始计算,0 为头节点】
关键点:
- 获取第n个节点的值:注意题目中的n从0开始还是从1开始
- 头部插入节点:注意节点插入的顺序;注意当插入位置为链表后一位时仍然插入
- 尾部插入节点:
cur.next == null
到达尾部 - 第n个节点前插入节点:插入节点需要知道前一个节点,因而在第n个节点前插入节点时cur指向第n-1个节点,cur.next指向第n个节点
- 删除第n个节点:删除节点需要知道前一个节点,因而在第n个节点前插入节点时cur指向第n-1个节点,cur.next指向第n个节点
- 要考虑到index不合法的输入
- 具体循环次数的确定:举例法
解答
// 单链表
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 MyLinkedList {
int size;
ListNode head; //虚拟头节点
public MyLinkedList() {
this.size = 0;
head = new ListNode(0);
}
public int get(int index) {
// 如果index非法,返回-1
if (index < 0 || index >= size)
return -1;
ListNode cur = head;
// 从0开始编号,从虚拟头节点开始查找第index+1个节点要index+1次循环
while(index >= 0){
cur = cur.next;
index--;
}
return cur.val;
}
public void addAtHead(int val) {
ListNode addedNode = new ListNode(val);
// 考虑到链表当中可能还没有节点,cur统一初始化为虚拟头节点
ListNode cur = head;
addedNode.next = cur.next;
cur.next = addedNode;
size++;
// printList();
}
public void addAtTail(int val) {
ListNode cur = head;
for (int i = size; i > 0; i--){
cur = cur.next;
}
ListNode addedNode = new ListNode(val);
cur.next = addedNode;
size++;
// printList();
}
public void addAtIndex(int index, int val) {
// 如果index非法,返回
if (index < 0 || index > size)
return;
ListNode cur = head;
// 在n插入,cur在n-1处,循环n次
while(index > 0){
cur = cur.next;
index--;
}
ListNode addedNode = new ListNode(val);
addedNode.next = cur.next;
cur.next = addedNode;
size++;
// printList();
}
public void deleteAtIndex(int index) {
// 如果index非法,返回
if (index < 0 || index >= size)
return;
ListNode cur = head;
// 删除第n个节点,cur要在n-1处,循环n次
while(index > 0){
cur = cur.next;
index--;
}
cur.next = cur.next.next;
size--;
// printList();
}
public void printList(){
ListNode cur = head;
for (int i = 0; i < size; i++) {
System.out.printf("%d ", cur.next.val);
cur = cur.next;
}
System.out.println();
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
Leetcode 206 反转链表
题目链接
方法?:遍历链表,每个节点都插入在头节点上(虽然本质上是构造了一个ReverseList的结构,并没有对原链表进行反转,但是能过测试lmao)
解答?
class Solution {
ListNode dummy = new ListNode(0);;
public ListNode reverseList(ListNode head) {
ListNode cur = head;
while (cur != null){
addAtHead(cur.val);
cur = cur.next;
}
return dummy.next;
}
public void addAtHead(int val){
ListNode addedNode = new ListNode(val);
ListNode cur = dummy;
addedNode.next = cur.next;
cur.next = addedNode;
}
}
方法1:双指针法。在改变指向的过程中为了避免断链需要用临时的
temp指针记录下一个节点(即pre->cur->temp)
注意点:
- cur的初始值为head,pre指向cur前一个节点,初始值为null
- 反转过程的顺序是固定的:记录cur下一个节点->反转cur->更新pre->更新cur
- 反转链表后的新头节点为pre
解答1
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
方法2:写成递归的形式。和双指针法相同逻辑但是写法更加简洁
解答2
注意点:
- 完全依照双指针法的更新逻辑进行递归参数的确定
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null, head); // pre和cur的初始值
}
public ListNode reverse(ListNode pre, ListNode cur){
if (cur == null){
return pre;
}
ListNode temp = cur.next;
cur.next = pre;
return reverse(cur, temp); // 依据双指针法的逻辑代入参数
}
}