[toc]

剑指 Offer 06. 从尾到头打印链表

剑指 Offer 06. 从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

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

限制:

0 <= 链表长度 <= 10000

利用栈

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) { val = x; }
  7. * }
  8. */
  9. class Solution {
  10. public int[] reversePrint(ListNode head) {
  11. Deque<Integer> stack = new LinkedList<>();
  12. while (head != null) {
  13. stack.offerFirst(head.val);
  14. head = head.next;
  15. }
  16. int size = stack.size();
  17. int[] res = new int[size];
  18. int index = 0;
  19. while (!stack.isEmpty()) {
  20. res[index++] = stack.pollFirst();
  21. }
  22. return res;
  23. }
  24. }

逆向输入数组

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) { val = x; }
  7. * }
  8. */
  9. class Solution {
  10. public int[] reversePrint(ListNode head) {
  11. ListNode curHead = head;
  12. int size = 0;
  13. while (curHead != null) {
  14. curHead = curHead.next;
  15. size++;
  16. }
  17. int[] res = new int[size];
  18. for (int i = size - 1; i >=0; i--) {
  19. res[i] = head.val;
  20. head = head.next;
  21. }
  22. return res;
  23. }
  24. }

剑指 Offer 18. 删除链表的节点

剑指 Offer 18. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

注意:此题对比原题有改动

  1. 示例 1:
  2. 输入: head = [4,5,1,9], val = 5
  3. 输出: [4,1,9]
  4. 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
  5. 示例 2:
  6. 输入: head = [4,5,1,9], val = 1
  7. 输出: [4,5,9]
  8. 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
  9. 说明:
  10. 题目保证链表中节点的值互不相同
  11. 若使用 C C++ 语言,你不需要 free delete 被删除的节点

解法

用前一个节点进行比较,可以直接删除节点。还可以用一个哑巴节点做头结点,这样就不用单独判断第一个节点了。

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) { val = x; }
  7. * }
  8. */
  9. class Solution {
  10. public ListNode deleteNode(ListNode head, int val) {
  11. ListNode pre = head;
  12. if (head.val == val) {
  13. pre = head.next;
  14. head.next = null;
  15. return pre;
  16. }
  17. while (pre.next != null) {
  18. if (pre.next.val == val) {
  19. pre.next = pre.next.next;
  20. return head;
  21. }
  22. pre = pre.next;
  23. }
  24. return null;
  25. }
  26. }

剑指 Offer 22. 链表中倒数第k个节点

剑指 Offer 22. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

快慢指针

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) { val = x; }
  7. * }
  8. */
  9. class Solution {
  10. public ListNode getKthFromEnd(ListNode head, int k) {
  11. ListNode fast = head, slow = head;
  12. while (k-- > 0) {
  13. fast = fast.next;
  14. }
  15. while (fast != null) {
  16. fast = fast.next;
  17. slow = slow.next;
  18. }
  19. return slow;
  20. }
  21. }

剑指 Offer 24. 反转链表

剑指 Offer 24. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

  1. 示例:
  2. 输入: 1->2->3->4->5->NULL
  3. 输出: 5->4->3->2->1->NULL
  4. 限制:
  5. 0 <= 节点个数 <= 5000

迭代解法

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) { val = x; }
  7. * }
  8. */
  9. class Solution {
  10. public ListNode reverseList(ListNode head) {
  11. if (head == null || head.next == null) {
  12. return head;
  13. }
  14. ListNode newHead = new ListNode(-1);
  15. while (head != null) {
  16. ListNode next = head.next;
  17. head.next = newHead.next;
  18. newHead.next = head;
  19. head = next;
  20. }
  21. return newHead.next;
  22. }
  23. }

递归解法

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) { val = x; }
  7. * }
  8. */
  9. class Solution {
  10. public ListNode reverseList(ListNode head) {
  11. if (head == null || head.next == null) {
  12. return head;
  13. }
  14. ListNode res = reverseList(head.next);
  15. head.next.next = head;
  16. head.next = null;
  17. return res;
  18. }
  19. }

剑指 Offer 35. 复杂链表的复制

剑指 Offer 35. 复杂链表的复制

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

示例 1:
剑指Offer-链表 - 图1

  1. 输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
  2. 输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:
剑指Offer-链表 - 图2

  1. 输入:head = [[1,1],[2,1]]
  2. 输出:[[1,1],[2,1]]

示例 3:
剑指Offer-链表 - 图3

  1. 输入:head = [[3,null],[3,0],[3,null]]
  2. 输出:[[3,null],[3,0],[3,null]]
  3. 示例 4
  4. 输入:head = []
  5. 输出:[]
  6. 解释:给定的链表为空(空指针),因此返回 null
  7. 提示:
  8. -10000 <= Node.val <= 10000
  9. Node.random 为空(null)或指向链表中的节点。
  10. 节点数目不超过 1000

解法

  1. /*
  2. // Definition for a Node.
  3. class Node {
  4. int val;
  5. Node next;
  6. Node random;
  7. public Node(int val) {
  8. this.val = val;
  9. this.next = null;
  10. this.random = null;
  11. }
  12. }
  13. */
  14. class Solution {
  15. public Node copyRandomList(Node head) {
  16. if (head == null) {
  17. return null;
  18. }
  19. Node ptr = head;
  20. // 将原链表每个节点旁边增加一个节点
  21. while (ptr != null) {
  22. Node newNode = new Node(ptr.val);
  23. newNode.next = ptr.next;
  24. ptr.next = newNode;
  25. ptr = newNode.next;
  26. }
  27. ptr = head;
  28. // 将复制链表的random指向对应的位置
  29. while (ptr != null) {
  30. ptr.next.random = (ptr.random != null) ? ptr.random.next : null;
  31. ptr = ptr.next.next;
  32. }
  33. // 将复制链表的next指向对应的位置
  34. Node ptrOld = head, ptrNew = head.next, newHead = head.next;
  35. while (ptrOld != null) {
  36. ptrOld.next = ptrOld.next.next;
  37. ptrNew.next = (ptrNew.next != null) ? ptrNew.next.next : null;
  38. ptrOld = ptrOld.next;
  39. ptrNew = ptrNew.next;
  40. }
  41. return newHead;
  42. }
  43. }

剑指 Offer 52. 两个链表的第一个公共节点

剑指 Offer 52. 两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共节点。

如下面的两个链表:
剑指Offer-链表 - 图4
在节点 c1 开始相交。

示例 1:

剑指Offer-链表 - 图5

  1. 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
  2. 输出:Reference of the node with value = 8
  3. 输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A [4,1,8,4,5],链表 B [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:
剑指Offer-链表 - 图6

  1. 输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
  2. 输出:null
  3. 输入解释:从各自的表头开始算起,链表 A [2,6,4],链表 B [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA skipB 可以是任意值。
  4. 解释:这两个链表不相交,因此返回 null
  5. 注意:
  6. 如果两个链表没有交点,返回 null.
  7. 在返回结果后,两个链表仍须保持原有的结构。
  8. 可假定整个链表结构中没有循环。
  9. 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

解法

A和B两个链表长度可能不同,但是A+B和B+A的长度是相同的,所以遍历A+B和遍历B+A一定是同时结束。 如果A,B相交的话A和B有一段尾巴是相同的,所以两个遍历的指针一定会同时到达交点 如果A,B不相交的话两个指针就会同时到达A+B(B+A)的尾节点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode l1 = headA, l2 = headB;
        while (l1 != l2) {
            l1 = (l1 == null) ? headB : l1.next;
            l2 = (l2 == null) ? headA : l2.next;
        }
        return l1;
    }
}

推荐阅读


剑指Offer-链表 - 图7