【Leecode】代码随想录链表篇day3(链表增删改查&反转)

链表节点定义

单链表

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

双链表

public class DListNode{
    int val;
    DListNode pre;
    DListNode next;
    DListNode() {};
    DListNode(int val, DListNode pre, DListNode next){this.val = val; this.pre = pre;this.next = next;}
}

链表增删改查

  • 题目链接:单双链表的增删改查
  • 我的解法:一开始,通过52/65用例,暂时用输入输出法找不到问题所在,感觉时index和下标的关系出问题,具体应该是get函数和addAtIndex两个函数有问题,绷不住了。。。
public class ListNode{
    int val;
    ListNode next;
    ListNode() {};
    ListNode(int val, ListNode next) {this.val = val; this.next = next;}
}
class MyLinkedList {
    int size;//便于判断index是否合理
    ListNode head;//虚拟头节点
    public MyLinkedList() {
        this.size = 0;
        this.head = new ListNode(-1, null);
    }
    
    public int get(int index) {
        System.out.print("get");
        if(index < 0 || index > size) return -1;
        ListNode p = head.next;
        while((p != null) && (index > 0)){p = p.next;index--;}
        if(p == null) return -1;
        return p.val;
    }
    
    public void addAtHead(int val) {
        System.out.print("addAtHead");
        ListNode newNode = new ListNode(val, null);
        newNode.next = head.next;
        head.next = newNode;
        size++;
        // System.out.print("addAtHead");
        // ListNode tmp = head.next;
        // while(tmp != null)
        // {
        //     System.out.print(tmp.val);
        //     System.out.print(" ");
        //     tmp = tmp.next;
        // }
    }
    
    public void addAtTail(int val) {
        System.out.print("addAtTail");
        ListNode newNode = new ListNode(val, null);
        ListNode p = head;
        while(p.next != null){p = p.next;}
        p.next = newNode;
        size++;
        // System.out.print("addAtTail");
        // ListNode tmp = head.next;
        // while(tmp != null)
        // {
        //     System.out.print(tmp.val);
        //     System.out.print(" ");
        //     tmp = tmp.next;
        // }
    }
    
    public void addAtIndex(int index, int val) {
        System.out.print("addAtIndex");
        if(index < 0 || index > size) return;
        ListNode newNode = new ListNode(val, null);
        ListNode p = head.next;
        ListNode pre = head;
        while((p != null) && (index > 0)){pre = pre.next; p = p.next;index--;}
        newNode.next = pre.next;
        pre.next = newNode;
        size++;
        // System.out.print("addAtIndex");
        // ListNode tmp = head.next;
        // while(tmp != null)
        // {
        //     System.out.print(tmp.val);
        //     System.out.print(" ");
        //     tmp = tmp.next;
        // }
    }
    
    public void deleteAtIndex(int index) {
        System.out.print("deleteAtIndex");
        if(index < 0 || index > size) return;
        ListNode pre = head;ListNode p = head.next;
        while((p != null) && (index > 0)){pre = pre.next; p = p.next; index--;}
        if(p != null)pre.next = p.next;
        else pre.next = null;
        size--;
        // System.out.print("deleteAtIndex");
        // ListNode tmp = head.next;
        // while(tmp != null)
        // {
        //     System.out.print(tmp.val);
        //     System.out.print(" ");
        //     tmp = tmp.next;
        // }
    }
}

后来发现就是index从零开始但是size从1开始问题,这个需要在涉及index的函数中注意,其次就是addAtIndex的定义本身很不一样,size==index时直接插就行了,贴一下正确自己的解法,虽然很慢

public class ListNode{
    int val;
    ListNode next;
    ListNode() {};
    ListNode(int val, ListNode next) {this.val = val; this.next = next;}
}
class MyLinkedList {
    int size;//便于判断index是否合理
    ListNode head;//虚拟头节点
    public MyLinkedList() {
        this.size = 0;
        this.head = new ListNode(-1, null);
    }
    
    public int get(int index) {
        if(index < 0 || index >= size) return -1;
        ListNode p = head.next;
        while(index > 0){p = p.next;index--;}
        return p.val;
    }
    
    public void addAtHead(int val) {
        ListNode newNode = new ListNode(val, null);
        newNode.next = head.next;
        head.next = newNode;
        size++;
    }
    
    public void addAtTail(int val) {
        ListNode newNode = new ListNode(val, null);
        ListNode p = head;
        while(p.next != null){p = p.next;}
        p.next = newNode;
        size++;
    }
    
    public void addAtIndex(int index, int val) {
        if(index < 0 || index > size) return;
        ListNode newNode = new ListNode(val, null);
        if(index == size) {addAtTail(val);return;}
        ListNode p = head.next;
        ListNode pre = head;
        while(index > 0){pre = pre.next; p = p.next;index--;}
        newNode.next = pre.next;
        pre.next = newNode;
        size++;
    }
    
    public void deleteAtIndex(int index) {
        if(index < 0 || index >= size) return;
        ListNode pre = head;ListNode p = head.next;
        while(index > 0){pre = pre.next; p = p.next; index--;}
        pre.next = p.next;
        size--;
    }
}

上面addAtHead和addAtTail都可以在内部调用addAtIndex实现,分别为前者addAtIndex(0,val),后者addAtTail(size, val),同时增加size记录链表长度便于判定index是否合法,这样可以在代码中不判空

  • 双链表版本
public class DListNode{
    int val;
    DListNode pre;
    DListNode next;
    DListNode() {};
    DListNode(int val, DListNode pre, DListNode next){this.val = val; this.pre = pre;this.next = next;}
}
class MyLinkedList {
    int size;//便于判断index是否合理
    DListNode head;//虚拟头节点
    DListNode tail;
    public MyLinkedList() {
        this.size = 0;
        this.head = new DListNode(-1, null, null);
        this.tail = new DListNode(-1, null, null);
        //双向链表互指
        head.next = tail;
        tail.pre = head;
    }
    
    public int get(int index) {
        if(index < 0 || index >= size) return -1;
        DListNode p = head.next;
        if(index >= size/2){//比较巧,那边短从那边开始
            p = tail.pre;
            index = size-index-1;
            while(index > 0){p = p.pre;index--;}
        }
        else{
            while(index > 0){p = p.next;index--;}
        }
        return p.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 < 0 || index > size) return;
        size++;
        DListNode newNode = new DListNode(val, null, null);
        if(index == size) {
            newNode.pre = tail.pre;
            tail.pre = newNode;
            newNode.next = tail;
            newNode.pre.next = newNode;
            return;}
        DListNode p = head.next;
        while(index > 0){p = p.next;index--;}
        p.pre.next = newNode;
        newNode.pre = p.pre;
        newNode.next = p;
        p.pre = newNode;
    }
    
    public void deleteAtIndex(int index) {
        if(index < 0 || index >= size) return;
        DListNode p = head.next;
        while(index > 0) {p = p.next; index--;}
        p.pre.next = p.next;
        p.next.pre = p.pre;
        size--;
    }
}
  • 复杂度:时空复杂度均为 O ( O( O(调用类中函数次数之和 ) ) )

删除元素

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head == null){return null;}
        ListNode h = new ListNode(0, head);
        ListNode pre = h;ListNode p = head;
        while(p != null)
        {
            if(p.val != val)
            {
                p = p.next;
                pre = pre.next;
            }
            else
            {
                pre.next = p.next;
                p = pre.next;
            }
        }
        return h.next;
    }
}

总结:不同于之前一直使用c++解题,java的指针概念是通过结构体模拟

  • 其他解法:不引入虚拟节点,head直接作为头节点
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while(head != null && head.val == val) head = head.next;
        if(head == null) return null;
        ListNode pre = head; ListNode p = head.next;
        while(p != null)
        {
            while(p != null && p.val != val) {p = p.next;pre = pre.next;}
            if(p!=null){pre.next = p.next;p = pre.next;}
        }
        return head;
    }
}

比起创建虚拟节点不带头结点的链表执行删除操作更快
删除操作的标配是pre指向前节点,p指向当前节点,要注意当前节点可能为空指针的判断。

  • 复杂度:时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

反转链表

  • 题目链接:反转单链表
  • 我的解法:引入虚拟头节点+头插法
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode h = new ListNode(-1, head);
        ListNode pre = head.next;
        ListNode p = pre.next;
        head.next = null;//头插法一定要记得将最末端置空
        while(pre != null)
        {
            pre.next = h.next;
            h.next = pre;
            pre = p;
            if(pre != null) p = pre.next;
        }
        return h.next;
    }
}

整体比较熟悉,记住在断开链表进行头插前要将断开点节点next指向null,不然会成环。

  • 复杂度:时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
  • 12
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值