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

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

最后:

  1. 更适合while循环:因为会删除节点。
  2. 删除节点:虚拟头节点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);
 */

新的:也有不足之处(对比代码随想录)

  1. 没有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;
    }
}

总结

  1. 删除节点时,一定会有个temp临时存储。
  2. prev = null:类似于虚拟头节点的作用。

尝试递归解法

果然还是不太会,有点抽象。先看了代码随想录的递归解法一,自己手动试了一下:

  1. 将递归最后的结果一直返回。
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);
    }
}

思路二:从后往前反转
这里逻辑还是有些绕:

  1. 从尾到头反转:尾部的节点反转了,再来反转前面的节点。
  2. 此时需要注意,head.next = null 主要是为了应对头节点(反转之前的头节点)。如果不是头节点,那么跳出本次递归后,head.next又会被赋上新值。
  3. 将新的头节点一路返回。
  4. 终止条件。
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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值