常见链表算法

108 阅读3分钟

链表操作中的双指针技巧运用

该模块主要介绍链表操作中使用到的双指针技巧。其中包括虚拟头节点的运用,例如合并两个/多个有序链表,分割链表,删除链表的倒数第N个节点。也包含了快慢指针技巧的运用,例如获取链表的中间节点,判断链表是否有环。

合并两个有序单链表(LeetCode21)

public class MergeTwoList {
    public static Node mergeTwoList(Node list1, Node list2) {
        Node dummy = new Node(-1);
        Node p1 = dummy;
        while (list1 != null && list2 != null) {
            if (list1.value < list2.value) {
                p1.next = list1;
                list1 = list1.next;
            } else {
                p1.next = list2;
                list2 = list2.next;
            }
            p1 = p1.next;
        }
        if (list1 != null) {
            p1.next = list1;
        }
        if (list2 != null) {
            p1.next = list2;
        }
        return dummy.next;
    }
}

分割链表(LeetCode86)

public class PartitionList {
    public static Node partition(Node head, int x) {
        Node dummy1 = new Node(-1);
        Node dummy2 = new Node(-1);
        Node p1 = dummy1;
        Node p2 = dummy2;
        Node p = head;
        while (p != null) {
            if (p.value < x) {
                p1.next = p;
                p1 = p1.next;
            } else {
                p2.next = p;
                p2 = p2.next;
            }
            // 注意需要将原链表进行断链
            Node temp = p.next;
            p.next = null;
            p = temp;
        }
        p1.next = dummy2.next;
        return dummy1.next;
    }
}

合并K个有序链表(LeetCode23)

public class MergeKList {
    public static Node mergeKLists(Node[] lists) {
        if (lists.length == 0) {
            return null;
        }
        Node dummy = new Node(-1);
        Node p = dummy;
        PriorityQueue<Node> priorityQueue = new PriorityQueue<>(lists.length, (a, b) -> (a.value - b.value));
        for (Node head : lists) {
            if (head != null) {
                priorityQueue.add(head);
            }
        }
        while (!priorityQueue.isEmpty()) {
            Node node = priorityQueue.poll();
            p.next = node;
            if (node.next != null) {
                priorityQueue.add(node.next);
            }
            p = p.next;
        }
        return dummy.next;
    }
}

删除链表的倒数第N个节点(LeetCode19)

public class RemoveNthFromEnd {
    public static Node removeNthFromEnd(Node head, int n) {
        Node dummy = new Node(-1);
        dummy.next = head;
        Node last = findNthFromEnd(head, n + 1);
        last.next = last.next.next;
        return dummy.next;
    }

    public static Node findNthFromEnd(Node head, int n) {
        Node p1 = head;
        for (int i = 0; i < n; i++) {
            p1 = p1.next;
        }
        Node p2 = head;
        while (p1 != null) {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p2;
    }
}

链表的中间节点(LeetCode876)

public class MiddleNode {
    public static Node middleNode(Node head) {
        Node slow = head;
        Node fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

判断链表是否有环并返回环的入口(LeetCode142)

public class CycleList {
    public static Node findCycleStart(Node head) {
        Node slow = head;
        Node fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.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;
    }
}

相交链表(LeetCode160)

public class IntersectionNode {
    public static Node getIntersectionNode(Node head1, Node head2) {
        Node p1 = head1;
        Node p2 = head2;
        while (p1 != p2) {
            if (p1 == null) {
                p1 = head2;
            } else {
                p1 = p1.next;
            }

            if (p2 == null) {
                p2 = head1;
            } else {
                p2 = p2.next;
            }
        }
        return p1;
    }
}

判断链表是否为回文链表

public class PalindromeList {
    public boolean isPalindrome(Node head) {
        if (head == null || head.next == null) {
            return false;
        }
        Node slow = head;
        Node fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        if (fast != null) {
            slow = slow.next;
        }
        Node left = head;
        Node right = reverse(slow);
        boolean result = true;
        while (right != null) {
            if (left.value != right.value) {
                result = false;
                break;
            }
            left = left.next;
            right = right.next;
        }
        return result;
    }

    public Node reverse(Node head) {
        Node pre = null;
        Node next = null;
        Node cur = head;
        while (cur != null) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

链表反转专题

使用迭代的方式反转链表

public class ReverseNode {
    public static Node reverseNodeIterator(Node head) {
        Node pre = null;
        Node cur;
        Node next;
        cur = head;
        while (cur != null) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

使用递归的方式反转链表

public class ReverseNode {
    public static Node reverseNodeRecursion(Node head) {
        if (head == null || head.next == null) {
            return head;
        }
        Node last = reverseNodeRecursion(head);
        head.next.next = head;
        head.next = null;
        return last;
    }
}

使用递归的方式反转前N个节点

使用递归的方式反转前N个节点,需要注意反转后的原始头节点不再指向NULL了,而应该指向一个后驱节点。

public class ReverseNode {
    private static Node successor = null;
    public static Node reverseNodeRecursionN(Node head, int n) {
        if (n == 1) {
            successor = head.next;
            return head;
        }
        Node last = reverseNodeRecursionN(head, n - 1);
        head.next.next = head;
        head.next = successor;
        return last;
}

使用递归的方式反转m~n之间的节点

public class ReverseNode {
    public static Node reverseNodeRecursionN(Node head, int n) {
        Node successor = null;
        if (n == 1) {
            successor = head.next;
            return head;
        }
        Node last = reverseNodeRecursionN(head, n - 1);
        head.next.next = head;
        head.next = successor;
        return last;
    }

    public static Node reverseNodeRecursionBetween(Node head, int m, int n) {
        if (m == 1) {
            return reverseNodeRecursionN(head, n);
        }
        head.next = reverseNodeRecursionBetween(head, m - 1, n - 1);
        return head;
    }
}

K个一组对链表进行逆序

public class ReverseNodePerK {
    public Node reverseNodeFromAToB(Node a, Node b) {
        Node pre = null;
        Node cur = a;
        Node next = a;
        while (cur != b) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = cur.next;
        }
        return pre;
    }

    public Node reverseNodePerK(Node head, int k) {
        if (head == null) {
            return head;
        }
        Node a = head;
        Node b = head;
        for (int i = 0; i < k; i++) {
            if (b == null) {
                return head;
            }
            b = b.next;
        }
        Node newHead = reverseNodeFromAToB(a, b);
        a.next = reverseNodePerK(b, k);
        return newHead;
    }
}