modify code

master
algorithmzuo 3 years ago
parent 5c8a23caeb
commit fdafcff0ed

@ -0,0 +1,72 @@
package class_2023_02_2_week;
// 你的音乐播放器里有 N 首不同的歌
// 在旅途中,你的旅伴想要听 L 首歌(不一定不同,即,允许歌曲重复
// 请你为她按如下规则创建一个播放列表
// 每首歌至少播放一次
// 一首歌只有在其他 K 首歌播放完之后才能再次播放
// 返回可以满足要求的播放列表的数量
// 由于答案可能非常大,请返回它模 10^9 + 7 的结果
// 测试链接 : https://leetcode.cn/problems/number-of-music-playlists/
public class Code01_NumberOfMusicPlaylists {
public static int mod = 1000000007;
public static int limit = 100;
// 阶乘表
public static long[] fac = new long[limit + 1];
// 阶乘结果的乘法逆元表
public static long[] inv = new long[limit + 1];
static {
fac[0] = inv[0] = 1;
for (int i = 1; i <= limit; i++) {
fac[i] = ((long) i * fac[i - 1]) % mod;
}
// 费马小定理计算乘法逆元
// for (int i = 1; i <= limit; i++) {
// inv[i] = power(fac[i], mod - 2);
// }
// 费马小定理计算乘法逆元,优化如下
// 这一块叫:阶乘的逆元倒推
inv[limit] = power(fac[limit], mod - 2);
for (int i = limit; i > 1; i--) {
inv[i - 1] = ((long) i * inv[i]) % mod;
}
}
// x的n次方% mod之后是多少
public static long power(long x, int n) {
long ans = 1;
while (n > 0) {
if ((n & 1) == 1) {
ans = (ans * x) % mod;
}
x = (x * x) % mod;
n >>= 1;
}
return ans;
}
// n * logn
public static int numMusicPlaylists(int n, int l, int k) {
long cur, ans = 0, sign = 1;
for (int i = 0; i <= n - k; i++, sign = sign == 1 ? (mod - 1) : 1) {
// cur ->
// fac[n] -> n! % mod 的结果!
// inv[i] -> i! 的逆元!
// inv[n - k - i] -> (n - k - i)! 的逆元
// sign * 1 -> 1
// * -1 -> mod - 1
cur = (sign * power(n - k - i, l - k)) % mod;
cur = (cur * fac[n]) % mod;
cur = (cur * inv[i]) % mod;
cur = (cur * inv[n - k - i]) % mod;
ans = (ans + cur) % mod;
}
return (int) ans;
}
}

@ -0,0 +1,82 @@
package class_2023_02_2_week;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
// 给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。
// 对位于 (row, col) 的每个结点而言,
// 其左右子结点分别位于 (row + 1, col - 1) 和 (row + 1, col + 1)
// 树的根结点位于 (0, 0) 。
// 二叉树的 垂序遍历 从最左边的列开始直到最右边的列结束,按列索引每一列上的所有结点,
// 形成一个按出现位置从上到下排序的有序列表。如果同行同列上有多个结点,
// 则按结点的值从小到大进行排序。
// 返回二叉树的 垂序遍历 序列。
// 测试链接 : https://leetcode.cn/problems/vertical-order-traversal-of-a-binary-tree/
public class Code02_VerticalOrderTraversalOfBinaryTree {
// 不提交这个类
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
}
// 提交以下所有的code
public static class Info {
public int row;
public int col;
public int val;
public Info(int r, int c, int v) {
row = r;
col = c;
val = v;
}
}
public static class InfoComparator implements Comparator<Info> {
@Override
public int compare(Info o1, Info o2) {
if (o1.col != o2.col) {
return o1.col - o2.col;
}
if (o1.row != o2.row) {
return o1.row - o2.row;
}
return o1.val - o2.val;
}
}
public static List<List<Integer>> verticalTraversal(TreeNode root) {
ArrayList<Info> collects = new ArrayList<>();
Info rootInfo = new Info(0, 0, root.val);
collects.add(rootInfo);
dfs(root, rootInfo, collects);
List<List<Integer>> ans = new ArrayList<>();
collects.sort(new InfoComparator());
for (int i = 0; i < collects.size(); i++) {
if (i == 0 || collects.get(i - 1).col != collects.get(i).col) {
ans.add(new ArrayList<>());
}
ans.get(ans.size() - 1).add(collects.get(i).val);
}
return ans;
}
public static void dfs(TreeNode root, Info rootInfo, ArrayList<Info> collects) {
if (root.left != null) {
Info leftInfo = new Info(rootInfo.row + 1, rootInfo.col - 1, root.left.val);
collects.add(leftInfo);
dfs(root.left, leftInfo, collects);
}
if (root.right != null) {
Info rightInfo = new Info(rootInfo.row + 1, rootInfo.col + 1, root.right.val);
collects.add(rightInfo);
dfs(root.right, rightInfo, collects);
}
}
}

@ -0,0 +1,54 @@
package class_2023_02_2_week;
import java.util.LinkedList;
// 给你一棵二叉树的根节点 root ,返回树的 最大宽度 。
// 树的 最大宽度 是所有层中最大的 宽度 。
// 每一层的 宽度 被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。
// 将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null 节点,
// 这些 null 节点也计入长度。
// 题目数据保证答案将会在 32 位 带符号整数范围内。
// 测试链接 : https://leetcode.cn/problems/maximum-width-of-binary-tree/
public class Code03_WidthOfBinaryTree {
// 不提交这个类
public static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
}
// 提交以下所有的方法
public static class Info {
// 当前节点
public TreeNode node;
// 编号!
public int index;
public Info(TreeNode n, int i) {
node = n;
index = i;
}
}
public static int widthOfBinaryTree(TreeNode root) {
int ans = 1;
LinkedList<Info> queue = new LinkedList<>();
queue.add(new Info(root, 1));
while (!queue.isEmpty()) {
ans = Math.max(ans, queue.peekLast().index - queue.peekFirst().index + 1);
int size = queue.size();
for (int i = 0; i < size; i++) {
Info cur = queue.pollFirst();
if (cur.node.left != null) {
queue.addLast(new Info(cur.node.left, cur.index * 2));
}
if (cur.node.right != null) {
queue.addLast(new Info(cur.node.right, cur.index * 2 + 1));
}
}
}
return ans;
}
}

@ -0,0 +1,144 @@
package class_2023_02_2_week;
import java.util.Arrays;
// 给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示
// 在节点网络中,只有当 graph[i][j] = 1 时,节点 i 能够直接连接到另一个节点 j。
// 一些节点 initial 最初被恶意软件感染。只要两个节点直接连接,
// 且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意软件感染。
// 这种恶意软件的传播将继续,直到没有更多的节点可以被这种方式感染。
// 假设 M(initial) 是在恶意软件停止传播之后,整个网络中感染恶意软件的最终节点数。
// 我们可以从 initial 中删除一个节点,
// 并完全移除该节点以及从该节点到任何其他节点的任何连接。
// 请返回移除后能够使 M(initial) 最小化的节点。
// 如果有多个节点满足条件,返回索引 最小的节点 。
// initial 中每个整数都不同
// 测试链接 : https://leetcode.cn/problems/minimize-malware-spread-ii/
public class Code04_MinimizeMalwareSpreadII {
public int minMalwareSpread(int[][] graph, int[] initial) {
// 节点个数
int n = graph.length;
// 病毒数组,
// 病毒initial -> [3,6,9]
// virus[3] = true;
// virus[6] = true;
// virus[7] = false;
boolean[] virus = new boolean[n];
for (int i : initial) {
virus[i] = true;
}
// 建立并查集
// n大小 0~n-1
UnionFind uf = new UnionFind(n);
// 忽略感染点及其链接!
// 合并集合!
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// i, j ?
if (graph[i][j] == 1 && !virus[i] && !virus[j]) {
uf.union(i, j);
}
}
}
// 所有非感染点,该链接都链接了!
// 0,6,7,8 是一个集合
// 6被选成了代表点无法控制只由并查集自己决定
// infect[6] == -1 ,目前这个集合没有感染源
// infect[6] == -2 ,目前这个集合已经发现了不只一个感染源
// infect[6] == x x>=0目前这个集合已经发现了一个感染源是x点
int[] infect = new int[n];
// 目前所有集合都没有感染源
Arrays.fill(infect, -1);
for (int v : initial) { // 枚举所有感染点!
// 当前感染点是v
// 跟v链接的所有点next
// n = 7 编号0~6
// n = 13 编号0~12
for (int next = 0; next < n; next++) {
if (v != next && !virus[next] && graph[v][next] == 1) {
// next 无辜点
// 找到next集合所在的代表点f
int f = uf.find(next);
if (infect[f] == -1) {
infect[f] = v;
} else { // != -1 可能 == -2 或者 >= 0
// 之前的感染点 -> infect[f]
if (infect[f] != -2 && infect[f] != v) {
infect[f] = -2;
}
// infect[f] == -2
// infect[f] == v
// 这两种情况,一律不设置是符合预期的
}
}
}
}
int[] cnt = new int[n];
for (int i = 0; i < n; i++) {
if (infect[i] >= 0) {
cnt[infect[i]] += uf.size[i];
}
}
// 4 -> 13
// 0 -> 6
// 1 -> 5
// 13 -> 50
// 9 -> 20
// 0 1 4 9 13 14 15
// 6 5 13 20 50 34 7
Arrays.sort(initial);
int ans = initial[0];
int count = cnt[ans];
for (int i : initial) {
if (cnt[i] > count) {
ans = i;
count = cnt[i];
}
}
return ans;
}
public static class UnionFind {
public int[] father;
public int[] size;
public int[] help;
public UnionFind(int n) {
father = new int[n];
size = new int[n];
help = new int[n];
for (int i = 0; i < n; i++) {
father[i] = i;
size[i] = 1;
}
}
public int find(int i) {
int hi = 0;
while (i != father[i]) {
help[hi++] = i;
i = father[i];
}
while (hi != 0) {
father[help[--hi]] = i;
}
return i;
}
public void union(int i, int j) {
int fi = find(i);
int fj = find(j);
if (fi != fj) {
if (size[fi] >= size[fj]) {
father[fj] = fi;
size[fi] += size[fj];
} else {
father[fi] = fj;
size[fj] += size[fi];
}
}
}
}
}

@ -2911,5 +2911,65 @@ k位翻转 就是从 nums 中选择一个长度为 k 的 子数组
第057节 2023年2月第2周流行算法题目解析
你的音乐播放器里有 N 首不同的歌
在旅途中,你的旅伴想要听 L 首歌(不一定不同,即,允许歌曲重复
请你为她按如下规则创建一个播放列表
每首歌至少播放一次
一首歌只有在其他 K 首歌播放完之后才能再次播放
返回可以满足要求的播放列表的数量
由于答案可能非常大,请返回它模 10^9 + 7 的结果
测试链接 : https://leetcode.cn/problems/number-of-music-playlists/
给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。
对位于 (row, col) 的每个结点而言,
其左右子结点分别位于 (row + 1, col - 1) 和 (row + 1, col + 1)
树的根结点位于 (0, 0) 。
二叉树的 垂序遍历 从最左边的列开始直到最右边的列结束,按列索引每一列上的所有结点,
形成一个按出现位置从上到下排序的有序列表。如果同行同列上有多个结点,
则按结点的值从小到大进行排序。
返回二叉树的 垂序遍历 序列。
测试链接 : https://leetcode.cn/problems/vertical-order-traversal-of-a-binary-tree/
给你一棵二叉树的根节点 root ,返回树的 最大宽度 。
树的 最大宽度 是所有层中最大的 宽度 。
每一层的 宽度 被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。
将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null 节点,
这些 null 节点也计入长度。
题目数据保证答案将会在 32 位 带符号整数范围内。
测试链接 : https://leetcode.cn/problems/maximum-width-of-binary-tree/
给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示
在节点网络中,只有当 graph[i][j] = 1 时,节点 i 能够直接连接到另一个节点 j。
一些节点 initial 最初被恶意软件感染。只要两个节点直接连接,
且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意软件感染。
这种恶意软件的传播将继续,直到没有更多的节点可以被这种方式感染。
假设 M(initial) 是在恶意软件停止传播之后,整个网络中感染恶意软件的最终节点数。
我们可以从 initial 中删除一个节点,
并完全移除该节点以及从该节点到任何其他节点的任何连接。
请返回移除后能够使 M(initial) 最小化的节点。
如果有多个节点满足条件,返回索引 最小的节点 。
initial 中每个整数都不同
测试链接 : https://leetcode.cn/problems/minimize-malware-spread-ii/

Loading…
Cancel
Save