From 7189c8d6909ced193ba3d9e412995b77af553c7a Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Thu, 2 Mar 2023 13:58:26 +0800 Subject: [PATCH] modify code --- .../mca_04/Code01_CompleteTreeNodeNumber.java | 50 ++++++ .../mca_04/Code02_DiameterOfBinaryTree.java | 48 ++++++ .../第03期/mca_04/Code03_MaxSubBSTSize.java | 87 ++++++++++ .../第03期/mca_04/Code04_UnionFind.java | 92 +++++++++++ .../第03期/mca_04/Code05_FriendCircles.java | 70 ++++++++ .../mca_04/Code06_CouplesHoldingHands.java | 74 +++++++++ .../mca_04/Code07_SwimInRisingWater.java | 155 ++++++++++++++++++ .../第03期/mca_04/Code08_LRUCache.java | 122 ++++++++++++++ 8 files changed, 698 insertions(+) create mode 100644 MCA算法突击课/第03期/mca_04/Code01_CompleteTreeNodeNumber.java create mode 100644 MCA算法突击课/第03期/mca_04/Code02_DiameterOfBinaryTree.java create mode 100644 MCA算法突击课/第03期/mca_04/Code03_MaxSubBSTSize.java create mode 100644 MCA算法突击课/第03期/mca_04/Code04_UnionFind.java create mode 100644 MCA算法突击课/第03期/mca_04/Code05_FriendCircles.java create mode 100644 MCA算法突击课/第03期/mca_04/Code06_CouplesHoldingHands.java create mode 100644 MCA算法突击课/第03期/mca_04/Code07_SwimInRisingWater.java create mode 100644 MCA算法突击课/第03期/mca_04/Code08_LRUCache.java diff --git a/MCA算法突击课/第03期/mca_04/Code01_CompleteTreeNodeNumber.java b/MCA算法突击课/第03期/mca_04/Code01_CompleteTreeNodeNumber.java new file mode 100644 index 0000000..3068efc --- /dev/null +++ b/MCA算法突击课/第03期/mca_04/Code01_CompleteTreeNodeNumber.java @@ -0,0 +1,50 @@ +package 第03期.mca_04; + +// 给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数 +// 完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外 +// 其余每层节点数都达到最大值 +// 并且最下面一层的节点都集中在该层最左边的若干位置 +// 若最底层为第 h 层,则该层包含 1~ 2h 个节点。 +// 测试链接 : https://leetcode.cn/problems/count-complete-tree-nodes/ +public class Code01_CompleteTreeNodeNumber { + + // 提交时不要提交这个类 + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + } + + // 提交如下的方法 + public static int countNodes(TreeNode head) { + if (head == null) { + return 0; + } + return bs(head, 1, mostLeftLevel(head, 1)); + } + + // 当前来到node节点,node节点在level层,总层数是h + // 返回node为头的子树(必是完全二叉树),有多少个节点 + public static int bs(TreeNode node, int Level, int h) { + if (Level == h) { + return 1; + } + if (mostLeftLevel(node.right, Level + 1) == h) { + return (1 << (h - Level)) + bs(node.right, Level + 1, h); + } else { + return (1 << (h - Level - 1)) + bs(node.left, Level + 1, h); + } + } + + // 如果node在第level层, + // 求以node为头的子树,最大深度是多少 + // node为头的子树,一定是完全二叉树 + public static int mostLeftLevel(TreeNode node, int level) { + while (node != null) { + level++; + node = node.left; + } + return level - 1; + } + +} diff --git a/MCA算法突击课/第03期/mca_04/Code02_DiameterOfBinaryTree.java b/MCA算法突击课/第03期/mca_04/Code02_DiameterOfBinaryTree.java new file mode 100644 index 0000000..db812d5 --- /dev/null +++ b/MCA算法突击课/第03期/mca_04/Code02_DiameterOfBinaryTree.java @@ -0,0 +1,48 @@ +package 第03期.mca_04; + +// 给定一棵二叉树,你需要计算它的直径长度 +// 一棵二叉树的直径长度是任意两个结点路径长度中的最大值 +// 这条路径可能穿过也可能不穿过根结点 +// 测试链接 : https://leetcode.cn/problems/diameter-of-binary-tree/ +public class Code02_DiameterOfBinaryTree { + + // 提交时不要提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int value) { + val = value; + } + } + + // 提交以下的方法 + public static int diameterOfBinaryTree(TreeNode root) { + return process(root).maxDistance; + } + + public static class Info { + public int maxDistance; + public int height; + + public Info(int m, int h) { + maxDistance = m; + height = h; + } + } + + public static Info process(TreeNode x) { + if (x == null) { + return new Info(0, 0); + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + int p1 = Math.max(leftInfo.maxDistance, rightInfo.maxDistance); + int p2 = leftInfo.height + rightInfo.height; + int maxDistance = Math.max(p1, p2); + return new Info(maxDistance, height); + } + +} diff --git a/MCA算法突击课/第03期/mca_04/Code03_MaxSubBSTSize.java b/MCA算法突击课/第03期/mca_04/Code03_MaxSubBSTSize.java new file mode 100644 index 0000000..a72d279 --- /dev/null +++ b/MCA算法突击课/第03期/mca_04/Code03_MaxSubBSTSize.java @@ -0,0 +1,87 @@ +package 第03期.mca_04; + +// 给定一个二叉树,找到其中最大的二叉搜索树(BST)子树,并返回该子树的大小 +// 其中,最大指的是子树节点数最多的 +// 二叉搜索树(BST)中的所有节点都具备以下属性: +// 左子树的值小于其父(根)节点的值 +// 右子树的值大于其父(根)节点的值 +// 注意:子树必须包含其所有后代 +// 测试链接 : https://leetcode.cn/problems/largest-bst-subtree +public class Code03_MaxSubBSTSize { + + // 提交时不要提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int value) { + val = value; + } + } + + // 提交如下方法 + public static int largestBSTSubtree(TreeNode head) { + if (head == null) { + return 0; + } + return process(head).maxBSTSubtreeSize; + } + + public static class Info { + public int maxBSTSubtreeSize; + public int allSize; + public int max; + public int min; + + public Info(int m, int a, int ma, int mi) { + maxBSTSubtreeSize = m; + allSize = a; + max = ma; + min = mi; + } + } + + public static Info process(TreeNode x) { + if (x == null) { + return null; + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int max = x.val; + int min = x.val; + int allSize = 1; + if (leftInfo != null) { + max = Math.max(leftInfo.max, max); + min = Math.min(leftInfo.min, min); + allSize += leftInfo.allSize; + } + if (rightInfo != null) { + max = Math.max(rightInfo.max, max); + min = Math.min(rightInfo.min, min); + allSize += rightInfo.allSize; + } + int p1 = -1; + if (leftInfo != null) { + p1 = leftInfo.maxBSTSubtreeSize; + } + int p2 = -1; + if (rightInfo != null) { + p2 = rightInfo.maxBSTSubtreeSize; + } + int p3 = -1; + boolean leftBST = leftInfo == null ? true : (leftInfo.maxBSTSubtreeSize == leftInfo.allSize); + boolean rightBST = rightInfo == null ? true : (rightInfo.maxBSTSubtreeSize == rightInfo.allSize); + if (leftBST && rightBST) { + boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.val); + boolean rightMinMoreX = rightInfo == null ? true : (x.val < rightInfo.min); + if (leftMaxLessX && rightMinMoreX) { + int leftSize = leftInfo == null ? 0 : leftInfo.allSize; + int rightSize = rightInfo == null ? 0 : rightInfo.allSize; + p3 = leftSize + rightSize + 1; + } + } + return new Info(Math.max(p1, Math.max(p2, p3)), allSize, max, min); + } + +} diff --git a/MCA算法突击课/第03期/mca_04/Code04_UnionFind.java b/MCA算法突击课/第03期/mca_04/Code04_UnionFind.java new file mode 100644 index 0000000..dfb4188 --- /dev/null +++ b/MCA算法突击课/第03期/mca_04/Code04_UnionFind.java @@ -0,0 +1,92 @@ +package 第03期.mca_04; + +// 测试链接 : https://www.nowcoder.com/questionTerminal/e7ed657974934a30b2010046536a5372 +// 请务必理解这个文件的实现,而且还提供了测试链接 +// 提交如下的code,并把主类名改成"Main" +// 在测试链接里可以直接通过 +// 请同学们务必参考如下代码中关于输入、输出的处理 +// 这是输入输出处理效率很高的写法 +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code04_UnionFind { + + public static int MAXN = 1000001; + + public static int[] father = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static int[] help = new int[MAXN]; + + // 初始化并查集 + public static void init(int n) { + for (int i = 0; i <= n; i++) { + father[i] = i; + size[i] = 1; + } + } + + // 从i开始寻找集合代表点 + public static int find(int i) { + int hi = 0; + while (i != father[i]) { + help[hi++] = i; + i = father[i]; + } + for (hi--; hi >= 0; hi--) { + father[help[hi]] = i; + } + return i; + } + + // 查询x和y是不是一个集合 + public static boolean isSameSet(int x, int y) { + return find(x) == find(y); + } + + // x所在的集合,和y所在的集合,合并成一个集合 + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (fx != fy) { + if (size[fx] >= size[fy]) { + size[fx] += size[fy]; + father[fy] = fx; + } else { + size[fy] += size[fx]; + father[fx] = fy; + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + while (in.nextToken() != StreamTokenizer.TT_EOF) { + int n = (int) in.nval; + init(n); + in.nextToken(); + int m = (int) in.nval; + for (int i = 0; i < m; i++) { + in.nextToken(); + int op = (int) in.nval; + in.nextToken(); + int x = (int) in.nval; + in.nextToken(); + int y = (int) in.nval; + if (op == 1) { + out.println(isSameSet(x, y) ? "Yes" : "No"); + out.flush(); + } else { + union(x, y); + } + } + } + } +} diff --git a/MCA算法突击课/第03期/mca_04/Code05_FriendCircles.java b/MCA算法突击课/第03期/mca_04/Code05_FriendCircles.java new file mode 100644 index 0000000..5083fe0 --- /dev/null +++ b/MCA算法突击课/第03期/mca_04/Code05_FriendCircles.java @@ -0,0 +1,70 @@ +package 第03期.mca_04; + +// 本题为leetcode原题 +// 测试链接:https://leetcode.cn/problems/friend-circles/ +// 可以直接通过 +public class Code05_FriendCircles { + + public static int findCircleNum(int[][] M) { + int N = M.length; + UnionFind uf = new UnionFind(N); + for (int i = 0; i < N; i++) { + for (int j = i + 1; j < N; j++) { + if (M[i][j] == 1) { // i和j互相认识 + uf.union(i, j); + } + } + } + return uf.sets(); + } + + public static class UnionFind { + private int[] father; + private int[] size; + private int[] help; + private int sets; + + public UnionFind(int N) { + father = new int[N]; + size = new int[N]; + help = new int[N]; + sets = N; + for (int i = 0; i < N; i++) { + father[i] = i; + size[i] = 1; + } + } + + private int find(int i) { + int hi = 0; + while (i != father[i]) { + help[hi++] = i; + i = father[i]; + } + for (hi--; hi >= 0; hi--) { + father[help[hi]] = i; + } + return i; + } + + public void union(int i, int j) { + int f1 = find(i); + int f2 = find(j); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + size[f1] += size[f2]; + father[f2] = f1; + } else { + size[f2] += size[f1]; + father[f1] = f2; + } + sets--; + } + } + + public int sets() { + return sets; + } + } + +} diff --git a/MCA算法突击课/第03期/mca_04/Code06_CouplesHoldingHands.java b/MCA算法突击课/第03期/mca_04/Code06_CouplesHoldingHands.java new file mode 100644 index 0000000..450489a --- /dev/null +++ b/MCA算法突击课/第03期/mca_04/Code06_CouplesHoldingHands.java @@ -0,0 +1,74 @@ +package 第03期.mca_04; + +// n对情侣坐在连续排列的 2n 个座位上,想要牵到对方的手 +// 人和座位由一个整数数组 row 表示,其中 row[i] 是坐在第 i 个座位上的人的ID +// 情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2n-2, 2n-1) +// 返回 最少交换座位的次数,以便每对情侣可以并肩坐在一起 +// 每次交换可选择任意两人,让他们站起来交换座位 +// 测试链接 : https://leetcode.cn/problems/couples-holding-hands/ +public class Code06_CouplesHoldingHands { + + public int minSwapsCouples(int[] row) { + // n人数,偶数 + int n = row.length; + // n/2 + // 0 1 -> 0 0 + // 4 5 -> 2 2 + UnionFind uf = new UnionFind(n / 2); + for (int i = 0; i < n; i += 2) { + uf.union(row[i] / 2, row[i + 1] / 2); + } + return n / 2 - uf.sets(); + } + + public static class UnionFind { + public int[] father; + public int[] size; + public int[] help; + public int sets; + + 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; + } + sets = n; + } + + private 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]; + } + sets--; + } + } + + public int sets() { + return sets; + } + + } + +} diff --git a/MCA算法突击课/第03期/mca_04/Code07_SwimInRisingWater.java b/MCA算法突击课/第03期/mca_04/Code07_SwimInRisingWater.java new file mode 100644 index 0000000..932160b --- /dev/null +++ b/MCA算法突击课/第03期/mca_04/Code07_SwimInRisingWater.java @@ -0,0 +1,155 @@ +package 第03期.mca_04; + +import java.util.Arrays; +import java.util.PriorityQueue; + +// 在一个 n x n 的整数矩阵 grid 中, +// 每一个方格的值 grid[i][j] 表示位置 (i, j) 的平台高度。 +// 当开始下雨时,在时间为 t 时,水池中的水位为 t 。 +// 你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。 +// 假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。 +// 当然,在你游泳的时候你必须待在坐标方格里面。 +// 你从坐标方格的左上平台 (0,0) 出发。 +// 返回 你到达坐标方格的右下平台 (n-1, n-1) 所需的最少时间 。 +// 测试链接 :https://leetcode.cn/problems/swim-in-rising-water +public class Code07_SwimInRisingWater { + + // 并查集的解法 + public static int swimInWater1(int[][] grid) { + // 行号 + int n = grid.length; + // 列号 + int m = grid[0].length; + // [0,0,5] + // [0,1,3].... + int[][] points = new int[n * m][3]; + int pi = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + points[pi][0] = i; + points[pi][1] = j; + points[pi++][2] = grid[i][j]; + } + } + // 所有格子小对象,生成好了! + // 排序![a,b,c] [d,e,f] + Arrays.sort(points, (a, b) -> a[2] - b[2]); + // 生成并查集!n * m + // 初始化的时候,把所有格子独自成一个集合! + UnionFind uf = new UnionFind(n, m); + int ans = 0; + for (int i = 0; i < points.length; i++) { + int r = points[i][0]; + int c = points[i][1]; + int v = points[i][2]; + if (r > 0 && grid[r - 1][c] <= v) { + uf.union(r, c, r - 1, c); + } + if (r < n - 1 && grid[r + 1][c] <= v) { + uf.union(r, c, r + 1, c); + } + if (c > 0 && grid[r][c - 1] <= v) { + uf.union(r, c, r, c - 1); + } + if (c < m - 1 && grid[r][c + 1] <= v) { + uf.union(r, c, r, c + 1); + } + if (uf.isSameSet(0, 0, n - 1, m - 1)) { + ans = v; + break; + } + } + return ans; + } + + public static class UnionFind { + public int col; + public int pointsSize; + public int[] father; + public int[] size; + public int[] help; + + public UnionFind(int n, int m) { + col = m; + pointsSize = n * m; + father = new int[pointsSize]; + size = new int[pointsSize]; + help = new int[pointsSize]; + for (int i = 0; i < pointsSize; i++) { + father[i] = i; + size[i] = 1; + } + } + + private 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; + } + + private int index(int i, int j) { + return i * col + j; + } + + public void union(int row1, int col1, int row2, int col2) { + int f1 = find(index(row1, col1)); + int f2 = find(index(row2, col2)); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + father[f2] = f1; + size[f1] += size[f2]; + } else { + father[f1] = f2; + size[f2] += size[f1]; + } + } + } + + public boolean isSameSet(int row1, int col1, int row2, int col2) { + return find(index(row1, col1)) == find(index(row2, col2)); + } + + } + + // Dijkstra算法 + public static int swimInWater2(int[][] grid) { + int n = grid.length; + int m = grid[0].length; + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[2] - b[2]); + boolean[][] visited = new boolean[n][m]; + heap.add(new int[] { 0, 0, grid[0][0] }); + int ans = 0; + while (!heap.isEmpty()) { + int r = heap.peek()[0]; + int c = heap.peek()[1]; + int v = heap.peek()[2]; + heap.poll(); + if (visited[r][c]) { + continue; + } + visited[r][c] = true; + if (r == n - 1 && c == m - 1) { + ans = v; + break; + } + add(grid, heap, visited, r - 1, c, v); + add(grid, heap, visited, r + 1, c, v); + add(grid, heap, visited, r, c - 1, v); + add(grid, heap, visited, r, c + 1, v); + } + return ans; + } + + public static void add(int[][] grid, PriorityQueue heap, boolean[][] visited, int r, int c, int preV) { + if (r >= 0 && r < grid.length && c >= 0 && c < grid[0].length && !visited[r][c]) { + heap.add(new int[] { r, c, preV + Math.max(0, grid[r][c] - preV) }); + } + } + +} diff --git a/MCA算法突击课/第03期/mca_04/Code08_LRUCache.java b/MCA算法突击课/第03期/mca_04/Code08_LRUCache.java new file mode 100644 index 0000000..d0e43d8 --- /dev/null +++ b/MCA算法突击课/第03期/mca_04/Code08_LRUCache.java @@ -0,0 +1,122 @@ +package 第03期.mca_04; + +import java.util.HashMap; + +// 本题测试链接 : https://leetcode.cn/problems/lru-cache/ +public class Code08_LRUCache { + + // 提交以下这个类 + public class LRUCache { + + public static class Node { + public int key; + public int val; + public Node last; + public Node next; + + public Node(int k, int v) { + key = k; + val = v; + } + } + + public static class DoubleLinkedList { + private Node head; + private Node tail; + + public DoubleLinkedList() { + head = null; + tail = null; + } + + public void addNode(Node newNode) { + if (newNode == null) { + return; + } + if (head == null) { + head = newNode; + tail = newNode; + } else { + tail.next = newNode; + newNode.last = tail; + tail = newNode; + } + } + + public void moveNodeToTail(Node node) { + if (tail == node) { + return; + } + if (head == node) { + head = node.next; + head.last = null; + } else { + node.last.next = node.next; + node.next.last = node.last; + } + node.last = tail; + node.next = null; + this.tail.next = node; + this.tail = node; + } + + public Node removeHead() { + if (head == null) { + return null; + } + Node ans = this.head; + if (head == tail) { + head = null; + tail = null; + } else { + head = ans.next; + ans.next = null; + head.last = null; + } + return ans; + } + + } + + private HashMap keyNodeMap; + private DoubleLinkedList nodeList; + private final int capacity; + + public LRUCache(int cap) { + keyNodeMap = new HashMap<>(); + nodeList = new DoubleLinkedList(); + capacity = cap; + } + + public int get(int key) { + if (keyNodeMap.containsKey(key)) { + Node ans = keyNodeMap.get(key); + nodeList.moveNodeToTail(ans); + return ans.val; + } + return -1; + } + + public void put(int key, int value) { + if (keyNodeMap.containsKey(key)) { + Node node = keyNodeMap.get(key); + node.val = value; + nodeList.moveNodeToTail(node); + } else { + Node newNode = new Node(key, value); + keyNodeMap.put(key, newNode); + nodeList.addNode(newNode); + if (keyNodeMap.size() == capacity + 1) { + removeMostUnusedCache(); + } + } + } + + private void removeMostUnusedCache() { + Node removeNode = nodeList.removeHead(); + keyNodeMap.remove(removeNode.key); + } + + } + +}