203.移除链表元素
记录之前的代码。
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode fakeHead = new ListNode(0, head);
ListNode prev = fakeHead, index = head;
for(; index != null; index = index.next){
// 如果值相同
if(index.val == val){
prev.next = index.next;
continue;
}
// 如果值不同
prev = prev.next;
}
return fakeHead.next;
}
}
最后:
- 更适合while循环:因为会删除节点。
- 删除节点:虚拟头节点dummy;只能依靠前一个节点来删除下一个节点。
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy = new ListNode(0, head);
ListNode cur = dummy;
while(cur.next!=null){
if (cur.next.val==val){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return dummy.next;
}
}
707.设计链表
记录之前做过的代码:虽然能够跑通,但是混淆了链表和节点的概念。
class MyLinkedList {
public int val;
public MyLinkedList next;
public MyLinkedList prev;
// 属性值是否都需要初始化?
public MyLinkedList() {
this.val = 0;
this.next = null;
this.prev = null;
}
public int get(int index) {
// 如果是本节点
if(index==0) return this.val;
// 如果是之后的节点
MyLinkedList node = this.next;
// 因为node现在是下标为1的节点,遍历到对应下标的节点
for(int i=2; i<index+1; i++){
node = node.next;
// 如果node为null,且未遍历到index,则链表不够长
if(node==null && i!=index) return -1;
}
return node.val;
}
// 对于链表,头节点插入,可以用值替换法
public void addAtHead(int val) {
MyLinkedList node = new MyLinkedList();
// 插入新节点
node.val = this.val;
node.prev = this;
node.next = this.next;
this.next = node;
this.val = val;
}
public void addAtTail(int val) {
MyLinkedList node = this;
// 找到尾节点
if (node.next!=null){
node = node.next;
}
// 插入
MyLinkedList nodeNew = new MyLinkedList();
nodeNew.val = val;
nodeNew.prev = node;
node.next = nodeNew;
}
public void addAtIndex(int index, int val) {
// 如果是首节点
if(index==0) {
this.addAtHead(val);
return;
}
// 遍历到对应下标,最后只会到index的前一个
MyLinkedList node = this;
for(int i=1; i<index; i++){
node = node.next;
if(node == null) return;
}
// 如果是尾节点
if(node.next == null) this.addAtTail(val);
// 如果是中间节点
MyLinkedList nodeNew = new MyLinkedList();
nodeNew.val = val;
nodeNew.next = node.next.next;
nodeNew.prev = node;
node.next = nodeNew;
nodeNew.next.prev = nodeNew;
}
public void deleteAtIndex(int index) {
// 如果是首节点,把第二个节点的值替换过来
if(index==0){
if(this.next!=null) {
this.val = this.next.val;
this.next = this.next.next;
if(this.next!=null) this.next.prev = this;
}
return;
}
// 如果是后续节点:遍历到对应下标
MyLinkedList node = this;
for(int i=1; i<index+1; i++){
node = node.next;
if(node==null) return; // 如果找不到index,则结束
}
// 是否为尾节点
if(node.next==null) node.prev.next = null;
else {
node.prev.next = node.next;
node.next.prev = node.prev;
}
}
}
/**
* 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);
*/
新的:也有不足之处(对比代码随想录)
- 没有size属性:可以进行越界判定。
class MyLinkedList {
Node dummy;
class Node{
int val;
Node next;
Node pre;
Node(){}
Node(int val){
this.val = val;
}
}
public MyLinkedList(){
dummy = new Node();
}
public int get(int index){
Node cur = dummy;
for(int i=0; i<index+1; i++){
cur = cur.next;
if(cur==null){
return -1;
}
}
return cur.val;
}
public void addAtHead(int val){
Node node = new Node(val);
node.next = dummy.next;
node.pre = dummy;
dummy.next = node;
if(node.next!=null){
node.next.pre = node;
}
}
public void addAtTail(int val){
Node node = new Node(val);
Node cur = dummy;
while(cur.next!=null){
cur = cur.next;
}
cur.next = node;
node.pre = cur;
}
public void addAtIndex(int index, int val){
Node node = new Node(val);
Node cur = dummy;
for(int i=0; i<index; i++){
cur = cur.next;
if (cur==null){
return;
}
}
node.next = cur.next;
node.pre = cur;
cur.next = node;
if(node.next!=null){
node.next.pre = node;
}
}
public void deleteAtIndex(int index){
Node cur = dummy;
for(int i=0; i<index; i++){
cur = cur.next;
if (cur.next==null){
return;
}
}
if (cur.next==null){
return;
}
cur.next = cur.next.next;
if (cur.next!=null){
cur.next.pre = cur;
}
}
}
206.反转链表:需要临时节点
记录之前的代码:但是这是重新创建对象,可能不是考点。
class Solution {
public ListNode reverseList(ListNode head) {
// 目标:用头插法实现链表
ListNode fakeNode = new ListNode();
// for循环遍历链表
for(ListNode now = head; now!=null; now = now.next){
ListNode newNode = new ListNode();
newNode.val = now.val;
newNode.next = fakeNode.next;
fakeNode.next = newNode;
}
return fakeNode.next;
}
}
重新做第一遍,有点乱,导致Error - Found cycle in the ListNode
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = head;
if(pre==null){
return null;
}
ListNode cur = pre.next;
// pre.next = null; // 如果不添加,就会链表循环了
ListNode temp;
while(cur!=null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
对比一下代码随想录解法:
// 双指针
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null) {
temp = cur.next;// 保存下一个节点
cur.next = prev;
prev = cur;
cur = temp;
}
return prev;
}
}
总结
- 删除节点时,一定会有个temp临时存储。
prev = null
:类似于虚拟头节点的作用。
尝试递归解法
果然还是不太会,有点抽象。先看了代码随想录的递归解法一,自己手动试了一下:
- 将递归最后的结果一直返回。
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
// 参数:两个节点
// 返回值:最后的节点 一定会return reverse(),而不是单纯reverse()。
private ListNode reverse(ListNode pre, ListNode cur){
// 结束逻辑
if (cur==null) {
return pre;
}
ListNode temp = cur.next;
cur.next = pre;
return reverse(cur, temp);
}
}
思路二:从后往前反转
这里逻辑还是有些绕:
- 从尾到头反转:尾部的节点反转了,再来反转前面的节点。
- 此时需要注意,
head.next = null
主要是为了应对头节点(反转之前的头节点)。如果不是头节点,那么跳出本次递归后,head.next
又会被赋上新值。 - 将新的头节点一路返回。
- 终止条件。
class Solution {
public ListNode reverseList(ListNode head) { // 返回末尾的节点
// 终止条件
if (head==null) return null;
if (head.next==null) return head;
// 递归逻辑
ListNode last = reverseList(head.next);
head.next.next = head;
head.next = null;
return last;
}
}