算法训练营三刷(Java) | 第一天~第四天
数组理论基础
第一天
LeetCode 704 二分查找
解题思路:双指针法
时间复杂度:O(log n)
左闭右闭写法
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] > target) right = mid - 1;
if (nums[mid] < target) left = mid + 1;
}
return -1;
}
}
左闭右开写法
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] > target) right = mid;
if (nums[mid] < target) left = mid + 1;
}
return -1;
}
}
LeetCode 27 移除元素
复杂思路:模拟,间隔移动
class Solution {
public int removeElement(int[] nums, int val) {
int num = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == val) num++;
}
num = nums.length - num;
int start = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != val) continue;
start = i;
while (i < nums.length && nums[i] == val) {
i++;
}
int interval = i - start;
for (int j = start; j < nums.length - interval; j++) {
nums[j] = nums[j + interval];
nums[j + interval] = val;
}
i -= interval;
}
return num;
}
}
注意点:每次替换完后i需要回退。
简单思路:双指针
冗杂错误代码示例:
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0, right = 1;
while (left < right && right < nums.length) {
while (right < nums.length && nums[right] == val) right++;
while (left < right && nums[left] != val) left++;
if (right < nums.length) {
nums[left] = nums[right];
nums[right] = val;
}
}
return left;
}
}
正确解法:
将左边视作右边不为val值元素的新数组。left表示的是新数组的下标。right找的是不为val的原数组中的元素。
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0, right = 0;
while (right < nums.length) {
if (nums[right] != val) {
nums[left] = nums[right];
left++; right++;
} else right++;
}
return left;
}
}
第二天
LeetCode 977 有序数组的平方
class Solution {
public int[] sortedSquares(int[] nums) {
for (int i = 0; i < nums.length; i++)
{
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
}
直接平方后重新排序即可
LeetCode 209 长度最小的子数组
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int len = nums.length;
for (int i = 0; i < len; i++)
{
int num_in = nums[i];
for (int j = i + 1; j < len; j++)
{
num_in += nums[j]; // 这里暗示的是连续子数组
if (num_in >= target && j - i + 1 < len)
{
len = j - i + 1;
break;
}
}
}
return len;
}
}
LeetCode 59 螺旋矩阵II
解题思路:按照从左到右、从上到下、从右到左、从下到上的顺序用一个小于n的下标值计数。每一层分别填入对应的数目的元素。
class Solution {
public int[][] generateMatrix(int n) {
int top = 0, bottom = n-1;
int left = 0, right = n-1;
int count = 0;
int[][] result = new int[n][n];
while (count < n * n) {
for (int i = left; i <= right; i++) {
result[top][i] = count+1;
count++;
}
for (int i = top+1; i <= bottom; i++) {
result[i][right] = count+1;
count++;
}
if (left < right && top < bottom) {
for (int i = right-1; i >= left+1; i--) {
result[bottom][i] = count+1;
count++;
}
for (int i = bottom; i >= top+1; i--) {
result[i][left] = count+1;
count++;
}
}
top++; left++; bottom--; right--;
}
return result;
}
}
数组扩充题目
卡码网58 区间和
解题思路:双重循环会超时,这里用到前缀和的思想,即每一个位置处数组元素等于它前一个数组元素加上当前位置输入。每个元素实际含义可以看作是前面所有输入元素之和。
这样两个位置数组元素相减就可以用一重循环解决原先要用二重循环解决的区间求和问题。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n+1];
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt() + a[i-1];
}
while (sc.hasNext()) {
int l = sc.nextInt(), r = sc.nextInt();
int sum = a[r + 1] - a[l];
System.out.println(sum);
}
}
}
卡码网44 开发商购买土地
解题思路:二维前缀和
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int[][] area = new int[n+1][m+1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
area[i][j] = sc.nextInt()+area[i-1][j]+area[i][j-1]-area[i-1][j-1];
}
}
int minDif = area[n][m];
for (int i = 1; i < n; i++) {
if (minDif > Math.abs(area[i][m] - area[n][m] + area[i][m]))
minDif = Math.abs(area[i][m] - area[n][m] + area[i][m]);
}
for (int i = 1; i < m; i++) {
if (minDif > Math.abs(area[n][i] - area[n][m] + area[n][i]))
minDif = Math.abs(area[n][i] - area[n][m] + area[n][i]);
}
System.out.println(minDif);
}
}
第三天
LeetCode 203 移除链表元素
解题思路:模拟+双指针+虚拟头节点
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode virtualHead = new ListNode(-1, head);
ListNode cur = head;
ListNode prev = virtualHead;
while (cur != null) {
while (cur != null && cur.val == val) cur = cur.next;
prev.next = cur;
prev = cur;
if (cur != null)
cur = cur.next;
}
head = virtualHead.next;
return head;
}
}
LeetCode 707 设计链表
解题思路:
自己手动定义链表节点,并在类内定义虚拟头节点和链表长度。逐个实现即可。
class ListNode {
int val;
ListNode next;
public ListNode() {}
public ListNode(int val) { this.val = val; }
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
class MyLinkedList {
int size;
ListNode head;
public MyLinkedList() {
head = new ListNode();
size = 0;
}
public int get(int index) {
if (index + 1 > size) return -1;
ListNode cur = head;
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
public void addAtHead(int val) {
ListNode newNode = new ListNode(val, head.next);
head.next = newNode;
size++;
}
public void addAtTail(int val) {
ListNode cur = head;
while (cur.next != null) cur = cur.next;
cur.next = new ListNode(val);
size++;
}
public void addAtIndex(int index, int val) {
// 注意可以在末尾后面一个位置处插入元素
if (index > size) return;
ListNode cur = head;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
ListNode newNode = new ListNode(val, cur.next);
cur.next = newNode;
size++;
}
public void deleteAtIndex(int index) {
if (index + 1 > size) return;
ListNode cur = head;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.next = cur.next.next;
size--;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
LeetCode 206 反转链表
解题思路:
双指针法,注意最后要把原先头节点next指向空,否则会死循环
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) return head;
ListNode prev = head;
ListNode cur = head.next;
while (cur != null) {
ListNode tmp = cur.next;
cur.next = prev;
prev = cur;
cur = tmp;
}
head.next = null;
return prev;
}
}
第四天
LeetCode 24 两两交换链表中的节点
解题思路:三节点+虚拟头节点
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) return head;
ListNode virtualHead = new ListNode(0, head);
ListNode prev = virtualHead;
ListNode cur = head;
ListNode next = head.next;
while (cur != null && next != null) {
ListNode tmp = next.next;
next.next = cur;
cur.next = tmp;
prev.next = next;
prev = cur;
cur = tmp;
if (tmp != null)
next = tmp.next;
}
return virtualHead.next;
}
}
LeetCode 19 删除链表的倒数第N个节点
解题思路:
计数反转+虚拟头节点+双指针
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode cur = head;
ListNode virtualHead = new ListNode(0, head);
int cnt = 1;
while (cur != null) {
if (cur.next != null) {
cur = cur.next;
cnt++;
} else break;
}
cnt = cnt - n;
cur = head;
ListNode prev = virtualHead;
while (cnt-- > 0) {
prev = cur;
cur = cur.next;
}
prev.next = cur.next;
return virtualHead.next;
}
}
面试题02.07.链表相交
解题思路:移到相同个数开始处,逐个比较
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int sizeA = 0;
int sizeB = 0;
while (curA != null) {
sizeA++; curA = curA.next;
}
while (curB != null) {
sizeB++; curB = curB.next;
}
int dis = Math.abs(sizeA-sizeB);
if (sizeA > sizeB) {
for (int i = 0; i < dis; i++) {
headA = headA.next;
}
} else {
for (int i = 0; i < dis; i++) {
headB = headB.next;
}
}
while (headA != headB) {
headA = headA.next;
headB = headB.next;
}
return headA;
}
}
LeetCode 142 环形链表II
解题思路:
通过快慢指针先找到相遇节点。如果没有说明没有环,直接返回null。
找到之后一个指针指向head,一个指向相遇节点,同步移动直到相遇就是入环节点。
注意开始时快慢指针不能放置在一处,否则直接退出循环。
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) return null;
ListNode fastIndex = head.next.next;
ListNode slowIndex = head.next;
while (fastIndex != null && slowIndex != null && slowIndex != fastIndex) {
if (fastIndex.next == null) return null;
fastIndex = fastIndex.next.next;
slowIndex = slowIndex.next;
}
if (fastIndex == null || slowIndex == null) return null;
ListNode index = head;
while (index != slowIndex) {
index = index.next;
slowIndex = slowIndex.next;
}
return index;
}
}