数组
- 数组(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>>();
// 枚举 a
for (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();
//将奇数添加到odds
if (arr[i] % 2 == 1) {
odds.add(arr[i]);
}
//将偶数添加到evens
if (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)
```java
public int getUpMedian(int[] arr1, int start1, int end1, int[] arr2, int s
int 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为1
if (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) {
// 持有head
ListNode 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);
}
@Override
protected 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;
// 查看剩余部分长度是否大于等于 k
for (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};
}
}