1. 数组

  • 数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

    1.1 两数之和(1)

  • 解法一:暴力穷举

    1. public static void main(String[][] args) {
    2. new Main().twoSum(new int[]{2, 7, 11, 15}, 9)
    3. }
    4. public int[] twoSum(int[] nums, int target) {
    5. int n = nums.length;
    6. for (int i = 0; i < n; ++i) {
    7. for (int j = i + 1; j < n; ++j) {
    8. if (nums[i] + nums[j] == target) {
    9. return new int[]{i, j};
    10. }
    11. }
    12. }
    13. return new int[0];
    14. }
  • 解法二:a + b = target那么b = target - a; 可以用O(n)的空间Set来存储原始数组,然后计算target-a是否在Set中。

    1. public static void main(String[][] args) {
    2. new Main().twoSum(new int[]{2, 7, 11, 15}, 9)
    3. }
    4. public int[] twoSum(int[] nums, int target) {
    5. Map<Integer, Integer> numIndexMap = new HashMap<>(nums.length + (nums.length >> 1));
    6. for (int i = 0; i < nums.length; ++i) {
    7. if (numIndexMap.containsKey(target - nums[i])) {
    8. return new int[]{numIndexMap.get(target - nums[i]), i};
    9. }
    10. numIndexMap.put(nums[i], i);
    11. }
    12. return new int[0];
    13. }

    1.2 三数之和(15)

  • 解法一:固定target用左右指针法 ```java public static void main (String[][] args) {

}

  1. public List<List<Integer>> threeSum(int[] nums) {
  2. Arrays.sort(nums);
  3. List<List<Integer>> resultList = new ArrayList<>(nums.length >> 1);
  4. // 枚举 a
  5. for (int first = 0; first < nums.length; ++first) {
  6. // 需要和上一次枚举的数不相同
  7. if (first > 0 && nums[first] == nums[first - 1]) {
  8. continue;
  9. }
  10. // c 对应的指针初始指向数组的最右端
  11. int third = nums.length - 1;
  12. // 枚举 b
  13. for (int second = first + 1; second < nums.length; ++second) {
  14. // 跳过第一个 并且 需要和上一次枚举的数不相同
  15. if (second != first + 1 && nums[second] == nums[second - 1]) {
  16. continue;
  17. }
  18. // 需要保证 b 的指针在 c 的指针的左侧
  19. while (second < third && nums[second] + nums[third] > -nums[first]) {
  20. --third;
  21. }
  22. // 如果指针重合,随着 b 后续的增加
  23. // 就不会有满足 first+b+c=0 并且 b<c 的 c 了,可以退出循环
  24. if (second == third) {
  25. break;
  26. }
  27. if (nums[second] + nums[third] == -nums[first]) {
  28. resultList.add(Arrays.asList(nums[first], nums[second], nums[third]));
  29. }
  30. }
  31. }
  32. return resultList;
  33. }
  1. - 解法二:
  2. ```java
  3. public static void main (String[][] args) {
  4. }

1.3 盛最多水的容器(11)

  • 解法一: ```java public static void main (String[][] args) {
  1. }
  2. public int maxArea(int[] height) {
  3. int l = 0, r = height.length - 1;
  4. int result = 0;
  5. while (l < r) {
  6. result = Math.max(result, Math.min(height[l], height[r]) * (r - l));
  7. if (height[l] <= height[r]) {
  8. ++l;
  9. } else {
  10. --r;
  11. }
  12. }
  13. return result;
  14. }
  1. - 解法二:
  2. ```java
  3. public static void main (String[][] args) {
  4. }

1.4 删除有序数组中的重复项(26)

  • 解法一:

    1. public int removeDuplicates(int[] nums) {
    2. int slow = 1, fast = 1;
    3. while (fast < nums.length) {
    4. if (nums[fast] != nums[fast - 1]) {
    5. nums[slow] = nums[fast];
    6. ++slow;
    7. }
    8. ++fast;
    9. }
    10. return nums.length == 0 ? 0 : slow;
    11. }

    1.5 移动零(283)

  • 解法一:

    1. public void moveZeroes(int[] nums) {
    2. int slow = 0;
    3. int fast = 0;
    4. while (fast < nums.length) {
    5. if (nums[fast] != 0) {
    6. nums[slow] = nums[fast];
    7. slow++;
    8. }
    9. ++fast;
    10. }
    11. for (int i = slow; i < nums.length; i++) {
    12. nums[i] = 0;
    13. }
    14. }

    1.6 加一(66)

  • 解法一:

    1. public int[] plusOne(int[] digits) {
    2. for (int i = digits.length - 1; i >= 0; i--) {
    3. if (digits[i] == 9) {
    4. digits[i] = 0;
    5. } else {
    6. digits[i] += 1;
    7. return digits;
    8. }
    9. }
    10. int[] ans = new int[digits.length + 1];
    11. ans[0] = 1;
    12. return ans;
    13. }

    1.7 合并两个有序数组(88)

  • 解法一:把数组2幅值到数组1排序即可 ```java public void merge(int[] nums1, int m, int[] nums2, int n) {

    1. for (int i = 0; i < n; ++i) {
    2. nums1[m + i] = nums2[i];
    3. }
    4. Arrays.sort(nums1);

    }

  1. - 解法二:双指针
  2. ```java
  3. public void merge(int[] nums1, int m, int[] nums2, int n) {
  4. int ptrOne = 0, ptrTwo = 0;
  5. int[] sorted = new int[m + n];
  6. int cur;
  7. while (ptrOne < m || ptrTwo < n) {
  8. if (ptrOne == m) {
  9. cur = nums2[ptrTwo++];
  10. } else if (ptrTwo == n) {
  11. cur = nums1[ptrOne++];
  12. } else if (nums1[ptrOne] < nums2[ptrTwo]) {
  13. cur = nums1[ptrOne++];
  14. } else {
  15. cur = nums2[ptrTwo++];
  16. }
  17. sorted[ptrOne + ptrTwo - 1] = cur;
  18. }
  19. if (m + n >= 0) {
  20. System.arraycopy(sorted, 0, nums1, 0, m + n);
  21. }
  22. }

1.8 轮转数组(189)

输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4]

  • 解法一:copy数组

    1. public void rotate(int[] nums, int k) {
    2. int n = nums.length;
    3. int[] newArr = new int[n];
    4. for (int i = 0; i < n; ++i) {
    5. newArr[(i + k) % n] = nums[i];
    6. }
    7. System.arraycopy(newArr, 0, nums, 0, n);
    8. }
  • 解法二:翻转

    1. public void rotate(int[] nums, int k) {
    2. k %= nums.length;
    3. reverse(nums, 0, nums.length - 1);
    4. reverse(nums, 0, k - 1);
    5. reverse(nums, k, nums.length - 1);
    6. }
    7. public void reverse(int[] nums, int start, int end) {
    8. while (start < end) {
    9. int temp = nums[start];
    10. nums[start] = nums[end];
    11. nums[end] = temp;
    12. start ++;
    13. end--;
    14. }
    15. }

    2. 链表

    2.1 单链表反转(206)

  • 解法一:双指针

    1. public ListNode reverseList(ListNode head) {
    2. if (null == head || null == head.next) {
    3. return head;
    4. }
    5. ListNode curr = head;
    6. ListNode prev = null;
    7. while (null != curr) {
    8. ListNode next = curr.next;
    9. curr.next = prev;
    10. prev = curr;
    11. curr = next;
    12. }
    13. return prev;
    14. }
  • 解法二:栈 ```java public ListNode reverseList(ListNode head) {

    1. if (null == head || null == head.next) {
    2. return head;
    3. }
    4. Stack<ListNode> stack = new Stack<>();
    5. ListNode temp = head;
    6. while(temp != null) {
    7. stack.push(temp);
    8. temp = temp.next;
    9. }
    10. temp = stack.pop();
    11. ListNode dummyHead = temp;
    12. while (!stack.isEmpty()) {
    13. temp.next = stack.pop();
    14. temp = temp.next;
    15. }
    16. temp.next = null;
    17. return dummyHead;

    }

  1. <a name="d0hI6"></a>
  2. ### 2.2 链表中有环(141, 142)
  3. - 解法一:快慢指针
  4. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/303188/1643178583230-69dc4667-cffc-421e-b6aa-a77ccddbf1d8.png#clientId=ua27debab-ea99-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=168&id=u0392f6da&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1125&originWidth=2000&originalType=binary&ratio=1&rotation=0&showTitle=false&size=153885&status=done&style=none&taskId=ucf41fe32-a78a-4683-94f8-702789a9c62&title=&width=299)
  5. ```java
  6. // 是否存在
  7. public boolean hasCycle(ListNode head) {
  8. if ( null == head|| null == head.next) {
  9. return false;
  10. }
  11. ListNode slow = head;
  12. ListNode fast = head.next;
  13. while(slow != fast) {
  14. if (null == fast || null == fast.next ) {
  15. return false;
  16. }
  17. slow = slow.next;
  18. fast = fast.next.next;
  19. }
  20. return true;
  21. }
  22. // 返回找到的节点
  23. public ListNode detectCycle(ListNode head) {
  24. if ( null == head|| null == head.next) {
  25. return null;
  26. }
  27. ListNode slow = head;
  28. ListNode fast = head;
  29. while(null != fast) {
  30. if (null == fast.next) {
  31. return null;
  32. }
  33. slow = slow.next;
  34. fast = fast.next.next;
  35. if (slow == fast) {
  36. ListNode temp = head;
  37. while(temp != slow) {
  38. temp = temp.next;
  39. slow = slow.next;
  40. }
  41. return temp;
  42. }
  43. }
  44. return null;
  45. }
  • 解法二:hash表

    1. // 是否找到
    2. public boolean hasCycle(ListNode head) {
    3. if (null == head || null == head.next) {
    4. return false;
    5. }
    6. Set<ListNode> nodeSet = new HashSet<>();
    7. ListNode temp = head;
    8. while (null != temp) {
    9. if (nodeSet.contains(temp)) {
    10. return true;
    11. }
    12. nodeSet.add(temp);
    13. temp = temp.next;
    14. }
    15. return false;
    16. }
    17. // 返回入环节点
    18. public ListNode detectCycle(ListNode head) {
    19. if (null == head || null == head.next) {
    20. return null;
    21. }
    22. Set<ListNode> nodeSet = new HashSet<>();
    23. ListNode temp = head;
    24. while (null != temp) {
    25. if (nodeSet.contains(temp)) {
    26. return temp;
    27. }
    28. nodeSet.add(temp);
    29. temp = temp.next;
    30. }
    31. return null;
    32. }

    2.3 合并两个有序链表(21)

  • 解法一:递归 ```java public ListNode mergeTwoLists(ListNode list1, ListNode list2) {

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

    }

  1. - 解法二:循环
  2. ```java
  3. public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
  4. ListNode dummyHead = new ListNode();
  5. ListNode prev = dummyHead;
  6. while (list1 != null && list2 != null) {
  7. if (list1.val <= list2.val) {
  8. prev.next = list1;
  9. list1 = list1.next;
  10. } else {
  11. prev.next = list2;
  12. list2 = list2.next;
  13. }
  14. prev = prev.next;
  15. }
  16. // 合并后 list1 和 list2 最多只有一个还未被合并完,直接将链表末尾指向未合并完的链表
  17. prev.next = list1 == null ? list2 : list1;
  18. return dummyHead.next;
  19. }

2.4 删除链表倒数第 n 个结点(19)

  • 解法一:双路指针,间隔n

    1. public ListNode removeNthFromEnd(ListNode head, int n) {
    2. ListNode dummy = new ListNode(0, head);
    3. ListNode front = head;
    4. // 抽象头节点
    5. ListNode temp = dummy;
    6. for (int i = 0; i < n; ++i) {
    7. front = front.next;
    8. }
    9. while (front != null) {
    10. front = front.next;
    11. temp = temp.next;
    12. }
    13. temp.next = temp.next.next;
    14. return dummy.next;
    15. }
  • 解法二:入栈后 pop出 prev指针

    1. public ListNode removeNthFromEnd(ListNode head, int n) {
    2. ListNode dummyHead = new ListNode(0, head);
    3. ListNode temp = dummyHead;
    4. Stack<ListNode> stack = new Stack<>();
    5. while (null != temp) {
    6. stack.push(temp);
    7. temp = temp.next;
    8. }
    9. ListNode prev = stack.pop();
    10. for (int i = 0; i < n; i++) {
    11. prev = stack.pop();
    12. }
    13. if (prev.next != null) {
    14. prev.next = prev.next.next;
    15. }
    16. return dummyHead.next;
    17. }
  • 解法三:计算长度

    1. public ListNode removeNthFromEnd(ListNode head, int n) {
    2. int count = 0;
    3. ListNode temp = head;
    4. while(null != temp) {
    5. ++count;
    6. temp = temp.next;
    7. }
    8. if ( count < n ){
    9. return head;
    10. }
    11. ListNode dummyHead = new ListNode();
    12. dummyHead.next = head;
    13. temp = dummyHead;
    14. for (int i = 0; i < count - n; i++) {
    15. temp = temp.next;
    16. }
    17. if (temp.next != null) {
    18. temp.next = temp.next.next;
    19. }
    20. return dummyHead.next;
    21. }

    2.5 求链表的中间结点(876)

  • 解法一:空间换时间 用数组记录

    1. public ListNode middleNode(ListNode head) {
    2. ListNode[] nodeArr = new ListNode[100];
    3. int count = 0;
    4. while (head != null) {
    5. nodeArr[count] = head;
    6. ++count;
    7. head = head.next;
    8. }
    9. return nodeArr[count / 2];
    10. }
  • 解法二:快慢指针

    1. public ListNode middleNode(ListNode head) {
    2. ListNode first = head;
    3. ListNode second = head;
    4. while(second != null && second.next != null) {
    5. first = first.next;
    6. second = second.next.next;
    7. }
    8. return first;
    9. }

    2.6 两两交换链表中的节点(24)

  • 解法一:

    1. public ListNode swapPairs(ListNode head) {
    2. // 持有head
    3. ListNode dummyHead = new ListNode();
    4. dummyHead.next = head;
    5. ListNode first = dummyHead;
    6. while (null != first.next && null != first.next.next) {
    7. ListNode second = first.next;
    8. ListNode third = second.next;
    9. // 交换
    10. first.next = third;
    11. second.next = third.next;
    12. third.next = second;
    13. // 移动到下一轮
    14. first = second;
    15. }
    16. return dummyHead.next;
    17. }
  • 解法二:递归

    1. public ListNode swapPairs(ListNode head) {
    2. if (head == null || head.next == null) {
    3. return head;
    4. }
    5. ListNode newHead = head.next;
    6. // 把他看成自后节点的引用
    7. head.next = swapPairs(newHead.next);
    8. newHead.next = head;
    9. return newHead;
    10. }

    2.7 K个一组翻转链表(25)

  • 解法一:遍历

    1. public ListNode reverseKGroup(ListNode head, int k) {
    2. if (null == head || null == head.next) {
    3. return head;
    4. }
    5. ListNode curr = head;
    6. // 计算长度 O(n)
    7. int count = 0;
    8. while (null != curr) {
    9. curr = curr.next;
    10. ++count;
    11. }
    12. curr = head;
    13. ListNode start = null, end = curr, res = null;
    14. ListNode pre;
    15. for (int i = 0; i < count / k; i++) {
    16. start = curr;
    17. //局部反转
    18. pre = null;
    19. int index = k;
    20. while (index != 0) {
    21. ListNode temp = curr.next;
    22. curr.next = pre;
    23. pre = curr;
    24. curr = temp;
    25. index--;
    26. }
    27. //记录要返回的node
    28. if (i == 0) {
    29. res = pre;
    30. }
    31. //拼接反转的部分
    32. if (i != 0) {
    33. end.next = pre;
    34. end = start;
    35. }
    36. }
    37. if (curr != null)
    38. end.next = curr;
    39. return res;
    40. }

    2.8 LRU 缓存(146)

  • 解法一: ```java public static void main (String[][] args) {

}

  1. - 解法二:
  2. ```java
  3. public static void main (String[][] args) {
  4. }