【Leetcode】-代码随想录算法训练营Day3 | 203.移除链表元素,707.设计链表,206.反转链表

Day3开始进入了链表的专题,因为之前没有跟上写,所以现在复习阶段后补一下这几天的博客。

Leetcode题目-203. Remove Linked List Elements

链接: 203. Remove Linked List Elements

思路

这是一道链表的easy题,总的来说就是删节点。实际的实现只要理解了链表的基本知识和操作并不难,即找到待删除节点的前一节点pre,让pre.next=pre.next.next即可,主要就是要注意如果要删除的节点为头节点时,操作会与上述删除非头节点时不太一致,只需让head.next成为头节点即可。按照常规做法,即找到待删除节点后要判断下节点是否为头节点,然后再进行相应的操作。
其实还有一种方法,可以避免掉分情况处理的分类代码。即构造“虚拟头节点”,在头节点之前构造一个虚拟头节点dummyHead,让dummyHead.next=head,然后头节点的操作就可和其他节点一样了。在返回时,只需返回dummyHead.next就相当于去掉了虚拟头节点,只返回了实际的链表结构了。

代码实现

分别贴上上述两种方法的代码实现

  1. 头节点单独考虑
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        // 头节点的处理方式:如果头节点值不为null且头节点的值等于val,则下一个节点作为新的头节点
        while (head != null && head.val==val) {
            head = head.next;
        }

        // 非头节点的处理方式:如果其值等于val,则其上一节点指向其下一个节点
        ListNode current=head;
        while (current!=null && current.next != null) {
            if(current.next.val==val){
                current.next=current.next.next;
            }else{
                current = current.next;
            }
        }

        return head;
    }
}
  1. 虚拟头节点
class Solution {
    // 虚拟头节点
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummyHead = new ListNode(0, head);

        ListNode currentNode = dummyHead;
        while (currentNode.next != null) {
            if (currentNode.next.val == val) {
                currentNode.next = currentNode.next.next;
            } else {
                currentNode = currentNode.next;
            }
        }

        return dummyHead.next;
    }
}

总结

时间复杂度:O(n),链表需要遍历一遍,n是链表的长度
空间复杂度:O(1)
虚拟头节点的思想很重要,有时候可以简化很多头节点的特殊情况复杂问题。
代码随想录文章讲解:传送门
代码随想录视频讲解:传送门

Leetcode题目-707. Design Linked List

链接: 707. Design Linked List

思路

这道题首先要理解题意。。知道让你干什么。即设计一个LinkedList,这个链表,除了记录链表的节点,你还要维护一个size(千万不要忘记了每次增删节点后的size++和size–操作)
接下来就是比较朴实的逻辑盘顺即可。这道题和上述一样,也是因为有头节点的一些特殊情况,我们增加一个虚拟头节点来设计会方便很多。在设计过程中,还要注意,各个函数输入值的合法性判断和题目中的特殊规定。

代码实现

class MyLinkedList {
    int size;
    // 虚拟头节点
    ListNode dummyHead;

    public MyLinkedList() {
        size = 0;
        dummyHead = new ListNode(0);
    }

    public int get(int index) {
        if (index > size - 1 || index < 0) {
            return -1;
        }

        ListNode currentNode = dummyHead.next;
        for (int i = 0; i < index; i++) {
            currentNode = currentNode.next;
        }

        return currentNode.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 > size) {
            return;
        }

        if (index < 0) {
            index = 0;
        }

        ListNode currentNode = dummyHead;
        for (int i = 0; i < index; i++) {
            currentNode = currentNode.next;
        }
        ListNode toAdd = new ListNode(val);
        toAdd.next = currentNode.next;
        currentNode.next = toAdd;

        size++;
    }

    public void deleteAtIndex(int index) {
        if (index < 0 || index > size - 1) {
            return;
        }

        ListNode currentNode = dummyHead;
        for (int i = 0; i < index; i++) {
            currentNode = currentNode.next;
        }
        currentNode.next = currentNode.next.next;
        size--;

    }
}

/**
 1. Your MyLinkedList object will be instantiated and called as such:
 2. MyLinkedList obj = new MyLinkedList();
 3. int param_1 = obj.get(index);
 4. obj.addAtHead(val);
 5. obj.addAtTail(val);
 6. obj.addAtIndex(index,val);
 7. obj.deleteAtIndex(index);
 */

总结

这道题是设计链表,所以在实现增删查方法中,有一个很重要的就是要考虑异常输入(题目给出了处理规则),这个思想也是在工程中处理数据很重要的,在自己设计方法的时候,也要充分考虑到各种输入,对于逻辑中不合法的输入,要做好处理,这样代码才具有良好的鲁棒性。

Leetcode题目-206. Reverse Linked List

链接: 206. Reverse Linked List

思路

此题的解决思路可以比较容易想到,就是遍历链表,然后改变指针的方向。比较简单的做法就是新建一个链表,一边遍历原链表,一边构建新链表(反向指针)。但是这样就是O(2n)的空间复杂度了。
为了节约内存空间,我们可以直接在原链表上操作,既然要改变链表指针的方向,肯定需要获取当前节点cur和当前节点的前一节点pre,这样才能顺利地将cur.next=pre,这个时候可以用到双指针法(之前在数组里用过)。然后在操作cur.next=pre这一步的时候,要记得先将cur.next存储起来,不然要往下一轮移动指针时就找不到啦。
代码随想录文章讲解:传送门
代码随想录视频讲解:传送门

代码实现

  1. 新构建链表
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null) return null;

        ListNode reverseHead = new ListNode(head.val);
        ListNode currentNode = head;
        while (currentNode.next != null) {
            currentNode = currentNode.next;
            reverseHead = new ListNode(currentNode.val, reverseHead);
        }

        return reverseHead;
    }
}

2.双指针法在原链表操作

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode tmp = null;
        while (cur != null) {
        	// 先存储cur.next,接下来将覆盖此值
            tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }

        return pre;
    }
}

总结

时间复杂度:O(n)
空间复杂度:O(1)(双指针法)
代码随想录文章讲解:传送门
代码随想录视频讲解:传送门

你可能感兴趣的:(Leetcode-代码随想录,链表,算法,leetcode,数据结构,java)