概述

主要是LeetCode上面常考的一些链表面试手撕题,一些太简单的就没有列出来,比如反转链表,找中间节点,找倒数第K个节点,这些都是基本操作,还是要掌握的,列出来的都是一些比较综合,难写一点的题目~

题目列表

  • 重排链表
  • 给单链表+1
  • 复制带随机指针的链表
  • 有序链表转二叉搜索树
  • 两数相加
  • 旋转链表
  • 奇偶链表 【2】
  • 环形链表:判断有环,找环路口 【2】
  • 合并K个排序链表-堆/两两归并【2】
  • 翻转K/不足K也翻转【2】
  • 排序链表【4】
  • 删除链表中的重复元素【3】

    重排链表

    一道题目,涉及链表找中点,翻转链表,链表合并

    1. class Solution {
    2. public ListNode findMid(ListNode head){
    3. ListNode f = head, s = head;
    4. while(f != null && f.next != null){
    5. f = f.next.next;
    6. s = s.next;
    7. }
    8. return s;
    9. }
    10. public ListNode reverse(ListNode head){
    11. if(head == null || head.next == null) return head;
    12. ListNode newHead = reverse(head.next);
    13. head.next.next = head;
    14. head.next = null;
    15. return newHead;
    16. }
    17. public void merge(ListNode l1, ListNode l2){
    18. while(l1 != null && l2 != null){
    19. ListNode l1next = l1.next;
    20. ListNode l2next = l2.next;
    21. l1.next = l2;
    22. l2.next = l1next;
    23. l1 = l1next;
    24. l2 = l2next;
    25. }
    26. }
    27. public void reorderList(ListNode head) {
    28. ListNode midNode = findMid(head);
    29. ListNode head2 = midNode.next;
    30. midNode.next = null;
    31. ListNode l2 = reverse(head2);
    32. merge(head, l2);
    33. }
    34. }

给单链表加一

  • 先找第一个非9节点
  • 将非9节点后面的节点,全部置为0,因为后面数字一定全是9
  • 注意999的情况 - 判断虚拟头结点有没有改变
    class Solution {
      public ListNode plusOne(ListNode head) {
          ListNode dummy = new ListNode(0);
          dummy.next = head;
          ListNode cur = dummy;
          ListNode notNine = cur;
          while(cur != null){
              if(cur.val != 9) notNine = cur;
              cur = cur.next;
          }
          notNine.val = notNine.val + 1;
          ListNode nine = notNine.next;
          while(nine != null){
              nine.val = 0;
              nine = nine.next;
          }
          return dummy.val == 0 ? dummy.next : dummy;
      }
    }
    

复制带随机指针的链表

  • 链表复制
  • 点和边的复制Image 3.png
  • 两个链表的拆分

Image 4.png

class Solution {
    public Node copyRandomList(Node head) {
        //复制点 
        //复制点和复制边都要跳两个p = p.next.next
        for(Node p = head; p != null; p = p.next.next){
            Node p2 = new Node(p.val);
            p2.next = p.next;
            p.next = p2;
        }
        //复制边
        for(Node p = head; p != null; p = p.next.next){
            if(p.random != null){
                p.next.random = p.random.next;
            }
        }
        //拆分链表
        Node dummy = new Node(-1); Node cur = dummy;
        for(Node p = head; p != null; p = p.next){
            Node q = p.next;
            p.next = q.next;
            cur.next = q;
            cur = cur.next;
        }
        return dummy.next;
    }
}

有序链表转换二叉搜索树

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        if(head == null) return null;
        if(head.next == null) return new TreeNode(head.val);
        ListNode pre = head, s = head, f = head;
        while(f != null && f.next != null){
            pre = s;
            f = f.next.next;
            s = s.next;
        }
        pre.next = null;
        TreeNode root = new TreeNode(s.val);
        root.left = sortedListToBST(head);
        root.right = sortedListToBST(s.next);
        return root;
    }
}

两数相加 II

先翻转

class Solution {
    public ListNode reverse(ListNode head){
        if(head == null || head.next == null) return head;
        ListNode newHead = reverse(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
    public ListNode addRTwo(ListNode l1, ListNode l2){
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        int t = 0;
        while(l1 != null || l2 != null || t != 0){
            if(l1 != null){t += l1.val; l1 = l1.next;}
            if(l2 != null){t += l2.val; l2 = l2.next;}
            cur.next = new ListNode(t%10);
            t /= 10;
            cur = cur.next;
        }
        return dummy.next;
    }
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode l1r = reverse(l1);
        ListNode l2r = reverse(l2);
        return reverse(addRTwo(l1r, l2r));
    }
}

用两个栈

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Stack<Integer> stk1 = new Stack<>();
        Stack<Integer> stk2 = new Stack<>();
        while(l1 != null){ stk1.push(l1.val); l1 = l1.next;}
        while(l2 != null){ stk2.push(l2.val); l2 = l2.next;}

        ListNode dummy = new ListNode(-1);
        int t = 0;
        while(!stk1.isEmpty() || !stk2.isEmpty() || t != 0){
            if(!stk1.isEmpty()) t += stk1.pop();
            if(!stk2.isEmpty()) t += stk2.pop();
            ListNode node = new ListNode(t % 10);
            t /= 10;
            node.next = dummy.next;
            dummy.next = node;
        }
        return dummy.next;
    }
}

旋转链表

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head == null) return null;
        int n = 0;
        ListNode tail = null;
        for(ListNode p = head; p != null; p = p.next) {
            if(p.next == null) tail = p;
            n++;
        }
        if(k % n == 0) return head;
        k %= n;
        ListNode f = head, s = head;
        for(int i = 0; i < k; i++){
            f = f.next;
        }
        while(f.next != null){
            f = f.next;
            s = s.next;
        }
        ListNode newHead = s.next;
        s.next = null;
        tail.next = head;
        return newHead;
    }
}

奇偶链表

奇偶链表

两个指针分别链接一下即可

class Solution {
    public ListNode oddEvenList(ListNode head) {
        ListNode odd = new ListNode(-1);
        ListNode eve = new ListNode(-1);
        ListNode curodd = odd, cureve = eve;

        for(int i = 1; head != null; head = head.next, i++){
            if(i % 2 != 0){
                curodd.next = head;
                curodd = curodd.next;
            }else{
                cureve.next = head;
                cureve = cureve.next;
            }
        }
        cureve.next = null;
        curodd.next = eve.next;
        return odd.next;
    }
}

排序奇数生偶数降低链表

在上面的基础上增加一个翻转,和合并即可

class Solution {
    public ListNode reverse(ListNode head){
        ListNode pre = null;
        while(head != null){
            ListNode nextNode = head.next;
            head.next = pre;
            pre = head;
            head = nextNode;
        }
        return pre;
    }
    public void merge(ListNode l1, ListNode l2){
        while(l1 != null && l2 != null){
            ListNode l1next = l1.next;
            ListNode l2next = l2.next;
            l1.next = l2;
            l2.next = l1next;
            l1 = l1next;
            l2 = l2next;
        }
    }
    public ListNode oddEvenList(ListNode head) {
        ListNode odd = new ListNode(-1);
        ListNode eve = new ListNode(-1);
        ListNode curodd = odd, cureve = eve;

        for(int i = 1; head != null; head = head.next, i++){
            if(i % 2 != 0){
                curodd.next = head;
                curodd = curodd.next;
            }else{
                cureve.next = head;
                cureve = cureve.next;
            }
        }
        cureve.next = null;
        curodd.next = null;

        ListNode l2 = reverse(eve.next);
        merge(odd.next, l2);
        return odd.next;
    }
}

环形链表

环形链表 - 判断环

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode f = head, s = head;
        while(f != null && f.next != null){
            f = f.next.next;
            s = s.next;
            if(f == s) return true;
        }
        return false;
    }
}

环形链表 - 找环的路口

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode f = head, s = head;
        while(f != null && f.next != null){
            f = f.next.next;
            s = s.next;
            if(f == s) break;
        }
        if(f == null || f.next == null) return null;
        s = head;
        while(f != s){
            f = f.next;
            s = s.next;
        }
        return f;
    }
}

合并K个升序链表

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        PriorityQueue<ListNode> pq = new PriorityQueue<>((l1, l2) -> l1.val - l2.val);
        for(ListNode node : lists){
            if(node != null) pq.offer(node);
        }
        while(!pq.isEmpty()){
            ListNode node = pq.poll();
            cur.next = node;
            cur = cur.next;
            if(node.next != null) pq.offer(node.next);
        }
        return dummy.next;
    }
}

两两归并

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0) return null;
        return merge(lists, 0, lists.length - 1);
    }
    public ListNode merge(ListNode[] lists, int l, int r){
        if(l == r) return lists[l];
        else if(l + 1 == r) return mergeTwo(lists[l], lists[r]);
        else{
            int mid = l + r >> 1;
            ListNode lnode = merge(lists, l, mid);
            ListNode rnode = merge(lists, mid + 1, r);
            return mergeTwo(lnode, rnode);
        }
    }
    public ListNode mergeTwo(ListNode l1, ListNode l2){
        if(l1 == null) return l2;
        if(l2 == null) return l1;
        if(l1.val <= l2.val){
            l1.next = mergeTwo(l1.next, l2);
            return l1;
        }else{
            l2.next = mergeTwo(l1, l2.next);
            return l2;
        }
    }
}

K 个一组翻转链表

不足K个不翻转

class Solution {
    public ListNode reverse(ListNode head){
        ListNode pre = null;
        while(head != null){
            ListNode nextNode = head.next;
            head.next = pre;
            pre = head;
            head = nextNode;
        }
        return pre;
    }
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy, s = head, f = dummy;
        while(f.next != null){
            for(int i = 0; i < k && f != null; i++) f = f.next;
            if(f == null) break;
            ListNode later = f.next;
            f.next = null;

            reverse(s);
            pre.next = f;
            s.next = later;

            pre = s;
            f = s;
            s = s.next;
        }
        return dummy.next;
    }
}

不足K个的部分也翻转

class Solution {
    public ListNode reverse(ListNode head){
        ListNode pre = null;
        while(head != null){
            ListNode nextNode = head.next;
            head.next = pre;
            pre = head;
            head = nextNode;
        }
        return pre;
    }
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy, s = head, f = dummy;
        while(f.next != null){
            for(int i = 0; i < k && f != null; i++) f = f.next;
            if(f == null) {
                if(s == null) break;
                ListNode tail = reverse(s);
                pre.next = tail;
                s.next = null;
                break;
            }
            ListNode later = f.next;
            f.next = null;

            reverse(s);
            pre.next = f;
            s.next = later;

            pre = s;
            f = s;
            s = s.next;
        }
        return dummy.next;
    }
}

排序链表

归并排序

class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode pre = head, f = head, s = head;
        while(f.next != null && f.next.next != null){
            pre  = s;
            f = f.next.next;
            s = s.next;
        }
        ListNode head2 = pre.next;
        pre.next = null;
        ListNode l = sortList(head), r = sortList(head2);
        return mergeTwo(l, r);
    }
    public ListNode mergeTwo(ListNode l1, ListNode l2){
        if(l1 == null) return l2;
        if(l2 == null) return l1;
        if(l1.val <= l2.val){
            l1.next = mergeTwo(l1.next, l2);
            return l1;
        }else{
            l2.next = mergeTwo(l1, l2.next);
            return l2;
        }
    }
}

快排排序

class Solution {
    public ListNode getTail(ListNode head){
        while(head.next != null) head = head.next;
        return head;
    }
    public int getMidValue(ListNode head){
        ListNode f = head, s = head;
        while(f != null && f.next != null){
            f = f.next.next;
            s = s.next;
        }
        return s.val;
    }
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode l = new ListNode(-1), m = new ListNode(-1), r = new ListNode(-1);
        ListNode ltail = l, mtail = m, rtail = r;
        int x = getMidValue(head);
        while(head != null){
            if(head.val < x){
                ltail.next = head;
                ltail = ltail.next;
            }else if(head.val == x){
                mtail.next = head;
                mtail = mtail.next;
            }else{
                rtail.next = head;
                rtail = rtail.next;
            }
            head = head.next;
        }
        ltail.next = null; mtail.next = null; rtail.next = null;
        l.next = sortList(l.next);
        r.next = sortList(r.next);
        getTail(l).next = m.next;
        getTail(l).next = r.next;
        return l.next;
    }
}

插入排序


class Solution {
    public ListNode insertionSortList(ListNode head) {
        ListNode dummy = new ListNode(-1);
        //head为当前要安排的节点
        while(head != null){
            ListNode headNext = head.next;
            ListNode p = dummy;
            //找到第一个比当前要安排的节点大的节点p.next
            //-----p->p.next->-----
            //-----p->head->p.next->-----
            while(p.next != null && p.next.val <= head.val) p = p.next;
            head.next = p.next;
            p.next = head;

            head = headNext;
        }
        return dummy.next;
    }
}

删除链表中的重复元素

删除排序链表中的重复元素 - 重复留一

删除过后,重复的元素只出现一次
链表 - 图3

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode cur = head;
        while(cur.next != null){
            if(cur.val == cur.next.val) cur = cur.next;
            else cur = cur.next;
        }
        return head;
    }
}

删除排序链表中的重复元素 - 重复不留

重复的元素一个不留
链表 - 图4

//dummy -> 1 ->2 ->3 -> 3....
//         s       f
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode s = dummy;
        while(s.next != null){
            ListNode f = s.next;
            while(f != null && f.val == s.next.val) f = f.next;
            if(s.next.next == f) s = s.next;
            else s.next = f;
        }
        return dummy.next;
    }
}

移除未排序链表中的重复节点 - 重复留一

输入:[1, 2, 3, 3, 2, 1]
输出:[1, 2, 3]

class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;
        while(pre.next != null){
            ListNode cur = pre.next;
            while(cur.next != null){
                if(pre.next.val == cur.next.val) cur.next = cur.next.next;
                else cur = cur.next;
            }
            pre = pre.next;
        }
        return dummy.next;
    }
}

移除未排序链表中的重复节点 - 重复不留

输入:[1, 2, 3, 4 , 5, 3, 6, 2, 1]
输出:[4, 5, 6]
在上面一题的基础上打一个标记判断有无重复即可
pre ->待判断(cur) -> cur.next

class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;
        boolean f = false;
        while(pre.next != null){
            ListNode cur = pre.next;
            while(cur.next != null){
                if(pre.next.val == cur.next.val) { f = true; cur.next = cur.next.next;}
                else cur = cur.next;
            }
            if(f == false){
                pre = pre.next;
            }else{
                pre.next = pre.next.next;
                f = false;
            }

        }
        return dummy.next;
    }
}