需要熟练的链表算法题

链表

1、从尾到头打印链表

public class T3 {
    // 创建list
    ArrayList<Integer> list = new ArrayList<>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        // 判断头节点是否为空
        if (listNode != null) {
            // 递归打印
            this.printListFromTailToHead(listNode.next);
            list.add(listNode.val);
        }
        return list;
    }
}

2、删除链表中重复的结点

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/

public ListNode deleteDuplication(ListNode pHead) {
    // 1. 边界,需要下一个结点做判断,因此还是需要判断下个结点是否为空
    if (pHead == null || pHead.next == null)
        return pHead;
    // 取下一个结点
    ListNode next = pHead.next;
    // 2. 判断当前结点和下一个结点是否相等
    if (pHead.val == next.val) {
        // 3. 如果相等,判断是否一直重复
        while (next != null && pHead.val == next.val)
            next = next.next;
        return deleteDuplication(next);
    } else {
        // 4, 否则不重复的话,就递归下一个结点
        pHead.next = deleteDuplication(pHead.next);
        return pHead;
    }
}

3.1 环形链表

https://leetcode-cn.com/problems/linked-list-cycle/

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

3.2、链表中环的入口结点

https://leetcode-cn.com/problems/linked-list-cycle-ii/submissions/

// 快慢指针
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null)
            return null;
        
        boolean hasCycle = false;
        ListNode p1 = head, p2 = head;
        while(p2.next != null && p2.next.next != null) {
            p1 = p1.next;
            p2 = p2.next.next;
            if (p1 == p2) {
                hasCycle = true;
                break;
            }
        }
        if(hasCycle){
            p2 = head;
            while (p1 != p2) {
                p1 = p1.next;
                p2 = p2.next;
            }
            return p1;
        } else {
            return null;
        }
    
    }
}

4、反转链表

https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/

public ListNode reverseList(ListNode head) {
    // 总感觉,本质还是两个指针,pre不过初始化为null,cur为head当前结点
    ListNode pre = null; 
    ListNode cur = head;
    while (cur != null) {
        // 1. 获取当前节点的下一个节点,方便cur下移动
        ListNode nextTemp = cur.next;
        // 2. 当前节点的下个节点指向前一个节点 (反转,那肯定cur的next指向pre咯)
        cur.next = pre; 
        // 3. pre移动下一个结点,那肯定是cur咯
        pre = cur; 
        // 4. cur移动下一个结点, 那肯定是nextTemp咯
        cur = nextTemp;
    }
    return pre;
}
public class T15 {
    public ListNode ReverseList(ListNode head) {
        // 判断
        if (head == null) return null;
        return reverse(null, head);
    }
    private ListNode reverse(ListNode pre, ListNode cur) {
        // 递归结束判断
        if (cur == null) return pre;
        // 1. 依然第一个方法遍历依然,得到cur的next,递归用
        ListNode next = cur.next;
        // 2. 反转操作
        cur.next = pre;
        return reverse(cur, next);
    }
}

5、链表中倒数第k个结点

https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/

public ListNode FindKthToTail(ListNode head, int k) {
    // 还是双指针
    // 1. 边界判断
    if (head == null)
        return null;
    ListNode p1 = head;
    // 2. 先让p1移动k步
    while (p1 != null && k-- > 0)
        p1 = p1.next;
    // 3. 这一步防止k大于head的长度
    if (k > 0)
        return null;
    ListNode p2 = head;
    // 4. 二者正常走,不过p1肯定先到末尾
    while (p1 != null) {
        p1 = p1.next;
        p2 = p2.next;
    }
    // 5. 返回p2
    return p2;
}

6、合并两个排序的链表

https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/

public class T16 {
    public ListNode Merge(ListNode list1,ListNode list2) {
        // 1. 如果list1为空,返回list2
        if (list1 == null) return list2;
        // 2. 如果list2为空,返回list1
        if (list2 == null) return list1;
        // 3. 如果list1.val < list2.val,则list1.next连接下一个比较值(递归比较)
        if (list1.val < list2.val) {
            list1.next = Merge(list1.next, list2);
            return list1;
        } else {
            // 4. 否则,list2.next 连接下一个比较值(递归比较)
            list2.next = Merge(list1, list2.next);
            return list2;
        }
    }
}

7、两个链表的第一个公共结点

https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

// 还是双指针
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    ListNode l1 = pHead1, l2 = pHead2;
    // 1. 循环条件
    while (l1 != l2) {
        // 2. 走完l1,从头走head2
        l1 = (l1 == null) ? pHead2 : l1.next;
        // 3. 走完l2,从头走head1
        l2 = (l2 == null) ? pHead1 : l2.next;
    }
    // 4. 返回l1
    return l1;
}

8. 两数相加(2819)

https://leetcode-cn.com/problems/add-two-numbers/

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 边界判断:3个
        // 1. l1和l2同时为空
        if (l1 == null && l2 == null) return null;
        // 2. l1为空,返回l2
        if (l1 == null) return l2;
        // 3. l2为空,返回l1
        if (l2 == null) return l1;
        // 三指针
        // 1. p1
        ListNode p1 = l1;
        // 2. p2
        ListNode p2 = l2;
        // 3. p3 特殊:返回最值链表
        ListNode l3 = new ListNode(-1);
        ListNode p3 = l3;
        // 4. 注意:进位
        int carried = 0;
        // 5. 循环条件,任意一个不为空即可
        while (p1 != null || p2 != null) {
            // p1不为空,获取p1val,否则0
            int a = p1 != null ? p1.val : 0;
            // p2不为空,获取p2val,否则0
            int b = p2 != null ? p2.val : 0;
            // 关键一步,(a+b+carried) % 10 个位
            p3.next = new ListNode((a + b + carried) % 10);
            // 加完,记得进位呀(a + b + carried) / 10
            carried = (a + b + carried) / 10;
            // 三个指针开始移动
            p3 = p3.next;
            p1 = p1 != null ? p1.next : null;
            p2 = p2 != null ? p2.next : null;
        }
        // 循环完之后,判断进位是否0,如果不是,new一个结点为1,是的话,就null
        p3.next = carried != 0 ? new ListNode(1) : null;
        // 返回l3下一个结点
        return l3.next;
    }
}

9. 合并K个排序链表(924)

https://leetcode-cn.com/problems/merge-k-sorted-lists/

最小堆

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        // 边界判断
        if (lists == null || lists.length == 0) return null;
        // 2. 堆,大顶堆
        PriorityQueue<ListNode> queue = new PriorityQueue<>((o1, o2) -> o1.val - o2.val);
        // 3. 返回结点
        ListNode dummy = new ListNode(0);
        // 4. 临时p
        ListNode p = dummy;
        // 5. 遍历k个lists,一个一个添加到queue
        for (ListNode node : lists) {
            if (node != null) queue.add(node);
        }
        // 6. 循环堆不为空
        while (!queue.isEmpty()) {
            // p的下个结点指向 取出堆中最大的链表结点
            p.next = queue.poll();
            // 移动
            p = p.next;
            // 如果下一个不为空,继续添加刚才堆中最大的下一个结点
            if (p.next != null) queue.add(p.next);
        }
        // 7. 返回下一个结点
        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);
    }

    private ListNode merge(ListNode[] lists, int left, int right) {
        // 归并排序一样
        if (left == right) return lists[left];
        int mid = left + (right - left) / 2;
        ListNode l1 = merge(lists, left, mid);
        ListNode l2 = merge(lists, mid + 1, right);
        return mergeTwoLists(l1, l2);
    }
    // 不过,最后归并的时候用的是合并两个排序的链表
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
}

10. 两两交换链表中的节点(947)

https://leetcode-cn.com/problems/swap-nodes-in-pairs/

class Solution {
    // 两两交换,那么双指针
    public ListNode swapPairs(ListNode head) {
        // 创建一个返回结点
        ListNode node = new ListNode(-1);
        // 该结点的next指向head
        node.next = head;
        // 在创建一个pre,移动到node
        ListNode pre = node;
        // 循环遍历
        // pre的next才是head,因此判断next next
        while (pre.next != null && pre.next.next != null) {
            // 获取l1, l2
            ListNode l1 = pre.next, l2 = pre.next.next;
            // 
            ListNode next = l2.next;
            l1.next = next;
            l2.next = l1;
            pre.next = l2;
            // pre移动一步
            pre = l1;
        }
        return node.next;
    }
}

11. 链表的中间结点(853)

https://leetcode-cn.com/problems/middle-of-the-linked-list/

// 双指针(快慢)
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode p = head, q = head;
        while (q != null && q.next != null) {
            q = q.next.next;
            p = p.next;
        }
        return p;
    }
}

12. 删除排序链表中的重复元素(603)

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/

// 和那个有点区别
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null) return head;
        head.next = deleteDuplicates(head.next);
        return head.val == head.next.val ? head.next : head;
    }
}

13. 回文链表(624)

https://leetcode-cn.com/problems/palindrome-linked-list/

这题考察了很多链表的题,挺综合额

class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        // 找中点
        ListNode slow = head, fast = head.next;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        // 奇偶情况
        if(fast != null) slow = slow.next;
        // cut
        cut(head, slow);
        // 比较
        return isEqual(head, reverse(slow));
        
    }
    
    // 切
    public void cut (ListNode head, ListNode cutNode) {
        ListNode node = head;
        // 循环遍历找到和cutNode相等的结点的前一个结点
        while(node.next != cutNode) {
            node = node.next;
        }
        // 然后直接null
        node.next = null;
    }
    
    // 反转链表派上用场
    public ListNode reverse(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null) {
            ListNode nextNode = cur.next;
            cur.next = pre;
            pre = cur;
            cur = nextNode;
        }
        return pre;
    }
    
    
    public boolean isEqual(ListNode l1, ListNode l2) {
        // 二者都不为空才行
        while(l1 != null && l2 != null) {
            // 二者值不相等直接false
            if(l1.val != l2.val) return false;
            // 移动
            l1 = l1.next;
            l2 = l2.next;
        }
        // 比如就true
        return true;
    }
}

14. 奇偶链表(317)

https://leetcode-cn.com/problems/odd-even-linked-list/

class Solution {
    public ListNode oddEvenList(ListNode head) {
        if (head == null) return head;
        // 临时变量 奇头 偶头下 偶头部
        ListNode odd = head, even = head.next, evenHead = even;
        // 循环遍历偶和偶下不为空
        while (even != null && even.next != null) {
            // 奇 -> 奇下下
            odd.next = odd.next.next;
            // 奇移动一步
            odd = odd.next;
            // 偶 -> 偶下下
            even.next = even.next.next;
            // 偶移动
            even = even.next;
        }
        // 奇 -> 偶头
        odd.next = evenHead;
        // 返回head
        return head;
    }
}

92. 反转链表 II

https://leetcode-cn.com/problems/reverse-linked-list-ii/

/**
 * 定位到要反转部分的头节点 2,head = 2;前驱结点 1,pre = 1;
 * 当前节点的下一个节点3调整为前驱节点的下一个节点 1->3->2->4->5,
 * 当前结点仍为2, 前驱结点依然是1,重复上一步操作。。。
 * 1->4->3->2->5.
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy, cur, next;
        for (int i = 1; i < m; i++) 
            pre = pre.next;
        cur  = pre.next;
        for (int i = m; i < n; i++) {
            next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next = next;
        }
        return dummy.next;
    }
}
// 投机取巧 缓存
class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode p = head;
        int idx = 1;
        Stack<Integer> stack = new Stack<>();
        while (p != null) {
            if (idx >= m && idx <= n)
                stack.push(p.val);
            idx++;
            p = p.next;
        }
        idx = 1;
        p = head;
        while (p != null) {
            if (idx >= m && idx <= n)
                p.val = stack.pop();
            idx++;
            p = p.next;
        }
        return head;
    }
}

15. K 个一组翻转链表(字节爱考)

https://leetcode-cn.com/problems/reverse-nodes-in-k-group/

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode(0), pre = dummy, curr = head, next;
        // 临时赋值
        dummy.next = head;
        // 算长度
        int len = 0;
        while (head != null) {
            len++;
            head = head.next;
        }
        // 从头
        head = dummy.next;
        // 两重for遍历
        for (int i = 0; i < len / k; i++) {
            for (int j = 0; j < k - 1; j++) {
                // 谜一般的操作
                // 临时next
                next = curr.next;
                // 我指向你
                curr.next = next.next;
                // 你指向他
                next.next = pre.next;
                // 他指向临时
                pre.next = next;
            }
            pre = curr; // 移动
            curr = pre.next; // 移动
        }
        return dummy.next;
    }
}

16. 奇数位升序偶数位降序的链表

public static ListNode oddEvenLinkedList(ListNode head) {
    // 将偶数链表拆分出来
    ListNode evenHead = getEvenList(head);
    // 逆序偶数链表
    ListNode reEvenHead = reverseList(evenHead);
    // 归并奇偶链表
    ListNode mHead = mergeList(head, reEvenHead);
    return mHead;
} 

public static ListNode getEvenList(ListNode head) {
    ListNode odd = new ListNode(-1);
    ListNode even = new ListNode(-1);
    int cnt = 1;
    ListNode cur1 = odd, cur2 = even;
    while (head != null) {
        if (cnt % 2 != 0) {
            cur1.next = head;
            cur1 = cur1.next;
        } else {
            cur2.next = head;
            cur2 = cur2.next;
        }
        head = head.next;
        cnt++;
    }
    cur1.next = null;
    cur2.next = null;
    return even.next;
}

public static ListNode reverseList(ListNode head) {
    ListNode pre = null;
    ListNode cur = head;
    while (cur != null) {
        ListNode next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

public static ListNode mergeList(ListNode l1, ListNode l2){
    // 我用递归
    if (l1 == null) 
        return l2;
    if (l2 == null)
        return l1;
    if (l1.val < l2.val) {
        l1.next = mergeList(l1.next, l2);
        return l1;
    } else {
        l2.next = mergeList(l1, l2.next);
        return l2;
    }
}

17. 分隔链表

https://leetcode-cn.com/problems/partition-list/

class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode dummy1 = new ListNode(0);
        ListNode dummy2 = new ListNode(0);
        ListNode node1 = dummy1, node2 = dummy2;
        while (head != null){
            if (head.val < x){
                node1.next = head;
                // 你左脚走
                head = head.next;
                // 1也左脚走
                node1 = node1.next;
                // 直接割,我不需要
                node1.next = null;
            } else {
                node2.next = head;
                head = head.next;
                node2 = node2.next;
                node2.next = null;
            }
        }
        node1.next = dummy2.next;
        return dummy1.next;
    }
}

18 排序链表

https://leetcode-cn.com/problems/sort-list/

class Solution {
    public ListNode sortList(ListNode head) {
        return head == null ? null : mergeSort(head);
    }
    private ListNode mergeSort(ListNode head) {
        if (head.next == null)
            return head;
        ListNode p = head, q = head, pre = null;
        while (q != null && q.next != null){
            pre = p;
            p = p.next;
            q = q.next.next;
        }
        pre.next = null;
        ListNode l = mergeSort(head);
        ListNode r = mergeSort(p);
        return merge(l, r);
    }

    private ListNode merge(ListNode l1, ListNode l2){
        if (l1 == null)
            return l2;
        if (l2 == null)
            return l1;
        if (l1.val < l2.val){
            l1.next = merge(l1.next, l2);
            return l1;
        } else {
            l2.next = merge(l1, l2.next);
            return l2;
        }
    }
}

143. 重排链表

https://leetcode-cn.com/problems/reorder-list/

class Solution {
    public void reorderList(ListNode head) {
        LinkedList<ListNode> queue = new LinkedList<>();
        ListNode cur = head;
        while (cur != null) {
            queue.addLast(cur);
            cur = cur.next;
        }
        while (!queue.isEmpty()) {
            if (cur == null)
                cur = queue.pollFirst();
            else {
                cur.next = queue.pollFirst();
                cur = cur.next;
            }
            cur.next = queue.pollLast();
            cur = cur.next;
        }
        if (cur != null) 
            cur.next = null;
    }
}

19. 删除链表的倒数第N个节点

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode fast = head;
        while (n-- > 0) {
            fast = fast.next;
        }
        // 这里没懂, 得举例子就懂了
        if (fast == null) return head.next;
        ListNode slow = head;
        while (fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        // 这里也懂了...举个例子就行
        slow.next = slow.next.next;
        return head;
    }
}

本博客所有文章除特别声明外,大部分为学习心得,欢迎与博主联系讨论