0
点赞
收藏
分享

微信扫一扫

链表刷题——20220404

读思意行 2022-04-05 阅读 99
java


技巧:使用虚拟节点prev可以避免处理空指针的情况,降低代码的复杂性

21. 合并两个有序链表 - 力扣(LeetCode) (leetcode-cn.com)

思路:将小的节点和prev相连

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode prev=new ListNode(-1);
        ListNode cur=prev;
        while(list1!=null&&list2!=null){
            if(list1.val<list2.val){
                cur.next=list1;
                list1=list1.next;
            }else{
                cur.next=list2;
                list2=list2.next;
            }
            cur=cur.next;
        }
        if(list1==null){
            cur.next=list2;
        }
        if(list2==null){
            cur.next=list1;
        }
        return prev.next;
    }

 23. 合并K个升序链表 - 力扣(LeetCode) (leetcode-cn.com)

 思路:利用堆将小的节点筛选出来和prev相连

 public ListNode mergeKLists(ListNode[] lists) {
        if(lists.length==0){
            return null;
        }
        ListNode prev=new ListNode(-1);
        ListNode cur=prev;
        PriorityQueue<ListNode>queue=new PriorityQueue<>((o1,o2)->(o1.val-o2.val));
        for(ListNode head:lists){
            if(head!=null){
                queue.add(head);
            }
        }
        while(!queue.isEmpty()){
            ListNode node=queue.poll();
            cur.next=node;
            cur=cur.next;
            if(node.next!=null){
                queue.add(node.next);
            }
        }
        return prev.next;
    }

优先队列 queue中的元素个数最多是 k,所以一次 poll 或者 add 方法的时间复杂度是 O(logk);

所有的链表节点都会被加入和弹出 queue,所以算法整体的时间复杂度是 O(Nlogk),其中 k 是链表的条数,N 是这些链表的节点总数。


技巧:双指针

剑指 Offer 22. 链表中倒数第k个节点 - 力扣(LeetCode) (leetcode-cn.com)

public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode p1=head;
        ListNode p2=head;
        while(k!=0){
            p1=p1.next;
            k--;
        }
        while(p1!=null){
            p1=p1.next;
            p2=p2.next;
        }
        return p2;
    }

  时间复杂度是 O(N)

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode) (leetcode-cn.com)

使用了虚拟头结点的技巧,也是为了防止出现空指针的情况,比如说链表总共有 5 个节点,题目就让你删除倒数第 5 个节点,也就是第一个节点,那按照算法逻辑,应该首先找到倒数第 6 个节点。但第一个节点前面已经没有节点了,这就会出错。

但有了我们虚拟节点 dummy 的存在,就避免了这个问题,能够对这种情况进行正确的删除。

public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode prev = new ListNode(-1);
        prev.next = head;
        ListNode node = findFromEnd(prev, n + 1);
        node.next = node.next.next;
        return prev.next;
    }
    public ListNode findFromEnd(ListNode head, int k) {
        ListNode p1 = head;
        ListNode p2 = head;
        while(k!=0){
            p1 = p1.next;
            k--;
        }
        while (p1 != null) {
            p2 = p2.next;
            p1 = p1.next;
        }
        return p2;
    }

876. 链表的中间结点 - 力扣(LeetCode) (leetcode-cn.com)

    public ListNode middleNode(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        return slow;
    }

 141. 环形链表 - 力扣(LeetCode) (leetcode-cn.com)

 思路:快慢指针相遇证明有环 

public boolean hasCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if(slow==fast){
                return true;
            }
        }
        return false;
    }

 142. 环形链表 II - 力扣(LeetCode) (leetcode-cn.com)

思路:当快慢指针相遇时,让其中任一个指针指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置

 public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(slow==fast){
                break;
            }
        }
            if(fast==null||fast.next==null){
                return null;
            }
            slow=head;
            while(slow!=fast){
                slow=slow.next;
                fast=fast.next;
            }
            return slow;
    }

 160. 相交链表 - 力扣(LeetCode) (leetcode-cn.com)

思路:让 p1 遍历完链表 A 之后开始遍历链表 B,让 p2 遍历完链表 B 之后开始遍历链表 A,这样相当于「逻辑上」两条链表接在了一起。 

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1=headA;
        ListNode p2=headB;
        while(p1!=p2){
            if(p1==null){
                p1=headB;
            }else{
                p1=p1.next;
            }
            if(p2==null){
                p2=headA;
            }else{
                p2=p2.next;
            }
        }
        return p1;
    }

技巧:递归

206. 反转链表

public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }

反转链表前 N 个节点

ListNode Next = null; // 后驱节点

// 反转以 head 为起点的 n 个节点,返回新的头结点
ListNode reverseN(ListNode head, int n) {
    if (n == 1) {
        // 记录第 n + 1 个节点
        Next = head.next;
        return head;
    }
    // 以 head.next 为起点,需要反转前 n - 1 个节点
    ListNode last = reverseN(head.next, n - 1);
    head.next.next = head;
    // 让反转之后的 head 节点和后面的节点连起来
    head.next = Next;
    return last;
}

92. 反转链表 II

ListNode reverseBetween(ListNode head, int m, int n) {
        if (m == 1) {
            return reverseN(head, n);
        }
        head.next = reverseBetween(head.next, m - 1, n - 1);
        return head;
    }

    ListNode Next = null; // 后驱节点
    ListNode reverseN(ListNode head, int n) {
        if (n == 1) {
            Next = head.next;
            return head;
        }
        ListNode last = reverseN(head.next, n - 1);
        head.next.next = head;
        head.next = Next;
        return last;
    }
举报

相关推荐

LeetCode刷题 --- 链表

链表刷题总结

C++刷题 -- 链表

链表必刷题一

【LeetCode】单链表——刷题

算法刷题之链表

0 条评论