代码随想录算法训练营第3天(链表1)| 203.移除链表元素 707.设计链表 206.反转链表
一、203.移除链表元素
题目:203. 移除链表元素 - 力扣(LeetCode)
视频:手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibili
讲解:代码随想录
注意:
针对头结点和非头结点的删除方式是不一样的:
正常节点:要找到该节点的上一节点
头结点:直接把 head 往后移一位
所以就要进行判断,要删除的节点是不是头结点(方法一)
或者,在头结点前设立一个虚拟节点(dummy head)(方法二)
方法一:判断链表删除元素
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;}
}
要点 1:
判断头结点是否符合的时候,使用 if 就只能判断一次,如果第二个元素也符合条件,就漏删了
所以要用 while ,在头结点不符合条件的时候,才继续往下走
要点 2:
往后查找的时候,要定义一个指针,那么指针的位置指向哪里?
如果是第二位,第二位符合删除条件,找不到上一位的 next 指针。(x)
所以要指向头结点
尝试过程:
class Solution {public ListNode removeElements(ListNode head, int val) {while (head != null && head.val == val) {head = head.next;}ListNode curr = head;while (curr.next != null && curr != null) { //这里有问题!!if (curr.next.val == val) {curr.next = curr.next.next;} else {curr = curr.next;}}return head;}
}
报这个错误的原因:
虽然从逻辑上看是想同时确保当前节点 curr
和它的下一个节点 curr.next
都不为 null
,但如果 curr
本身已经是 null
了,再去访问 curr.next
就会直接抛出空指针异常。
正确的做法应该是先判断 curr
是否为 null
,再去判断 curr.next
是否为 null
,像这样修改条件为 while (curr!= null && curr.next!= null)
。
方法二:设置虚拟头结点
class Solution {public ListNode removeElements(ListNode head, int val) {ListNode dummy = new ListNode();dummy.next = head;ListNode curr = dummy;while(curr != null && curr.next != null){if(curr.next.val == val){curr.next = curr.next.next;} else {curr = curr.next;}}return dummy.next;}
}
注意头结点的指针是不能更改的,因为最后要用到头结点返回。
二、707.设计链表
题目:707. 设计链表 - 力扣(LeetCode)
视频:帮你把链表操作学个通透!LeetCode:707.设计链表_哔哩哔哩_bilibili
讲解:代码随想录
利用虚拟头结点方式,对所有结点统一操作
curr 指针从什么位置开始
循环从什么地方结束
增加/删除时几个指针的操作顺序
class ListNode { //定义链表ListNode next;int val;public ListNode() {}public ListNode(int val){this.val = val;}
}class MyLinkedList {ListNode dummy;int size;//以此开始public MyLinkedList() { //初始化size = 0;dummy = new ListNode(-1);}public int get(int index) { //获取if(index < 0 || index > size-1) return -1;ListNode cur = dummy;for(int i=0; i < index; i++){cur = cur.next;}return cur.next.val;}public void addAtHead(int val) { //addAtIndex(0, val); }public void addAtTail(int val) { //addAtIndex(size, val);}public void addAtIndex(int index, int val) { //插入if(index < 0 || index > size) return;ListNode newNode = new ListNode(val);ListNode curr = dummy;for(int i = 0; i < index; i++){curr = curr.next;}newNode.next = curr.next;curr.next = newNode;size++; }public void deleteAtIndex(int index) { //删除if(index < 0 || index > size-1) return;ListNode curr = dummy;for(int i = 0; i < index; i++){curr = curr.next;}curr.next = curr.next.next;size--;}
}
class MyLinkedList {class ListNode {int val;ListNode next;ListNode(int val) {this.val = val;}}private int size;private ListNode head;// 初始化public MyLinkedList() {this.size = 0;this.head = new ListNode(0);}public int get(int index) {if (index < 0 || index >= size) {return -1;}ListNode cur = head;for (int i = 0; i <= index; i++) {cur = cur.next;}return cur.val;}public void addAtHead(int val) {ListNode newNode = new ListNode(val);newNode.next = head.next;head.next = newNode;size++;}public void addAtTail(int val) {ListNode newNode = new ListNode(val);ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = newNode;size++;}public void addAtIndex(int index, int val) {if (index < 0 || index >= size) {return;}ListNode cur = head;for (int i = 0; i < index; i++) {cur = cur.next;}ListNode newNode = new ListNode(val);newNode.next = cur.next;cur.next = newNode;size++;}public void deleteAtIndex(int index) {if(index < 0 || index >=size){return;}ListNode cur = head;for(int i=0; i<index; i++){cur = cur.next;}cur.next = cur.next.next;size--;}
}
要点是要确定每个循环停在哪里,否则会报空指针异常
三、206.反转链表
题目:206. 反转链表 - 力扣(LeetCode)
视频:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili
讲解:代码随想录
要点:
1、 两个指针(加上 temp 是三个指针)的起始位置
2、 循环的终止条件
3、 调换每一个节点,调整的顺序
正确答案:
class Solution {public ListNode reverseList(ListNode head) {if(head == null || head.next == null) return head;ListNode pre = null;ListNode cur = head;ListNode temp;while(cur != null){temp = cur.next;cur.next = pre;pre = cur;cur = temp;}return pre;}
}
尝试过程:
class Solution {public ListNode reverseList(ListNode head) {if(head == null || head.next == null) return head;ListNode pre = null;ListNode cur = head;ListNode temp;for(int i = 0; i <= head.size-1; i++){ //这里报错!!temp = cur.next;cur.next = pre;pre = cur;cur = temp;}return pre;}
}
错误原因:ListNode
类并没有定义size
属性。在链表中,我们通常通过遍历来获取链表的长度,而不是直接存储长度。
四、链表总结
1、虚拟头结点
:如果想要针对不同位置(头与非头结点)的节点用相同的处理办法,可以考虑虚拟头结点,对所有节点一视同仁。
2、双指针
:节省空间
3、注意几个调整动作的先后顺序,防止指针丢失的结果。