diff --git a/MCA算法突击课/第02期/mca_01/Code03_IsCBT.java b/MCA算法突击课/第02期/mca_01/Code03_IsCBT.java new file mode 100644 index 0000000..83a4811 --- /dev/null +++ b/MCA算法突击课/第02期/mca_01/Code03_IsCBT.java @@ -0,0 +1,120 @@ +package 第02期.mca_01; + +import java.util.LinkedList; + +// 测试链接 : https://leetcode.com/problems/check-completeness-of-a-binary-tree/ + +public class Code03_IsCBT { + + // 不要提交这个类 + public static class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + + public TreeNode(int v) { + val = v; + } + } + + public static boolean isCompleteTree1(TreeNode head) { + if (head == null) { + return true; + } + LinkedList queue = new LinkedList<>(); + // 是否遇到过左右两个孩子不双全的节点 + boolean leaf = false; + TreeNode l = null; + TreeNode r = null; + queue.add(head); + while (!queue.isEmpty()) { + head = queue.poll(); + l = head.left; + r = head.right; + if ( + // 如果遇到了不双全的节点之后,又发现当前节点不是叶节点 + (leaf && (l != null || r != null)) || (l == null && r != null) + + ) { + return false; + } + if (l != null) { + queue.add(l); + } + if (r != null) { + queue.add(r); + } + if (l == null || r == null) { + leaf = true; + } + } + return true; + } + + public static boolean isCompleteTree2(TreeNode head) { + return process(head).isCBT; + } + + public static class Info { + public boolean isFull; + public boolean isCBT; + public int height; + + public Info(boolean full, boolean cbt, int h) { + isFull = full; + isCBT = cbt; + height = h; + } + } + + public static Info process(TreeNode x) { + if (x == null) { + return new Info(true, true, 0); + } + Info leftInfo = process(x.left); + Info rightInfo = process(x.right); + int height = Math.max(leftInfo.height, rightInfo.height) + 1; + boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height; + boolean isCBT = false; + if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height) { + isCBT = true; + } else if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) { + isCBT = true; + } else if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) { + isCBT = true; + } else if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) { + isCBT = true; + } + return new Info(isFull, isCBT, height); + } + + // for test + public static TreeNode generateRandomBST(int maxLevel, int maxValue) { + return generate(1, maxLevel, maxValue); + } + + // for test + public static TreeNode generate(int level, int maxLevel, int maxValue) { + if (level > maxLevel || Math.random() < 0.5) { + return null; + } + TreeNode head = new TreeNode((int) (Math.random() * maxValue)); + head.left = generate(level + 1, maxLevel, maxValue); + head.right = generate(level + 1, maxLevel, maxValue); + return head; + } + + public static void main(String[] args) { + int maxLevel = 5; + int maxValue = 100; + int testTimes = 1000000; + for (int i = 0; i < testTimes; i++) { + TreeNode head = generateRandomBST(maxLevel, maxValue); + if (isCompleteTree1(head) != isCompleteTree2(head)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/MCA算法突击课/第02期/mca_01/Code04_NumberOfIslands.java b/MCA算法突击课/第02期/mca_01/Code04_NumberOfIslands.java new file mode 100644 index 0000000..a4be2b8 --- /dev/null +++ b/MCA算法突击课/第02期/mca_01/Code04_NumberOfIslands.java @@ -0,0 +1,317 @@ +package 第02期.mca_01; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +// 本题为leetcode原题 +// 测试链接:https://leetcode.com/problems/number-of-islands/ +// 所有方法都可以直接通过 +public class Code04_NumberOfIslands { + + public static int numIslands3(char[][] board) { + int islands = 0; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == '1') { + islands++; + infect(board, i, j); + } + } + } + return islands; + } + + // 从(i,j)这个位置出发,把所有练成一片的'1'字符,变成0 + public static void infect(char[][] board, int i, int j) { + if (i < 0 || i == board.length || j < 0 || j == board[0].length || board[i][j] != '1') { + return; + } + board[i][j] = 0; + infect(board, i - 1, j); + infect(board, i + 1, j); + infect(board, i, j - 1); + infect(board, i, j + 1); + } + + public static int numIslands1(char[][] board) { + int row = board.length; + int col = board[0].length; + Dot[][] dots = new Dot[row][col]; + List dotList = new ArrayList<>(); + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (board[i][j] == '1') { + dots[i][j] = new Dot(); + dotList.add(dots[i][j]); + } + } + } + UnionFind1 uf = new UnionFind1<>(dotList); + for (int j = 1; j < col; j++) { + // (0,j) (0,0)跳过了 (0,1) (0,2) (0,3) + if (board[0][j - 1] == '1' && board[0][j] == '1') { + uf.union(dots[0][j - 1], dots[0][j]); + } + } + for (int i = 1; i < row; i++) { + if (board[i - 1][0] == '1' && board[i][0] == '1') { + uf.union(dots[i - 1][0], dots[i][0]); + } + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + if (board[i][j] == '1') { + if (board[i][j - 1] == '1') { + uf.union(dots[i][j - 1], dots[i][j]); + } + if (board[i - 1][j] == '1') { + uf.union(dots[i - 1][j], dots[i][j]); + } + } + } + } + return uf.sets(); + } + + public static class Dot { + + } + + public static class Node { + + V value; + + public Node(V v) { + value = v; + } + + } + + public static class UnionFind1 { + public HashMap> nodes; + public HashMap, Node> parents; + public HashMap, Integer> sizeMap; + + public UnionFind1(List values) { + nodes = new HashMap<>(); + parents = new HashMap<>(); + sizeMap = new HashMap<>(); + for (V cur : values) { + Node node = new Node<>(cur); + nodes.put(cur, node); + parents.put(node, node); + sizeMap.put(node, 1); + } + } + + public Node findFather(Node cur) { + Stack> path = new Stack<>(); + while (cur != parents.get(cur)) { + path.push(cur); + cur = parents.get(cur); + } + while (!path.isEmpty()) { + parents.put(path.pop(), cur); + } + return cur; + } + + public void union(V a, V b) { + Node aHead = findFather(nodes.get(a)); + Node bHead = findFather(nodes.get(b)); + if (aHead != bHead) { + int aSetSize = sizeMap.get(aHead); + int bSetSize = sizeMap.get(bHead); + Node big = aSetSize >= bSetSize ? aHead : bHead; + Node small = big == aHead ? bHead : aHead; + parents.put(small, big); + sizeMap.put(big, aSetSize + bSetSize); + sizeMap.remove(small); + } + } + + public int sets() { + return sizeMap.size(); + } + + } + + public static int numIslands2(char[][] board) { + int row = board.length; + int col = board[0].length; + UnionFind2 uf = new UnionFind2(board); + for (int j = 1; j < col; j++) { + if (board[0][j - 1] == '1' && board[0][j] == '1') { + uf.union(0, j - 1, 0, j); + } + } + for (int i = 1; i < row; i++) { + if (board[i - 1][0] == '1' && board[i][0] == '1') { + uf.union(i - 1, 0, i, 0); + } + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + if (board[i][j] == '1') { + if (board[i][j - 1] == '1') { + uf.union(i, j - 1, i, j); + } + if (board[i - 1][j] == '1') { + uf.union(i - 1, j, i, j); + } + } + } + } + return uf.sets(); + } + + public static class UnionFind2 { + private int[] parent; + private int[] size; + private int[] help; + private int col; + private int sets; + + public UnionFind2(char[][] board) { + col = board[0].length; + sets = 0; + int row = board.length; + int len = row * col; + parent = new int[len]; + size = new int[len]; + help = new int[len]; + for (int r = 0; r < row; r++) { + for (int c = 0; c < col; c++) { + if (board[r][c] == '1') { + int i = index(r, c); + parent[i] = i; + size[i] = 1; + sets++; + } + } + } + } + + // (r,c) -> i + private int index(int r, int c) { + return r * col + c; + } + + // 原始位置 -> 下标 + private int find(int i) { + int hi = 0; + while (i != parent[i]) { + help[hi++] = i; + i = parent[i]; + } + for (hi--; hi >= 0; hi--) { + parent[help[hi]] = i; + } + return i; + } + + public void union(int r1, int c1, int r2, int c2) { + int i1 = index(r1, c1); + int i2 = index(r2, c2); + int f1 = find(i1); + int f2 = find(i2); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + size[f1] += size[f2]; + parent[f2] = f1; + } else { + size[f2] += size[f1]; + parent[f1] = f2; + } + sets--; + } + } + + public int sets() { + return sets; + } + + } + + // 为了测试 + public static char[][] generateRandomMatrix(int row, int col) { + char[][] board = new char[row][col]; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + board[i][j] = Math.random() < 0.5 ? '1' : '0'; + } + } + return board; + } + + // 为了测试 + public static char[][] copy(char[][] board) { + int row = board.length; + int col = board[0].length; + char[][] ans = new char[row][col]; + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + ans[i][j] = board[i][j]; + } + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int row = 0; + int col = 0; + char[][] board1 = null; + char[][] board2 = null; + char[][] board3 = null; + long start = 0; + long end = 0; + + row = 1000; + col = 1000; + board1 = generateRandomMatrix(row, col); + board2 = copy(board1); + board3 = copy(board1); + + System.out.println("感染方法、并查集(map实现)、并查集(数组实现)的运行结果和运行时间"); + System.out.println("随机生成的二维矩阵规模 : " + row + " * " + col); + + start = System.currentTimeMillis(); + System.out.println("感染方法的运行结果: " + numIslands3(board1)); + end = System.currentTimeMillis(); + System.out.println("感染方法的运行时间: " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + System.out.println("并查集(map实现)的运行结果: " + numIslands1(board2)); + end = System.currentTimeMillis(); + System.out.println("并查集(map实现)的运行时间: " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + System.out.println("并查集(数组实现)的运行结果: " + numIslands2(board3)); + end = System.currentTimeMillis(); + System.out.println("并查集(数组实现)的运行时间: " + (end - start) + " ms"); + + System.out.println(); + + row = 10000; + col = 10000; + board1 = generateRandomMatrix(row, col); + board3 = copy(board1); + System.out.println("感染方法、并查集(数组实现)的运行结果和运行时间"); + System.out.println("随机生成的二维矩阵规模 : " + row + " * " + col); + + start = System.currentTimeMillis(); + System.out.println("感染方法的运行结果: " + numIslands3(board1)); + end = System.currentTimeMillis(); + System.out.println("感染方法的运行时间: " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + System.out.println("并查集(数组实现)的运行结果: " + numIslands2(board3)); + end = System.currentTimeMillis(); + System.out.println("并查集(数组实现)的运行时间: " + (end - start) + " ms"); + + } + +} diff --git a/MCA算法突击课/第02期/mca_01/Problem01_WaterKing.java b/MCA算法突击课/第02期/mca_01/Problem01_WaterKing.java new file mode 100644 index 0000000..a7a91f7 --- /dev/null +++ b/MCA算法突击课/第02期/mca_01/Problem01_WaterKing.java @@ -0,0 +1,32 @@ +package 第02期.mca_01; + +// 找到数组中的水王数 +// 本题来自,大厂刷题班,23节 +public class Problem01_WaterKing { + + public static int waterKing(int[] arr) { + int cand = 0; + int hp = 0; + for (int i = 0; i < arr.length; i++) { + if (hp == 0) { + cand = arr[i]; + hp = 1; + } else if (arr[i] == cand) { + hp++; + } else { + hp--; + } + } + if (hp == 0) { + return -1; + } + hp = 0; + for (int i = 0; i < arr.length; i++) { + if (arr[i] == cand) { + hp++; + } + } + return hp > arr.length / 2 ? cand : -1; + } + +} diff --git a/MCA算法突击课/第02期/mca_01/Problem02_InsertDeleteGetRandom.java b/MCA算法突击课/第02期/mca_01/Problem02_InsertDeleteGetRandom.java new file mode 100644 index 0000000..acb32c2 --- /dev/null +++ b/MCA算法突击课/第02期/mca_01/Problem02_InsertDeleteGetRandom.java @@ -0,0 +1,52 @@ +package 第02期.mca_01; + +import java.util.HashMap; + +// 测试链接 : https://leetcode.com/problems/insert-delete-getrandom-o1/ +public class Problem02_InsertDeleteGetRandom { + + public class RandomizedSet { + + private HashMap keyIndexMap; + private HashMap indexKeyMap; + private int size; + + public RandomizedSet() { + keyIndexMap = new HashMap(); + indexKeyMap = new HashMap(); + size = 0; + } + + public boolean insert(int val) { + if (!keyIndexMap.containsKey(val)) { + keyIndexMap.put(val, size); + indexKeyMap.put(size++, val); + return true; + } + return false; + } + + public boolean remove(int val) { + if (keyIndexMap.containsKey(val)) { + int deleteIndex = keyIndexMap.get(val); + int lastIndex = --size; + int lastKey = indexKeyMap.get(lastIndex); + keyIndexMap.put(lastKey, deleteIndex); + indexKeyMap.put(deleteIndex, lastKey); + keyIndexMap.remove(val); + indexKeyMap.remove(lastIndex); + return true; + } + return false; + } + + public int getRandom() { + if (size == 0) { + return -1; + } + int randomIndex = (int) (Math.random() * size); + return indexKeyMap.get(randomIndex); + } + } + +} diff --git a/MCA算法突击课/第02期/mca_01/Problem03_LinkedListCycleII.java b/MCA算法突击课/第02期/mca_01/Problem03_LinkedListCycleII.java new file mode 100644 index 0000000..3fb4963 --- /dev/null +++ b/MCA算法突击课/第02期/mca_01/Problem03_LinkedListCycleII.java @@ -0,0 +1,32 @@ +package 第02期.mca_01; + +public class Problem03_LinkedListCycleII { + + // 这个类不用提交 + public static class ListNode { + public int val; + public ListNode next; + } + + public static ListNode detectCycle(ListNode head) { + if (head == null || head.next == null || head.next.next == null) { + return null; + } + ListNode slow = head.next; + ListNode fast = head.next.next; + while (slow != fast) { + if (fast.next == null || fast.next.next == null) { + return null; + } + fast = fast.next.next; + slow = slow.next; + } + fast = head; + while (slow != fast) { + slow = slow.next; + fast = fast.next; + } + return slow; + } + +} diff --git a/MCA算法突击课/第02期/mca_01/Problem04_MinSwapStep.java b/MCA算法突击课/第02期/mca_01/Problem04_MinSwapStep.java new file mode 100644 index 0000000..b3d46cd --- /dev/null +++ b/MCA算法突击课/第02期/mca_01/Problem04_MinSwapStep.java @@ -0,0 +1,74 @@ +package 第02期.mca_01; + +public class Problem04_MinSwapStep { + + // 一个数组中只有两种字符'G'和'B', + // 可以让所有的G都放在左侧,所有的B都放在右侧 + // 或者可以让所有的G都放在右侧,所有的B都放在左侧 + // 但是只能在相邻字符之间进行交换操作,请问请问至少需要交换几次, + public static int minSteps1(String s) { + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + int step1 = 0; + int gi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'G') { + step1 += i - (gi++); + } + } + int step2 = 0; + int bi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'B') { + step2 += i - (bi++); + } + } + return Math.min(step1, step2); + } + + // 可以让G在左,或者在右 + public static int minSteps2(String s) { + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + int step1 = 0; + int step2 = 0; + int gi = 0; + int bi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'G') { // 当前的G,去左边 方案1 + step1 += i - (gi++); + } else {// 当前的B,去左边 方案2 + step2 += i - (bi++); + } + } + return Math.min(step1, step2); + } + + // 为了测试 + public static String randomString(int maxLen) { + char[] str = new char[(int) (Math.random() * maxLen)]; + for (int i = 0; i < str.length; i++) { + str[i] = Math.random() < 0.5 ? 'G' : 'B'; + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int maxLen = 100; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + String str = randomString(maxLen); + int ans1 = minSteps1(str); + int ans2 = minSteps2(str); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + } +} \ No newline at end of file diff --git a/MCA算法突击课/第02期/即将开始/package-info.java b/MCA算法突击课/第02期/即将开始/package-info.java deleted file mode 100644 index 546f5a8..0000000 --- a/MCA算法突击课/第02期/即将开始/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package 第02期.即将开始; \ No newline at end of file