代码随想录算法训练营第三天 | 203. 移除链表元素 707. 设计链表 206. 反转链表

[LeetCode] 203. 移除链表元素

[LeetCode] 203. 移除链表元素文章讲解

[LeetCode] 203. 移除链表元素视频讲解

自己看到题目的第一想法 

    哦, 是删除链表里的元素啊. 大学数据结构里前几讲不是吗? 上课一听就懂, 逻辑简单明了. 那不是小菜一碟吗?

    核心思想当然是拿到当前节点的上一个节点, 然后将上一个节点的指针指向当前节点的下一个节点.

    思路延续下来就是, 我们需要一个指向当前节点的前一个节点的 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 的版本... 其他还好, 整体来说还是耗费了一些不必要耗费的时间, 解题的效率不足.

[LeetCode] 707. 设计链表

[LeetCode] 707. 设计链表文章解说

[LeetCode] 707. 设计链表视频解说

自己看到题目的第一想法

    依旧是简单到不能再简单了对吗! 增删改查 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, 造成逻辑不清晰...

    我觉得很关键的一点就是, 一定要先找到对应的节点, 增加和删除要找到待处理节点的前一个节点, 获取和修改则找到当前节点.

[LeetCode] 206. 反转链表

[LeetCode] 206. 反转链表文章解释

[LeetCode] 206. 反转链表视频解释

自己看到题目的第一想法 

// 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;
    }
}

自己实现过程中遇到哪些困难

    好像没有, 看过视频后用递归也算是挺顺利的. 当然效率依旧不够.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值