算法训练营day03 | 链表理论基础,203.移除链表元素,707.设计链表,206.反转链表

47 阅读4分钟

链表基础

203.移除链表元素

题目链接

做题小结

这道题比较简单,具体考察了链表的移动和删除。AC了。

while((head != null) && (head.val == val)){
    head = head.next;
}

if(head == null){
    return head;
}

ListNode prevNode = head;
ListNode currNode = head.next;

while(currNode != null){
    if(currNode.val == val){
        currNode = currNode.next;
        prevNode.next = currNode;            
    }else{
        prevNode = currNode;
        currNode = currNode.next;
    }

}

return head;

记录下随想录的代码

文章链接

if (head == null) {
    return head;
}
// 因为删除可能涉及到头节点,所以设置dummy节点,统一操作
ListNode dummy = new ListNode(-1, head);
ListNode pre = dummy;
ListNode cur = head;
while (cur != null) {
    if (cur.val == val) {
        pre.next = cur.next;
    } else {
        pre = cur;
    }
    cur = cur.next;
}
return dummy.next;
while (head != null && head.val == val) {
    head = head.next;
}
// 已经为null,提前退出
if (head == null) {
    return head;
}
// 已确定当前head.val != val
ListNode pre = head;
ListNode cur = head.next;
while (cur != null) {
    if (cur.val == val) {
        pre.next = cur.next;
    } else {
        pre = cur;
    }
    cur = cur.next;
}
return head;

707.设计链表

题目链接

做题小结

这题主要考察了linked list的构造,基本逻辑就是要理解链表。

class ListNode {
    int val;
    ListNode next;
    ListNode pred;
    ListNode(){}
    ListNode(int val) {
        this.val=val;
    }
}
class MyLinkedList {
    ListNode head = null;
    int size;

    public MyLinkedList() {
        size = 0;
    }

    public int get(int index) {
        if(size == 0){
            return -1;
        }
        if (index < 0 || index > size) {
            return -1;
        }
        ListNode currentNode = head;
        int cnt = 0;
        while (currentNode != null){
            if(cnt == index){
                return currentNode.val;
            }else{
                cnt++;
                currentNode = currentNode.next;
            }
        }

        return -1;
    }

    //在链表最前面插入一个节点,等价于在第0个元素前添加
    public void addAtHead(int val) {
        if(head == null){
            head = new ListNode(val);
            size++;
            return;
        }

        ListNode newNode = new ListNode(val);
        newNode.next = head;
        head = newNode;
        size++;
    }

    //在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加
    public void addAtTail(int val) {
        if(head == null){
            head = new ListNode(val);
            size++;
            return;
        }

        ListNode pred = head;
        ListNode curr = head.next;
        while(curr != null){
            pred = curr;
            curr = curr.next;
        }
        pred.next = new ListNode(val);
        size++;
    }

    // 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
    // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果 index 大于链表的长度,则返回空
    public void addAtIndex(int index, int val) {
        if(index == 0){
            addAtHead(val);
            return;
        }

        if(index == size){
            addAtTail(val);
            return;
        }

        if (index > size || index <0) {
            return;
        }

        ListNode newNode = new ListNode(val);

        //找到要插入节点的前驱
        ListNode pred = head;
        ListNode curr = head.next;
        int cnt = 1;
        while(curr != null){
            if(cnt == index){
                pred.next = newNode;
                newNode.next = curr;
                size++;
                return;
            }else{
                pred = curr;
                curr = curr.next;
                cnt++;
            }
        }
        
    }

    //删除第index个节点
    public void deleteAtIndex(int index) {
        if (index < 0 || index > size) {
            return;
        }
        if (index == 0) {
            if(size == 0){
                return;
            }
            head = head.next;
            size--;
	        return;
        }
        ListNode pred = head;
        ListNode curr = head.next;
        int cnt = 1;
        while(curr != null){
            if(cnt == index){
                pred.next = curr.next;
                size--;
                return;
            }else{
                pred = curr;
                curr = curr.next;
                cnt++;
            }
        }
    }
}

随想录讲解

文章链接 随想录使用了虚拟头的方法进行构造,而我的方法则是在链表本身进行操作,没有使用虚拟头。时间复杂度上还是有一定差别,下次可以尝试使用虚拟头。

class ListNode {
    int val;
    ListNode next;
    ListNode(){}
    ListNode(int val) {
        this.val=val;
    }
}
class MyLinkedList {
    //size存储链表元素的个数
    int size;
    //虚拟头结点
    ListNode head;

    //初始化链表
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);
    }

    //获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点
    public int get(int index) {
        //如果index非法,返回-1
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode currentNode = head;
        //包含一个虚拟头节点,所以查找第 index+1 个节点
        for (int i = 0; i <= index; i++) {
            currentNode = currentNode.next;
        }
        return currentNode.val;
    }

    //在链表最前面插入一个节点,等价于在第0个元素前添加
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }

    //在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    // 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
    // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果 index 大于链表的长度,则返回空
    public void addAtIndex(int index, int val) {
        if (index > size) {
            return;
        }
        if (index < 0) {
            index = 0;
        }
        size++;
        //找到要插入节点的前驱
        ListNode pred = head;
        for (int i = 0; i < index; i++) {
            pred = pred.next;
        }
        ListNode toAdd = new ListNode(val);
        toAdd.next = pred.next;
        pred.next = toAdd;
    }

    //删除第index个节点
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) {
            return;
        }
        size--;
        if (index == 0) {
            head = head.next;
	    return;
        }
        ListNode pred = head;
        for (int i = 0; i < index ; i++) {
            pred = pred.next;
        }
        pred.next = pred.next.next;
    }
}

206.反转链表

题目链接

做题小结

这题也比较简单,基本上如果对链表的逻辑比较了解的话都能够写出来。

if(head == null){
    return head;
}

ListNode newHead = head;
head = head.next;
newHead.next = null;
while(head != null){
    ListNode newNode = head;
    head = head.next;
    newNode.next = newHead;
    newHead = newNode;
}

return newHead;

随想录讲解

随想录提供了2种方法-双指针和递归法。双指针法和我的逻辑相同,递归法在空间复杂度上有一定优化。

  1. 双指针
ListNode prev = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null) {
    temp = cur.next;// 保存下一个节点
    cur.next = prev;
    prev = cur;
    cur = temp;
}
return prev;
  1. 递归
class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(null, head);
    }

    private ListNode reverse(ListNode prev, ListNode cur) {
        if (cur == null) {
            return prev;
        }
        ListNode temp = null;
        temp = cur.next;// 先保存下一个节点
        cur.next = prev;// 反转
        // 更新prev、cur位置
        // prev = cur;
        // cur = temp;
        return reverse(cur, temp);
    }
}