数组
- 数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
1.1 【简单】两数之和(1)

/*** 利用map* a + b = target那么b = target - a; 可以用O(n)的空间Set来存储原始数组,* 然后计算target-a是否在Set中。*/public int[] twoSum(int[] nums, int target) {Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums.length; i++) {if (map.get(nums[i]) != null) {return new int[]{map.get(nums[i]), i};}map.put(target - nums[i], i);}return new int[2];}
1.2 【中等】三数之和(15)

/*** 排序后枚举即可*/public List<List<Integer>> threeSum(int[] nums) {int n = nums.length;Arrays.sort(nums);List<List<Integer>> ans = new ArrayList<List<Integer>>();// 枚举 afor (int i = 0; i < n; ++i) {int target = -nums[i];if (i > 0 && nums[i] == nums[i - 1]) {continue;}int third = n - 1;for (int second = i+1; second < n; ++second) {if (second > i+1 && nums[second] == nums[second - 1]) {continue;}while (second < third && nums[second] + nums[third] > target) {third--;}if (second == third) {break;}if (nums[second] + nums[third] == target) {List<Integer> list = new ArrayList<>();list.add(nums[i]);list.add(nums[second]);list.add(nums[third]);ans.add(list);}}}return ans;}
1.3 【简单】删除有序数组中的重复项(26)

/*** 双指针前进,第一个指针走完返回第二个指针索引位置即可*/public int removeDuplicates(int[] nums) {if (nums.length == 0) {return 0;}int temp = nums[0];int j = 1;for (int i = 1; i < nums.length; i++) {if (nums[i] != temp) {temp = nums[i];nums[j] = nums[i];j++;}}return j;}
1.4 【简单】移动零(283)

/*** 暴力迁移*/public void moveZeroes(int[] nums) {int num = 0;for (int i = 0; i < nums.length; i++) {while (nums[i] == 0) {if (nums.length - i == num) {return;}int temp = nums[i];for (int j = i + 1; j < nums.length; j++) {nums[j - 1] = nums[j];}nums[nums.length - 1] = temp;num++;}}}
1.5 【简单】加一(66)

/*** 暴力解法*/public int[] plusOne(int[] digits) {int flag = 0;for (int i = digits.length - 1; i >= 0; i--) {if (flag == 0 && i != digits.length - 1) {break;}if (digits[i] == 9) {flag = 1;digits[i] = 0;} else {digits[i] = digits[i] + 1;flag = 0;}}if (flag == 1) {int[] result = new int[digits.length + 1];result[0] = 1;for (int i = 0; i < digits.length; i++) {result[i + 1] = digits[i];}return result;} else {return digits;}}
1.6 【简单】合并两个有序数组(88)

/*** 暴力解法*/public void merge(int[] nums1, int m, int[] nums2, int n) {if (n == 0) {return;}int totalNum = m + n;int j = 0;for (int i = 0; i < totalNum && j < n; i++) {if (nums1[i] > nums2[j]) {// 往后移动for (int k = totalNum - 1; k > i; k--) {nums1[k] = nums1[k - 1];}nums1[i] = nums2[j];j++;}}if (j < n) {for (int i = totalNum - n + j; i < totalNum; i++) {nums1[i] = nums2[j];j++;}}}
1.7 【中等】轮转数组(189)

/*** 三次置换*/public void rotate(int[] nums, int k) {k %= nums.length;reverse(nums, 0, nums.length - 1);reverse(nums, 0, k - 1);reverse(nums, k, nums.length - 1);}public void reverse(int[] nums, int start, int end) {while (start < end) {int temp = nums[start];nums[start] = nums[end];nums[end] = temp;start ++;end--;}}
1.8 【困难】素数伴侣(HJ28)

素数必定是偶数加奇数,所以可以分为两个list进行匹配,用到的方法是匈牙利算法,先到先得,能让则让。
举例说明:如图所示,首先A1和B2配对(先到先得),然后轮到A2,A2也可以和B2配对,这时候B2发现A1还可以和B4配对,所以放弃了A1,选择和A2组成伴侣(能让就让)。接着A3直接和B1配对(先到先得)。最后A4尝试与B4配对,但是这样A1就只能与B2配对,而A2就找不到伴侣了,一层层递归下来,发现不可行,所以A4不能与B4配对。 ```java import java.util.*;
public class Main{
public static void main(String[] args) {Scanner sc = new Scanner(System.in);while (sc.hasNext()) {//输入正偶数int n = sc.nextInt();//用于记录输入的n个整数int[] arr = new int[n];//用于存储所有的奇数ArrayList<Integer> odds = new ArrayList<>();//用于存储所有的偶数ArrayList<Integer> evens = new ArrayList<>();for (int i = 0; i < n; i++) {arr[i] = sc.nextInt();//将奇数添加到oddsif (arr[i] % 2 == 1) {odds.add(arr[i]);}//将偶数添加到evensif (arr[i] % 2 == 0) {evens.add(arr[i]);}}//下标对应已经匹配的偶数的下标,值对应这个偶数的伴侣int[] matcheven = new int[evens.size()];//记录伴侣的对数int count = 0;for(int j=0;j<odds.size();j++){//用于标记对应的偶数是否查找过boolean[] v = new boolean[evens.size()];if(find(odds.get(j),matcheven,evens,v)){count++;}}System.out.println(count);}}private static boolean find(int x, int[] matcheven, ArrayList<Integer> evens, boolean[] v) {for(int i=0;i<evens.size();i++){//该位置偶数没被访问过,并且能与x组成素数伴侣if(isSu(x+evens.get(i))&&!v[i]){v[i] = true;/** 如果i位置偶数还没有伴侣,则与x组成伴侣,如果已经有伴侣,并且这个伴侣能重新找到新伴侣,* 则把原来伴侣让给别人,自己与x组成伴侣*/if(matcheven[i]==0||find(matcheven[i],matcheven,evens,v)){matcheven[i] = x;return true;}}}return false;}public static boolean isSu(int num){for(int i=2;i<num;i++){if(num%i==0){return false;}}return true;}
}
<a name="XMR4y"></a>#### 1.9 【中等】两个排序数组中的第k小的数,要求时间复杂度为O(logN)(字节真题)举例:<br />arr1=[1,2,3,4,5],arr2=[3,4,5],k=1<br />1是所有数中第一小的数,所以返回1<br />arr1=[1,2,3],arr2=[3,4,5,6],k=4<br />3是所有数中第4小的数,所以返回3<br />要求:如果arr1的长度为N,arr2的长度为M,时间复杂度请达到O(log(min{M,N}),额外空间复杂度为O(1)```javapublic int getUpMedian(int[] arr1, int start1, int end1, int[] arr2, int sint mid1 = 0, mid2 = 0;//用于判断过程中数组的长度的奇偶int offset = 0;while (start1 < end1) {mid1 = (start1 + end1) / 2;mid2 = (start2 + end2) / 2;offset = ((end1 - start1 + 1) & 1) ^ 1;//元素个数为奇数,offset为0,元素个数为偶数,offset为1if (arr1[mid1] > arr2[mid2]) {end1 = mid1;start2 = mid2 + offset;} else if (arr1[mid1] < arr2[mid2]) {end2 = mid2;start1 = mid1 + offset;} else {return arr1[mid1];}}return Math.min(arr1[start1], arr2[start2]);}public int findKthNum(int[] arr1, int[] arr2, int kth) {if (arr1 == null || arr2 == null) {throw new RuntimeException("Your arr is invalind");}if (kth < 1 || kth > arr1.length + arr2.length) {throw new RuntimeException("k is invalid");}int[] longs = arr1.length >= arr2.length ? arr1 : arr2;int[] shorts = arr1.length < arr2.length ? arr1 : arr2;int l = longs.length;int s = shorts.length;if (kth <= s) {//在shortArr中选前面k个数,在longArr中也选前面k个数// 则两段数组的上中位数就是第k小数return getUpMedian(shorts, 0, kth - 1, longs, 0, kth - 1);}if (kth > l) {// shorts[kth - l - 1]大于longs的所有数字,那么shorts[kth - l - 1]即为答案if (shorts[kth - l - 1] >= longs[l - 1]) {return shorts[kth - l - 1];}// 同理if (longs[kth - s - 1] >= shorts[s - 1]) {return longs[kth - s - 1];}// 取后面的中位数return getUpMedian(shorts, kth - 1, s - 1, longs, kth - s, l - 1);}// len(s)<k<len(l),如果longs[kth - s - 1]比所有shorts都大,那么即为答案if (longs[kth - s - 1] >= shorts[s - 1]) {return longs[kth - s - 1];}// 取中位数return getUpMedian(shorts, 0, s - l, longs, kth - s, kth - 1);}
参考:https://www.cnblogs.com/pathjh/p/9096652.html
2.链表
2.1 【简单】反转链表(206)

/*** 需要记忆*/public ListNode reverseList(ListNode head) {if (null == head || null == head.next) {return head;}ListNode curr = head;ListNode prev = null;while (curr != null) {ListNode next = curr.next;curr.next = prev;prev = curr;curr = next;}return prev;}
2.2 【简单、中等】链表中有环(141, 142)

/*** 快慢指针*/public boolean hasCycle(ListNode head) {if (head == null || head.next == null) {return false;}ListNode slow = head;ListNode fast = head.next;while (slow != fast) {if (fast == null || fast.next == null) {return false;}slow = slow.next;fast = fast.next.next;}return true;}

/*** 快慢指针,需要推导一个等式 a=c+(n-1)(b+c)a=c+(n−1)(b+c)*/public ListNode detectCycle(ListNode head) {if (head == null) {return null;}ListNode slow = head, fast = head;while (fast != null) {slow = slow.next;if (fast.next != null) {fast = fast.next.next;} else {return null;}if (fast == slow) {ListNode ptr = head;while (ptr != slow) {ptr = ptr.next;slow = slow.next;}return ptr;}}return null;}
2.3 【简单】合并两个有序链表(21)

/*** list1与list2往下走即可*/public ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode result = new ListNode();ListNode prev = result;while (list1 != null && list2 != null) {if (list1.val < list2.val) {prev.next = list1;list1 = list1.next;} else {prev.next = list2;list2 = list2.next;}prev = prev.next;}prev.next = list1 == null ? list2 : list1;return result.next;}
2.4 【中等】删除链表倒数第 n 个结点(19)

/*** 涉及到删除结点的需要一个dummy*/public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummy = new ListNode(0, head);ListNode front = head;// 抽象头节点ListNode temp = dummy;for (int i = 0; i < n; ++i) {front = front.next;}while (front != null) {front = front.next;temp = temp.next;}temp.next = temp.next.next;return dummy.next;}
2.5 【简单】求链表的中间结点(876)

/*** 用数组即可简单实现*/public ListNode middleNode(ListNode head) {ListNode[] nodeArr = new ListNode[100];int count = 0;while (head != null) {nodeArr[count] = head;++count;head = head.next;}return nodeArr[count / 2];}
2.6 【中等】两两交换链表中的节点(24)

/*** 需要dummy以及后两个节点*/public ListNode swapPairs(ListNode head) {// 持有headListNode dummyHead = new ListNode();dummyHead.next = head;ListNode first = dummyHead;while (first.next != null && first.next.next != null) {ListNode second = first.next;ListNode third = second.next;// 交换first.next = third;second.next = third.next;third.next = second;// 移动到下一轮first = second;}return dummyHead.next;}
2.7 【中等】LRU 缓存(146)

/*** 重写LinkedHashMap即可*/class LRUCache extends LinkedHashMap<Integer, Integer> {private int capacity;public LRUCache(int capacity) {super(capacity, 0.75F, true);this.capacity = capacity;}public int get(int key) {return super.getOrDefault(key, -1);}public void put(int key, int value) {super.put(key, value);}@Overrideprotected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {return size() > capacity;}}
2.8 【中等】剑指 Offer II 077. 链表排序

public class SortList {public ListNode sortList(ListNode head) {return sortList(head, null);}public ListNode sortList(ListNode head, ListNode tail) {if (head == null) {return null;}if (head.next == tail) {head.next = null;return head;}// 寻找中点ListNode slow = head, fast = head;while (fast != tail) {slow = slow.next;fast = fast.next;if (fast != tail) {fast = fast.next;}}ListNode mid = slow;ListNode list1 = sortList(head, mid);ListNode list2 = sortList(mid, tail);// 合并链表return merge(list1, list2);}public ListNode merge(ListNode head1, ListNode head2) {ListNode dummyHead = new ListNode(0);ListNode temp = dummyHead, temp1 = head1, temp2 = head2;while (temp1 != null && temp2 != null) {if (temp1.val <= temp2.val) {temp.next = temp1;temp1 = temp1.next;} else {temp.next = temp2;temp2 = temp2.next;}temp = temp.next;}if (temp1 != null) {temp.next = temp1;} else if (temp2 != null) {temp.next = temp2;}return dummyHead.next;}}
2.9 【困难】K 个一组翻转链表(25)

class Solution {public ListNode reverseKGroup(ListNode head, int k) {ListNode hair = new ListNode(0);hair.next = head;ListNode pre = hair;while (head != null) {ListNode tail = pre;// 查看剩余部分长度是否大于等于 kfor (int i = 0; i < k; ++i) {tail = tail.next;if (tail == null) {return hair.next;}}ListNode nex = tail.next;ListNode[] reverse = myReverse(head, tail);head = reverse[0];tail = reverse[1];// 把子链表重新接回原链表pre.next = head;tail.next = nex;pre = tail;head = tail.next;}return hair.next;}public ListNode[] myReverse(ListNode head, ListNode tail) {// 反转ListNode prev = tail.next;ListNode p = head;while (prev != tail) {ListNode nex = p.next;p.next = prev;prev = p;p = nex;}return new ListNode[]{tail, head};}}
