算法训练营第三天| 203. 移除链表元素 、707. 设计链表 、206. 反转链表

84 阅读3分钟

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

image.png

思路

思路很简单,就是遍历整个链表,当发现目标值的时候将其移除出链表。 为了达到这个目的,我们需要

  • 一个前驱节点,他的的作用是当发现目标结点的时候,这个节点指向的的是目标结点的前一个结点;
  • 一个当前节点,我们遍历链表的节点
  • 一个用来保存头节点的节点,如果我们直接用头节点遍历链表,那么最后我们会失去头节点的索引
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            return head;
        }
        ListNode dummy = new ListNode(-1, head);
        ListNode cur = head;
        ListNode pre = dummy;
        while (cur != null) {
            if (cur.val == val) {
                pre.next = cur.next;
            } else {
                pre = cur;
            }
            cur = cur.next;
        }
        return dummy.next;
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

707. 设计链表

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点

思路

本题就是个正常的链表设计,这五个接口都是常用的接口。 我用的思路是使用虚拟头节点;当初始化MyLinkedList list = new MyLinkedList()时,内部所存的节点实际上时一个前驱节点dummyHead,此节点的next节点才是链表的第一个节点。

特别注意:
命名一定要有一致性,不然写代码的时候容易弄混淆概念导致出bug!!!!! 例如:

  • dummyHead 是虚拟头节点, dummyHead.next才是链表的第一个节点
  • cur 是当前节点,也就是我们当前所处的链表位置。
  • pre 是当前节点的前一个节点,删除节点时我们需要的这个节点

剩下的注意事项都写在代码注释里了

class Node {
    int val;
    Node next;
    Node () {};
    Node(int val) {
        this.val = val;
        this.next = null;
    } 
    Node(int val, Node next) {
        this.val = val;
        this.next = next;
    } 
}
class MyLinkedList {
    Node dummyHead;
    int size;

    public MyLinkedList() {
        // 虚拟头节点
        dummyHead = new Node(0);
        size = 0;
    }
    
    public int get(int index) {
        // 因为 index 是从0开始,所以链表最后一个节点的下标是 size-1
        if (index < 0 || index >= size) {
            return -1;
        }
        Node cur = dummyHead;
        // 这里要特别注意,因为使用的是虚拟头节点,所以是 i <= index
        // i = 0 的时候是虚拟头节点
        for (int i = 0; i <= index; i++) {
            cur = cur.next;
        }
        return cur.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;
        }
        size++;
        Node pre = dummyHead;
        for (int i = 0; i < index; i++) {
            pre = pre.next;
        }
        pre.next = new Node(val, pre.next);
    }
    
    public void deleteAtIndex(int index) {
        if (index >= size || index < 0) {
            return;
        }
        size--;
        Node pre = dummyHead;
        for (int i = 0; i < index; i++) {
            pre = pre.next;
        }
        pre.next = pre.next.next;
    }
}

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

image.png

思路

通过改变next节点的指向来实现链表反转。
我们需要三个指针来协助我们完成翻转:

  • pre指针初始化为null
  • cur指针初始化为头节点
  • temp指针为cur.next

cur != null 我们就进行如下操作:

temp = cur.next
cur.next = pre
pre = cur
cur = temp

cur = null时,我们就完成了整个链表的翻转,可以自己好好体会一下,或者在草稿纸上画图。
这时节点pre就是我们新链表的头节点。

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode temp = null;
        while (cur != null) {
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)