轮转数组

- 这个思路比较简单,就是两次局部翻转 + 全局翻转
- 不过需要注意如果
k >= nums.length,是需要取余的
java
public void rotate(int[] nums, int k) {
int n = nums.length;
k = k % n;
if (k == 0) return;
reverse(nums, 0, n - k - 1);
reverse(nums, n - k, n - 1);
reverse(nums, 0, n - 1);
}
private void reverse(int[] nums, int from, int to) {
while (from < to) {
int temp = nums[from];
nums[from++] = nums[to];
nums[to--] = temp;
}
}
除了自身以外数组的乘积

如果用除法,就所有元素乘积,最后遍历每个元素一除,时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
如果使用暴力,就是从前到后遍历,一个一个乘起来,跳过自己,时间复杂度是 O ( n ) O(n) O(n),然后一共 n n n 个元素,所以是 O ( n 2 ) O(n^2) O(n2) 时间复杂度,复杂度提高在哪里呢?是因为每次算的时候实际是有重复的,每个位置的 nums[i] 都被计算使用了 ( n − 1 ) (n - 1) (n−1) 次,能不能变为一次呢?

怎么减少重复呢?前缀和!不过不是计算和,而是计算乘积。但是它不是计算前缀和,还要跳过一个数,还不能除法。前缀和 + 后缀和不就可以了
java
public int[] productExceptSelf(int[] nums) {
// 前缀和 + 后缀和
int[] answer = new int[nums.length];
Arrays.fill(answer, 1);
int pre = 1;
// 先计算每个位置处 [0, i - 1] 的前缀乘积
for (int i = 0; i < nums.length - 1; ++i) {
pre *= nums[i];
answer[i + 1] *= pre;
}
int back = 1;
// 再加上每个位置处 [i + 1, nums.length - 1] 的后缀乘积
for (int j = nums.length - 1; j > 0; --j) {
back *= nums[j];
answer[j - 1] *= back;
}
return answer;
}
矩阵置零

- 能不能在遍历到
0的时候把数据上下全置为0?不可以,如果置为0,后续你不知道遍历到的0是因为它本身是0还是你自己设置为0的,所以我们需要保留需要置为0的行或者列,最后遍历完之后统一清理。
java
class Solution {
public void setZeroes(int[][] matrix) {
int n = matrix.length, m = matrix[0].length;
boolean[] rowZero = new boolean[n]; // 行是否需要置零
boolean[] colZero = new boolean[m]; // 列是否需要置零
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (matrix[i][j] == 0)
rowZero[i] = colZero[j] = true;
for (int i = 0; i < n; ++i)
if (rowZero[i])
for (int j = 0; j < m; ++j)
matrix[i][j] = 0;
for (int j = 0; j < m; ++j)
if (colZero[j])
for (int i = 0; i < n; ++i)
matrix[i][j] = 0;
}
}
时间复杂度是 O ( m n ) O(mn) O(mn),这个是没办法优化的,空间复杂度是 O ( m + n ) O(m + n) O(m+n),还能优化吗?能,有一种用常量空间的解决方案
- 用矩阵第一行保存该列是否被置零
- 用矩阵第一列保存该行是否被置零

哎,为什么能这样呢?你想第一行的如果某个元素它本身是 0,你是不是这一列一定是要被清空的,或者这一列它本身是有 0 的,那最后一定也会被置零的,我放到第一行该列的位置,记录为 0 没问题吧
但是还有个小问题,需要区分第一行的 0 是本身这个位置就是 0,还是后来置为 0 的,因为我们是用的第一行第一列来存的,所以后续根据这个置为 0 的时候,是先把 matrix[1][1] 右下的位置都根据规则置为空后,再单独处理第一行第一列,假如原来第一行的 0 是他本身就是 0,那我们后续这一行都要清空的,否则就不用操作了 。所以还需要两个标志位。
java
class Solution {
public void setZeroes(int[][] matrix) {
int n = matrix.length, m = matrix[0].length;
boolean rowZero = false, colZero = false;
for (int i = 0; i < n; i++)
if (matrix[i][0] == 0)
colZero = true;
for (int j = 0; j < m; j++)
if (matrix[0][j] == 0)
rowZero = true; // 记录第一行第一列最后是否需要清空
for (int i = 1; i < n; ++i)
for (int j = 1; j < m; ++j)
if (matrix[i][j] == 0)
matrix[i][0] = matrix[0][j] = 0; // 遍历
for (int i = 1; i < n; ++i)
if (matrix[i][0] == 0)
for (int j = 1; j < m; ++j)
matrix[i][j] = 0;
for (int j = 1; j < m; ++j)
if (matrix[0][j] == 0)
for (int i = 1; i < n; ++i)
matrix[i][j] = 0; // 根据第一行第一列清空 matrix[1][1] 右下部分矩阵
if (rowZero)
for (int j = 0; j < m; ++j)
matrix[0][j] = 0;
if (colZero)
for (int i = 0; i < n; ++i)
matrix[i][0] = 0; // 处理第一行第一列
}
}
相交链表



如果相交,那从后往前,肯定是找到交点的,并且这一部分是他们都有的,但是链表从前往后我们怎么找呢?观察示例 1, A A A 和 B B B 链表如果有交点,只可能从他们尾部长度相等的时候开始,也就是 A A A 第一个结点为 4 4 4 的位置, B B B 第一个结点为 6 6 6 的位置,如果他们 A.next == B.next,才会出现交点。所以我们可以先找链表长度,让他们都从尾部往前开始对齐,再往后找交点
java
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA = 0, lenB = 0;
ListNode A = headA, B = headB;
// 计算A、B链表长度
while (A != null) {
lenA++;
A = A.next;
}
while (B != null) {
lenB++;
B = B.next;
}
A = headA;
B = headB;
// 长度对齐
if (lenA < lenB) {
while (lenA != lenB) {
B = B.next;
lenB--;
}
} else {
while (lenA != lenB) {
A = A.next;
lenA--;
}
}
// 此时 A 往后的链表长度等于 B 往后的链表长度
while (A != B) {
A = A.next;
B = B.next;
}
return A;
}
}
下面再看一种更好的解法

假设有交点,从交点到结束长度为 z z z, A A A 头节点到交点长度为 x x x, B B B 头节点到交点长度为 y y y,有等式 x + y + z = = x + y + z x + y + z == x + y + z x+y+z==x+y+z,含义是什么呢?我让 p p p 从 headA \text{headA} headA 开始走,它遍历完 A A A 之后从 B B B 开始,让 q q q 从 headB \text{headB} headB 开始走,它遍历完 B B B 之后从 A A A 开始,如果有交点,他们一定会相遇!!!如果没有交点,最后两个人都会为 null
java
class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p = headA;
ListNode q = headB;
while (p != q) {
p = p != null ? p.next : headB;
q = q != null ? q.next : headA;
}
return p;
}
}