文章链接地址

技巧一:理解指针或引用的含义

技巧二:警惕指针丢失和内存泄漏

技巧三:利用哨兵简化实现难度

技巧四:重点留意边界条件处理

技巧五:举例画图,辅助思考

技巧六:多写多练,没有捷径

练习题目:

1、单链表反转链

单链表反转(简单)力扣链接
rev1ex1.jpg

  1. // 迭代方式
  2. class Solution {
  3. public ListNode reverseList(ListNode head) {
  4. // 当只有一个节点和空节点的时候,直接将头节点进行返回
  5. if(head == null || head.next == null) return head;
  6. // node表示的是当前指针所在位置
  7. ListNode current = head.next;
  8. // b表示前一个指针
  9. ListNode previous = head;
  10. // 将头指针的下一个节点指向空,因为不这样处理的话,反转后的链表会在最后两个节点中形成闭环。
  11. // 错误如下: Error - Found cycle in the ListNode
  12. head.next = null;
  13. // 下一个节点
  14. ListNode behind;
  15. while(current != null) {
  16. behind = current.next;
  17. // 交换节点
  18. current.next = previous;
  19. previous = current;
  20. current = behind;
  21. }
  22. return previous;
  23. }
  24. }
  25. // 递归方式
  26. /**
  27. 每次递归都会得到的数据有
  28. head表示的是当前的节点
  29. 可以有:head head.next
  30. 需要做的是将head.next.next = head;从最后面开始做起
  31. */
  32. class Solution {0
  33. public ListNode reverseList(ListNode head) {
  34. return recursiveListNode(head);
  35. }
  36. // 使用递归方式
  37. public ListNode recursiveListNode(ListNode head) {
  38. if(head == null || head.next == null) {
  39. //这里有两种情况。一种是只有一个节点,或者是已到达了组后一个节点
  40. return head;
  41. }
  42. // 这个是得到递归的最后一个节点。也就是反转后的头节点
  43. ListNode last = recursiveListNode(head.next);
  44. // head表示的是cur节点,找到他的前一个节点(反转后的前一个节点)
  45. ListNode cur = head;
  46. // 这里的pre节点表示的是反转后的链表的尾节点
  47. ListNode pre = last;
  48. while(pre.next != null) {
  49. pre = pre.next;
  50. }
  51. // pre反转后的尾节点, cur需要当前方法中加入的尾节点。进行cur连接到尾部
  52. pre.next = cur;
  53. cur.next = null;
  54. return last;
  55. }
  56. }

单链表反转(中等)力扣链接

  1. class Solution {
  2. public ListNode reverseBetween(ListNode head, int left, int right) {
  3. ListNode dummyNode = new ListNode(-1);
  4. dummyNode.next = head;
  5. ListNode pre = dummyNode;
  6. for (int i = 0; i < left - 1; i++) {
  7. pre = pre.next;
  8. }
  9. ListNode rightNode = pre;
  10. for (int i = 0; i < right - left + 1; i++) {
  11. rightNode = rightNode.next;
  12. }
  13. ListNode leftNode = pre.next;
  14. ListNode curr = rightNode.next;
  15. pre.next = null;
  16. rightNode.next = null;
  17. reverseLinkedList(leftNode);
  18. pre.next = rightNode;
  19. leftNode.next = curr;
  20. return dummyNode.next;
  21. }
  22. private void reverseLinkedList(ListNode head) {
  23. ListNode pre = null;
  24. ListNode cur = head;
  25. while (cur != null) {
  26. ListNode next = cur.next;
  27. cur.next = pre;
  28. pre = cur;
  29. cur = next;
  30. }
  31. }
  32. }

2、表中环的检测

环形链表II

  1. /**
  2. * Definition for singly-linked list.
  3. * class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) {
  7. * val = x;
  8. * next = null;
  9. * }
  10. * }
  11. */
  12. public class Solution {
  13. // ListNode表示的是链表为环的位置的节点
  14. public ListNode detectCycle(ListNode head) {
  15. // if(head == null || head.next == null) return null;
  16. // return getCycleNode(head);
  17. return fastAndSlow(head);
  18. }
  19. //
  20. public ListNode fastAndSlow(ListNode node) {
  21. ListNode slow = node;
  22. ListNode fast = node;
  23. // 这里说明的是快指针还能继续往下走,那么满指针也能往下走
  24. while(fast != null && fast.next != null) {
  25. slow = slow.next;
  26. fast = fast.next.next;
  27. if(fast==null) return null;
  28. if(fast == slow) { // 说明存在着环
  29. // 说明 n 表示的是头节点到环入口的位置, h是环有多少个节点,*表示任意个
  30. // slow走的步数为 n + x
  31. // fast走的步数为 *h + n+x
  32. // 存在着 2n + 2x = *h + n+x
  33. //===》 n+x = *h
  34. //===》 n = *h - x, ===> 这里有个意思就是节点到环入口的位置,快或者满指针加上就又有一个环也就是又到了入口的位置。
  35. ListNode pre = node;
  36. ListNode fast1 = fast;
  37. while(pre != fast1) {
  38. pre = pre.next;
  39. fast1 = fast1.next;
  40. }
  41. return pre;
  42. }
  43. }
  44. return null;
  45. }
  46. // 使用set保存每一个ListNode,存在的话就进行返回重复的
  47. static Set<ListNode> set = new HashSet<ListNode>();
  48. public ListNode getCycleNode(ListNode head) {
  49. while(head != null) {
  50. if(set.contains(head)) {
  51. return head;
  52. }
  53. set.add(head);
  54. head = head.next;
  55. }
  56. return null;
  57. }
  58. }

3、两个有序的链表合并

21. 合并两个有序链表 难度 简单

// 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode head = new ListNode(-1); // 
       if(list1 == null && list2 == null) return head.next;
       ListNode first = head;

       while(list1 != null || list2 != null) {
           if(list1 == null) {// 第一个链表为空,第二个表直接添加进去
                while(list2 != null) {
                    head.next = list2;
                    list2 = list2.next;
                    head = head.next;
                }
                return first.next;
           }

           if(list2 == null) {
               while(list1 != null) {
                   head.next = list1;
                   list1 = list1.next;
                   head = head.next;
               }
               return first.next;
           }

           if(list1.val < list2.val) {
               head.next = list1;
               list1 = list1.next;
               head = head.next;
           } else {
               head.next = list2;
               list2 = list2.next;
               head = head.next;
           }
       }
       return first.next;
    }
}


// 递归方式
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        return getHead(list1, list2);
    }

    // 递归找到最后一个节点,将最后两个节点进行比较s形
    public static ListNode getHead(ListNode l1, ListNode l2) {
        if(l1 == null) {
            return l2;
        }
        if(l2 == null) {
            return l1;
        }
        if(l1.val < l2.val) {
            l1.next = getHead(l1.next, l2);
            return l1;
        } else {
            l2.next = getHead(l1, l2.next);
            return l2;
        }
    }
}

1669. 合并两个链表

给你两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。
请你将 list1 中下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点的位置。
下图中蓝色边和节点展示了操作后的结果:
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-in-between-linked-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
    public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
        ListNode node = list1;

        ListNode first = null;
        for(int i = 0; i < a; i++) { //取到拼接的开始的节点
            first = list1;
            list1 = list1.next;
        }
        ListNode last = null;
        list1 = node;
        for(int j = 0; j< b+2; j++) {//取到拼接的结束的节点的后一个节点
            last = list1;
            list1 = list1.next;
        }

        first.next = list2;
        while(list2.next!=null) {
            list2 = list2.next;
        }

        list2.next = last;
        return node;
    }
}

23. 合并K个升序链表
下面是迭代合并,那如果是合并合并或者使用优先队列(PriorityQueue)呢?(优先队列(PriorityQueue)??什么是优先队列)

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        //ListNode node = new ListNode(Integer.MIN_VALUE);
        ListNode node = null;
        ListNode result = node;
        for(int i = 0; i<lists.length; i++) { // 迭代合并
            if(lists[i] == null) continue;
            node = getHead(node, lists[i]);
        }
        return node;
    }
    public static ListNode getHead(ListNode l1, ListNode l2) { // 合并两个链表
        ListNode head = new ListNode();
        ListNode head1 = head;
        while(l1!=null || l2!=null) {
            if(l1==null || l2==null) {
                head.next = (l1==null)?l2:l1;
                return head1.next; 
            }
            if(l1.val < l2.val) {
                head.next = l1;
                head = head.next;
                l1 = l1.next;
            } else {
                head.next = l2;
                head = head.next;
                l2 = l2.next;
            }
        }
        return null;
    }
}

4、删除链表倒数第 n 个结点

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

 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {

        // return iterationLink1(head, n);
        //  return iterationLink2(head, n);
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        int a = recursiveDelete(dummyNode, n+1);
        System.out.println(a);
        return dummyNode.next;
    }

    // 递归
    public static int recursiveDelete(ListNode node, int n) {
        if(node == null) {// 返回
            return 0;
        }       
        int count = recursiveDelete(node.next, n)+1;
        if(count == n) {
            // System.out.println("删除" + node.val + " " + count);
            if(node.next!=null) {
                node.next = node.next.next;
            }
        }
        return count;
    }

    // 迭代2  先走n步,然后在通过一个节点从头开始走,直到第一个节点走到末尾
    public static ListNode iterationLink2(ListNode head, int n) {
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        ListNode first = dummyNode;
        ListNode last = dummyNode;
        while((n--) >= 0) {  // 走n+1步,因为要找到删除节点的前一个节点  //  for(int i = 0; i <= n; i++) {
            first = first.next;
        }
        while(first!=null) {
            first = first.next;
            last = last.next;
        }

        last.next = last.next.next;
        return dummyNode.next;

    }

    // 迭代1
    public static ListNode iterationLink1(ListNode head, int n) {
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        ListNode ans = dummyNode;
        int count = 0;
        while(head!=null) {
            count++;
            head = head.next;
        }
        System.out.println(count);

        for(int i = 0; i < (count - n); i++) {
            dummyNode = dummyNode.next;
        }
        if(dummyNode.next != null) {
            dummyNode.next = dummyNode.next.next;
        }

        return ans.next;
    }
}

5、求链表的中间结点

876. 链表的中间结点

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

        while(fast!=null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}