自己看到题目的第一想法
哦, 是删除链表里的元素啊. 大学数据结构里前几讲不是吗? 上课一听就懂, 逻辑简单明了. 那不是小菜一碟吗?
核心思想当然是拿到当前节点的上一个节点, 然后将上一个节点的指针指向当前节点的下一个节点.
思路延续下来就是, 我们需要一个指向当前节点的前一个节点的 previousNode, 以及指向当前节点的 currentNode. 因此当 currentNode.value == value 的时候, 就将 previousNode.next = currentNode.next 就可以了. 可是, 如果是 head 节点怎么办呢, 这时候 previousNode 为 null. 于是就多了 if (previousNode == null) 的判断, 特殊处理头节点的删除.
// 删除列表元素, 第一反应的解法:
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode previousNode = null;
ListNode currentNode = head;
while (currentNode != null) {
if (currentNode.val != val) {
previousNode = currentNode;
currentNode = currentNode.next;
continue;
}
// 表示当前节点是头节点, 相对来说这里增加了理解的复杂度
if (previousNode == null) {
currentNode = currentNode.next;
head = currentNode;
} else {
previousNode.next = currentNode.next;
currentNode = previousNode.next;
}
}
return head;
}
}
看完代码随想录之后的想法
嗯... 我们需要的是找到待删除的节点的前一个节点, 头节点没有前一个节点怎么办呢, 再造一个!!! 记得以前上课的时候老师也是这么说的, 然后说其实不要也可以, 不知道为什么, 自己偏偏就要和老师对着干, 以至于都忘记了老师的教诲... 这次又唤醒了沉睡的记忆, 鞭笞了腐朽的心灵.
// 删除列表元素, 看完视频后的解法:
/**
* 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) {
ListNode dummyNode = new ListNode(val, head);
ListNode currentNode = dummyNode;
while (currentNode.next != null) {
// if (currentNode.val == val) { // 这里撕错了...
if (currentNode.next.val == val) {
currentNode.next = currentNode.next.next;
} else {
currentNode = currentNode.next;
}
}
return dummyNode.next;
}
}
自己实现过程中遇到哪些困难
一开始因为头节点的处理和其他节点不一样, 一直纠结怎么不需要单独写头节点的删除, 因此出了 previousNode == null 的版本... 其他还好, 整体来说还是耗费了一些不必要耗费的时间, 解题的效率不足.
自己看到题目的第一想法
依旧是简单到不能再简单了对吗! 增删改查 crud, 这还能不会吗?
咦, 要不要添加一个尾节点 tailNode 呢?
咦, 添加到指定位置的时候, previousNode 到底怎么获取到呢?
咦, 删除指定元素的时候, 头节点怎么处理呢?
怎么多了那么多问题... 确实把自己绕了一圈, 浪费了不少时间.
// 设计一个链表
/**
* 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);
*/
class MyLinkedList {
private Node head;
private Node tail;
public MyLinkedList() {
}
public int get(int index) {
Node currentNode = head;
int currentIndex = -1;
while (currentNode != null) {
currentIndex++;
if (currentIndex == index) {
return currentNode.value;
}
currentNode = currentNode.next;
}
return -1;
}
public void addAtHead(int val) {
Node node = new Node(val);
if (head == null) {
head = tail = node;
tail = head;
} else {
node.next = head;
head = node;
}
}
public void addAtTail(int val) {
Node node = new Node(val);
if (tail == null) {
head = tail = node;
} else {
tail.next = node;
tail = node;
}
}
public void addAtIndex(int index, int val) {
if (index == 0) {
addAtHead(val);
return;
}
Node previousNode = null;
Node currentNode = head;
int currentIndex = 0;
while (currentNode != null) {
if (currentIndex + 1 == index) {
Node node = new Node(val);
node.next = currentNode.next;
currentNode.next = node;
if (currentNode == tail) {
tail = node;
}
return;
}
currentIndex++;
currentNode = currentNode.next;
if (currentIndex >= index) {
return;
}
}
}
public void deleteAtIndex(int index) {
if (index == 0) {
if (head != null) {
head = head.next;
}
return;
}
Node currentNode = head;
int currentIndex = 0;
while (currentNode != null) {
if (currentIndex + 1 == index && currentNode.next != null) {
if (currentNode.next == tail) {
tail = currentNode;
}
currentNode.next = currentNode.next.next;
}
currentIndex++;
currentNode = currentNode.next;
if (currentIndex >= index) {
return;
}
}
}
private class Node {
public int value;
public Node next;
public Node(int value) {
this.value = value;
}
}
}
看完代码随想录之后的想法
虚节点可以统一世界, 造成循环不变量, 真棒!
重点是找到待处理节点的前一个节点, 当前节点其义自现!
// 看完视频解说之后写的答案
// 没有补充 size, 没有 tail node, 造成了一定的性能损耗.
class MyLinkedList {
private final Node dummyNode;
public MyLinkedList() {
dummyNode = new Node(0);
}
public int get(int index) {
if (dummyNode.next == null || index < 0) {
return -1;
}
Node currentNode = dummyNode;
while (index-- >= 0 && currentNode != null) {
currentNode = currentNode.next;
}
if (currentNode != null) {
return currentNode.value;
}
return -1;
}
public void addAtHead(int val) {
dummyNode.next = new Node(val, dummyNode.next);
}
public void addAtTail(int val) {
if (dummyNode.next == null) {
addAtHead(val);
return;
}
Node currentNode = dummyNode.next;
while (currentNode.next != null) {
currentNode = currentNode.next;
}
currentNode.next = new Node(val);
}
public void addAtIndex(int index, int val) {
if (dummyNode.next == null) {
if (index == 0) {
addAtHead(val);
}
return;
}
Node currentNode = dummyNode;
while (index-- > 0 && currentNode != null) {
currentNode = currentNode.next;
}
if (currentNode != null) {
currentNode.next = new Node(val, currentNode.next);
}
}
public void deleteAtIndex(int index) {
Node currentNode = dummyNode;
while (index-- > 0 && currentNode != null) {
currentNode = currentNode.next;
}
if (currentNode != null && currentNode.next != null) {
currentNode.next = currentNode.next.next;
}
}
private class Node {
public int value;
public Node next;
public Node(int value) {
this.value = value;
}
public Node(int value, Node next) {
this.value = value;
this.next = next;
}
}
}
自己实现过程中遇到哪些困难
增加了 tailNode 的时候, 对 tailNode 的赋值不够完全.
没有判断 n 的有效性, 因此一直在循环里做这种判断, 逻辑变得不清晰而复杂, 得不偿失.
没有去找当前节点的 previousNode, 而是直接找 currentNode, 造成逻辑不清晰...
我觉得很关键的一点就是, 一定要先找到对应的节点, 增加和删除要找到待处理节点的前一个节点, 获取和修改则找到当前节点.
自己看到题目的第一想法
// 206. 反转链表: 第一反应的解法
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) {
return head;
}
ListNode previousNode = head;
ListNode currentNode = head.next;
head.next = null;
ListNode nextNode = null;
while (currentNode != null) {
nextNode = currentNode.next;
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
return previousNode;
}
}
准备两个指针, 第一个表示 previousNode, 第二个表示 currentNode, 然后将 nextNode = currentNode.next, currentNode.next = previous, previous = currentNode, currentNode = nextNode. 就解决了, 很简单的.
看完代码随想录之后的想法
新增了递归的解决方式.
// 看过解答后的解法
class Solution {
// 循环解法
public ListNode reverseList(ListNode head) {
ListNode previous = null;
ListNode current = head;
ListNode next = null;
while (current != null) {
next = current.next;
current.next = previous;
previous = current;
current = next;
}
return previous;
}
// 递归解法, 递归解法的心法是什么呢?
public ListNode reverseListRecursion(ListNode head) {
return reverseListRecursion(null, head);
}
public ListNode reverseListRecursion(ListNode previous, ListNode current) {
if (current != null) {
ListNode next = current.next;
current.next = previous;
return reverseListRecursion(current, next);
}
return previous;
}
}
自己实现过程中遇到哪些困难
好像没有, 看过视频后用递归也算是挺顺利的. 当然效率依旧不够.