1、链表的反转

使用双指针(准确的是三指针记录),分别记录当前,上一节点,下一节点,然后进行反转

  1. public ListNode reverseList(ListNode head) {
  2. ListNode p = head, q;
  3. head = null;
  4. while (p != null) {
  5. q = p.next;
  6. p.next = head;
  7. head = p;
  8. p = q;
  9. }
  10. return head;
  11. }

2、相交链表

力扣

image.png

  • 指针遍历完A再遍历B,到达node和B再遍历a一致都是

a + b - c 的 距离

  • 如果AB、重合时有两种情况:
    • A、B 有重合点
    • 无重合点 ,公式中对应C 为 0
  1. public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
  2. ListNode A = headA, B = headB;
  3. if (A == null || B==null) return null;
  4. while (A != B) {
  5. A = A != null ? A.next : headB;
  6. B = B != null ? B.next : headA;
  7. A = A.next != null ? A.next : headB; // 此类写法无法应对无交点的情况
  8. B = B.next != null ? B.next : headA;
  9. }
  10. return A;
  11. }

3、环形链表

力扣

思想与上类似,都是采用双指针,进行推到,如果双指针遍历的节点相同,则返回true 本题采用快门指针的思想,初始值都为头节点,慢指针走一步,快指针走两步,如果两个节点相同时则返回true,如果fast节点为空,则说明没有环,返回false

  1. public boolean hasCycle(ListNode head) {
  2. if (head==null) return false;
  3. ListNode slow = head, fast = slow;
  4. while (true) {
  5. if (fast==null || fast.next==null) return false;
  6. slow = slow.next;
  7. fast = fast.next.next;
  8. if (fast == slow) return true;
  9. }
  10. }
  11. // public boolean hasCycle(ListNode head) {
  12. // if (head==null) return false;
  13. // ListNode slow = head, fast = slow.next; //虽然这种赋值也可以,但是无法用公式推导
  14. // while (slow != fast) {
  15. // if (fast==null || fast.next==null) return false;
  16. // slow = slow.next;
  17. // fast = fast.next.next;
  18. // }
  19. // return true;
  20. // }

4、环形链表 | |

力扣

设fast走的路程 f ,slow的路程 s,头节点导环的起点为 a,环的路程 b

  • f = 2s
  • f = s + nb

所以 s = nb 令fast节点指向 head,当fast节点走到入口时 走的距离为 a ,slow 的距离 nb + a 所以两节点相遇

  1. public ListNode detectCycle(ListNode head) {
  2. ListNode fast = head, slow = head;
  3. while (true) {
  4. if (fast == null || fast.next == null) return null;
  5. fast = fast.next.next;
  6. slow = slow.next;
  7. if (fast == slow) break;
  8. }
  9. System.out.println(fast.val);
  10. System.out.println(slow.val);
  11. fast = head;
  12. while (slow != fast) {
  13. slow = slow.next;
  14. fast = fast.next;
  15. }
  16. return fast;
  17. }

4、合并有序链表

力扣

递归

当两个链表有一个为 null 时,就可以视为递归最后一层结束标志 在每一层都有

  • list1.next = mergeTwoLists(list1.next, list2);

或者

  • list2.next = mergeTwoLists(list1, list2.next);

对两个链表进行连接

  1. public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
  2. if (list1 == null) {
  3. return list2;
  4. }else if (list2 == null) {
  5. return list1;
  6. } else if (list1.val < list2.val) {
  7. list1.next = mergeTwoLists(list1.next, list2);
  8. return list1;
  9. } else {
  10. list2.next = mergeTwoLists(list1, list2.next);
  11. return list2;
  12. }
  13. }

迭代

分别定义两个辅助节点,

  • preHead : 虚拟头节点记录头节点的位置
  • pre : 记录移动轨迹的辅助节点
  1. public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
  2. ListNode preHead = new ListNode(-1);
  3. ListNode pre = preHead;
  4. while (list1 != null && list2 != null) {
  5. if (list1.val <= list2.val) {
  6. pre.next = list1;
  7. list1 = list1.next;
  8. } else {
  9. pre.next = list2;
  10. list2 = list2.next;
  11. }
  12. System.out.println(pre.val);
  13. pre = pre.next;
  14. }
  15. pre.next = list1 == null ? list2 : list1;
  16. return preHead.next;
  17. }

两数相加

相加过程中需要确认当前位置的数字和下一位需要进位的值,然后连接前后节点

迭代模拟

  1. public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
  2. int nextVar = 0;
  3. ListNode preHead = new ListNode(-1); //头节点
  4. ListNode head = preHead;
  5. while (!(l1 == null && l2 == null)) {
  6. int l1var = 0, l2var =0;
  7. if (l1 != null) {
  8. l1var = l1.val;
  9. l1 = l1.next
  10. }
  11. if (l2 != null) {
  12. l2var = l2.val;
  13. l2 = l2.next;
  14. }
  15. ListNode node = new ListNode((l1var + l2var + nextVar) % 10);
  16. nextVar = (l1var + l2var+nextVar) / 10;
  17. //连接前后节点
  18. head.next = node;
  19. head = node;
  20. }
  21. // 如果最后一位需要进位
  22. if (nextVar == 1) {
  23. head.next = new ListNode(nextVar);
  24. }
  25. return preHead.next;
  26. }

递归

  1. public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
  2. return add(l1, l2, 0);
  3. }
  4. /**
  5. 返回两个链表相加的头部
  6. */
  7. public ListNode add(ListNode l1, ListNode l2, int bit) {
  8. if (l1 == null && l2 == null && bit == 0) {
  9. return null;
  10. }
  11. int val = bit;
  12. if (l1 != null) {
  13. val += l1.val;
  14. l1 = l1.next;
  15. }
  16. if (l2 != null) {
  17. val += l2.val;
  18. l2 = l2.next;
  19. }
  20. ListNode node = new ListNode(val % 10);
  21. node.next = add(l1, l2, val / 10);
  22. return node;
  23. }

两两交换链表中的节点

交换相邻两点,需要注意链表的完整

递归

  1. public ListNode swapPairs(ListNode head) {
  2. if(head == null || head.next == null){ // 递归返回条件
  3. return head;
  4. }
  5. ListNode next = head.next;
  6. //当前节点的下一个值指向下一层递归,注意参数是next.next
  7. head.next = swapPairs(next.next);
  8. next.next = head; //反转
  9. return next;
  10. }

迭代+多指针辅助

  1. public ListNode swapPairs(ListNode head) {
  2. // 如果链表为空或只有一个节点,直接返回
  3. if (head == null || head.next == null) return head;
  4. // 新建一个虚拟头结点
  5. ListNode preHead = new ListNode(-1);
  6. // p指向虚拟头结点
  7. ListNode p = preHead;
  8. // 定义快慢指针,初始值均为头结点
  9. ListNode fast = head, slow = head;
  10. // 只要快指针和快指针的下一个节点都不为空,就继续遍历
  11. while (fast != null && fast.next != null) {
  12. // 快指针向后移动一位
  13. fast = fast.next;
  14. // 定义一个临时节点q,指向快指针的下一个节点
  15. ListNode q = fast.next;
  16. // 将快指针的下一个节点指向慢指针
  17. fast.next = slow;
  18. // 将慢指针的下一个节点指向临时节点q
  19. slow.next = q;
  20. // 将p的下一个节点指向快指针
  21. p.next = fast;
  22. // p指向慢指针
  23. p = slow;
  24. // 慢指针和快指针均指向临时节点q
  25. slow = q;
  26. fast = q;
  27. }
  28. // 返回虚拟头结点的下一个节点,即新链表的头结点
  29. return preHead.next;
  30. }

复制带随机指针的链表

力扣

由于是深度拷贝,因此在拷贝random指针时,需要确定当前random是否被定义,因此无论采用什么方式,基本上都是将next 与 random 分开拷贝。

  1. public Node copyRandomList(Node head) {
  2. if (head==null) return head;
  3. Node cur = head;
  4. HashMap<Node, Node> nodeMap = new HashMap<>();
  5. while (cur != null) {
  6. nodeMap.put(cur, new Node(cur.val)); //key 为老节点,value为新建节点,此时新建节点的next和random都未定义
  7. cur = cur.next;
  8. }
  9. cur = head;
  10. while (cur != null) {
  11. nodeMap.get(cur).next = nodeMap.get(cur.next); // map中V.next指向 K.next
  12. nodeMap.get(cur).random = nodeMap.get(cur.random); //同上
  13. cur = cur.next;
  14. }
  15. return nodeMap.get(head);
  16. }