diff --git a/notes/Java IO.md b/notes/Java IO.md index 351c300d..78cc7814 100644 --- a/notes/Java IO.md +++ b/notes/Java IO.md @@ -35,17 +35,21 @@ Java 的 I/O 大概可以分成以下几类: # 二、磁盘操作 -File 类可以用于表示文件和目录,但是它只用于表示文件的信息,而不表示文件的内容。 +File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。 递归地输出一个目录下所有文件: ```java -public static void listAllFiles(File dir) { +public static void listAllFiles(File dir) +{ + if (dir == null || !dir.exists()) { + return; + } if (dir.isFile()) { System.out.println(dir.getName()); return; } - for (File file : dir.listFiles()) { + for (File file : Objects.requireNonNull(dir.listFiles())) { listAllFiles(file); } } @@ -56,12 +60,13 @@ public static void listAllFiles(File dir) { 使用字节流操作进行文件复制: ```java -public static void copyFile(String src, String dist) throws IOException { - FileInputStream in = new FileInputStream("file/1.txt"); - FileOutputStream out = new FileOutputStream("file/2.txt"); +public static void copyFile(String src, String dist) throws IOException +{ + FileInputStream in = new FileInputStream(src); + FileOutputStream out = new FileOutputStream(dist); byte[] buffer = new byte[20 * 1024]; - // read() 最多读取 buffer.length 个字节,返回的是实际读取的个数 - // 返回 -1 的时候表示读到 eof,即文件尾 + /* read() 最多读取 buffer.length 个字节 + 返回的是实际读取的个数,返回 -1 的时候表示读到 eof,即文件尾 */ while (in.read(buffer, 0, buffer.length) != -1) { out.write(buffer); } @@ -87,7 +92,8 @@ DataInputStream 装饰者提供了对更多数据类型进行输入的操作, 不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。 -InputStreamReader 实现从文本文件的字节流解码成字符流;OutputStreamWriter 实现字符流编码成为文本文件的字节流。 +- InputStreamReader 实现从文本文件的字节流解码成字符流; +- OutputStreamWriter 实现字符流编码成为文本文件的字节流。 逐行输出文本文件的内容: @@ -98,8 +104,9 @@ String line; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } -// 装饰者模式使得 BufferedReader 组合了一个 Reader 对象 -// 在调用 BufferedReader 的 close() 方法时会去调用 fileReader 的 close() 方法,因此只要一个 close() 调用即可 +/* 装饰者模式使得 BufferedReader 组合了一个 Reader 对象 + 在调用 BufferedReader 的 close() 方法时会去调用 fileReader 的 close() 方法 + 因此只要一个 close() 调用即可 */ bufferedReader.close(); ``` @@ -140,31 +147,35 @@ byte[] bytes = str1.getBytes(); 序列化的类需要实现 Serializable 接口,它只是一个标准,没有任何方法需要实现,但是如果不去实现它的话而进行序列化,会抛出异常。 ```java -public static void main(String[] args) throws IOException, ClassNotFoundException { +public static void main(String[] args) throws IOException, ClassNotFoundException +{ A a1 = new A(123, "abc"); String objectFile = "file/a1"; ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFile)); objectOutputStream.writeObject(a1); objectOutputStream.close(); + ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(objectFile)); A a2 = (A) objectInputStream.readObject(); objectInputStream.close(); System.out.println(a2); } -private static class A implements Serializable { +private static class A implements Serializable +{ private int x; private String y; - A(int x, String y) { + A(int x, String y) + { this.x = x; this.y = y; } @Override - public String toString() { - return "x = " + x + - " " + "y = " + y; + public String toString() + { + return "x = " + x + " " + "y = " + y; } } ``` @@ -202,16 +213,19 @@ InetAddress.getByAddress(byte[] address); 可以直接从 URL 中读取字节流数据。 ```java -URL url = new URL("http://www.baidu.com"); -InputStream is = url.openStream(); // 字节流 -InputStreamReader isr = new InputStreamReader(is, "utf-8"); // 字符流 -BufferedReader br = new BufferedReader(isr); -String line = br.readLine(); -while (line != null) { - System.out.println(line); - line = br.readLine(); +public static void main(String[] args) throws IOException +{ + URL url = new URL("http://www.baidu.com"); + InputStream is = url.openStream(); /* 字节流 */ + InputStreamReader isr = new InputStreamReader(is, "utf-8"); /* 字符流 */ + BufferedReader br = new BufferedReader(isr); + String line = br.readLine(); + while (line != null) { + System.out.println(line); + line = br.readLine(); + } + br.close(); } -br.close(); ``` ## Sockets diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md index f4927da8..9472f962 100644 --- a/notes/剑指 offer 题解.md +++ b/notes/剑指 offer 题解.md @@ -2526,7 +2526,8 @@ public ArrayList> FindContinuousSequence(int sum) { 正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。 ```java -public String ReverseSentence(String str) { +public String ReverseSentence(String str) +{ int n = str.length(); char[] chars = str.toCharArray(); int i = 0, j = 0; @@ -2541,12 +2542,14 @@ public String ReverseSentence(String str) { return new String(chars); } -private void reverse(char[] c, int i, int j) { +private void reverse(char[] c, int i, int j) +{ while (i < j) swap(c, i++, j--); } -private void swap(char[] c, int i, int j) { +private void swap(char[] c, int i, int j) +{ char t = c[i]; c[i] = c[j]; c[j] = t; @@ -2566,7 +2569,8 @@ private void swap(char[] c, int i, int j) { 将 "abcXYZdef" 旋转左移三位,可以先将 "abc" 和 "XYZdef" 分别旋转,得到 "cbafedZYX",然后再把整个字符串旋转得到 "XYZdefabc"。 ```java -public String LeftRotateString(String str, int n) { +public String LeftRotateString(String str, int n) +{ if (n >= str.length()) return str; char[] chars = str.toCharArray(); @@ -2576,12 +2580,14 @@ public String LeftRotateString(String str, int n) { return new String(chars); } -private void reverse(char[] chars, int i, int j) { +private void reverse(char[] chars, int i, int j) +{ while (i < j) swap(chars, i++, j--); } -private void swap(char[] chars, int i, int j) { +private void swap(char[] chars, int i, int j) +{ char t = chars[i]; chars[i] = chars[j]; chars[j] = t; @@ -2599,15 +2605,16 @@ private void swap(char[] chars, int i, int j) { ## 解题思路 ```java -public ArrayList maxInWindows(int[] num, int size) { +public ArrayList maxInWindows(int[] num, int size) +{ ArrayList ret = new ArrayList<>(); - PriorityQueue heap = new PriorityQueue((o1, o2) -> o2 - o1); if (size > num.length || size < 1) return ret; + PriorityQueue heap = new PriorityQueue<>((o1, o2) -> o2 - o1); /* 大顶堆 */ for (int i = 0; i < size; i++) heap.add(num[i]); ret.add(heap.peek()); - for (int i = 1, j = i + size - 1; j < num.length; i++, j++) { + for (int i = 1, j = i + size - 1; j < num.length; i++, j++) { /* 维护一个大小为 size 的大顶堆 */ heap.remove(num[i - 1]); heap.add(num[j]); ret.add(heap.peek()); @@ -2633,15 +2640,17 @@ public ArrayList maxInWindows(int[] num, int size) { 空间复杂度:O(N2) ```java -public List> dicesSum(int n) { +public List> dicesSum(int n) +{ final int face = 6; final int pointNum = face * n; long[][] dp = new long[n + 1][pointNum + 1]; + for (int i = 1; i <= face; i++) dp[1][i] = 1; for (int i = 2; i <= n; i++) - for (int j = i; j <= pointNum; j++) // 使用 i 个骰子最小点数为 i + for (int j = i; j <= pointNum; j++) /* 使用 i 个骰子最小点数为 i */ for (int k = 1; k <= face && k <= j; k++) dp[i][j] += dp[i - 1][j - k]; @@ -2649,6 +2658,7 @@ public List> dicesSum(int n) { List> ret = new ArrayList<>(); for (int i = n; i <= pointNum; i++) ret.add(new AbstractMap.SimpleEntry<>(i, dp[n][i] / totalNum)); + return ret; } ``` @@ -2658,24 +2668,30 @@ public List> dicesSum(int n) { 空间复杂度:O(N) ```java -public List> dicesSum(int n) { +public List> dicesSum(int n) +{ final int face = 6; final int pointNum = face * n; long[][] dp = new long[2][pointNum + 1]; + for (int i = 1; i <= face; i++) dp[0][i] = 1; - int flag = 1; + + int flag = 1; /* 旋转标记 */ for (int i = 2; i <= n; i++, flag = 1 - flag) { for (int j = 0; j <= pointNum; j++) - dp[flag][j] = 0; // 旋转数组清零 - for (int j = i; j <= pointNum; j++) // 使用 i 个骰子最小点数为 i + dp[flag][j] = 0; /* 旋转数组清零 */ + + for (int j = i; j <= pointNum; j++) for (int k = 1; k <= face && k <= j; k++) dp[flag][j] += dp[1 - flag][j - k]; } + final double totalNum = Math.pow(6, n); List> ret = new ArrayList<>(); for (int i = n; i <= pointNum; i++) ret.add(new AbstractMap.SimpleEntry<>(i, dp[1 - flag][i] / totalNum)); + return ret; } ``` @@ -2691,18 +2707,20 @@ public List> dicesSum(int n) { ## 解题思路 ```java -public boolean isContinuous(int[] nums) { +public boolean isContinuous(int[] nums) +{ if (nums.length < 5) return false; Arrays.sort(nums); int cnt = 0; - for (int num : nums) + for (int num : nums) /* 统计癞子数量 */ if (num == 0) cnt++; + for (int i = cnt; i < nums.length - 1; i++) { if (nums[i + 1] == nums[i]) return false; - cnt -= nums[i + 1] - nums[i] - 1; + cnt -= nums[i + 1] - nums[i] - 1; /* 使用癞子去补全不连续的顺子 */ } return cnt >= 0; } @@ -2721,10 +2739,11 @@ public boolean isContinuous(int[] nums) { 约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。 ```java -public int LastRemaining_Solution(int n, int m) { - if (n == 0) +public int LastRemaining_Solution(int n, int m) +{ + if (n == 0) /* 特殊输入的处理 */ return -1; - if (n == 1) + if (n == 1) /* 返回条件 */ return 0; return (LastRemaining_Solution(n - 1, m) + m) % n; } @@ -2743,13 +2762,13 @@ public int LastRemaining_Solution(int n, int m) { 使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该在 i 之前并且价格最低。 ```java -public int maxProfit(int[] prices) { +public int maxProfit(int[] prices) +{ if (prices == null || prices.length == 0) return 0; - int n = prices.length; int soFarMin = prices[0]; int maxProfit = 0; - for (int i = 1; i < n; i++) { + for (int i = 1; i < prices.length; i++) { soFarMin = Math.min(soFarMin, prices[i]); maxProfit = Math.max(maxProfit, prices[i] - soFarMin); } @@ -2774,7 +2793,8 @@ public int maxProfit(int[] prices) { 以下实现中,递归的返回条件为 n <= 0,取非后就是 n > 0,递归的主体部分为 sum += Sum_Solution(n - 1),转换为条件语句后就是 (sum += Sum_Solution(n - 1)) > 0。 ```java -public int Sum_Solution(int n) { +public int Sum_Solution(int n) +{ int sum = n; boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0); return sum; @@ -2796,8 +2816,9 @@ a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进 递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。 ```java -public int Add(int num1, int num2) { - return num2 == 0 ? num1 : Add(num1 ^ num2, (num1 & num2) << 1); +public int Add(int a, int b) +{ + return b == 0 ? a : Add(a ^ b, (a & b) << 1); } ``` @@ -2812,12 +2833,13 @@ public int Add(int num1, int num2) { ## 解题思路 ```java -public int[] multiply(int[] A) { +public int[] multiply(int[] A) +{ int n = A.length; int[] B = new int[n]; - for (int i = 0, product = 1; i < n; product *= A[i], i++) + for (int i = 0, product = 1; i < n; product *= A[i], i++) /* 从左往右累乘 */ B[i] = product; - for (int i = n - 1, product = 1; i >= 0; product *= A[i], i--) + for (int i = n - 1, product = 1; i >= 0; product *= A[i], i--) /* 从右往左累乘 */ B[i] *= product; return B; } @@ -2844,17 +2866,18 @@ Output: ## 解题思路 ```java -public int StrToInt(String str) { +public int StrToInt(String str) +{ if (str == null || str.length() == 0) return 0; boolean isNegative = str.charAt(0) == '-'; int ret = 0; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); - if (i == 0 && (c == '+' || c == '-')) + if (i == 0 && (c == '+' || c == '-')) /* 符号判定 */ continue; - if (c < '0' || c > '9') - return 0; // 非法输入 + if (c < '0' || c > '9') /* 非法输入 */ + return 0; ret = ret * 10 + (c - '0'); } return isNegative ? -ret : ret; @@ -2891,7 +2914,7 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { [Leetcode : 236. Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/) -在左右子树中查找两个节点的最低公共祖先,如果在其中一颗子树中查找到,那么就返回这个解,否则可以认为根节点就是最低公共祖先。 +在左右子树中查找是否存在 p 或者 q,如果 p 和 q 分别在两个子树中,那么就说明根节点就是 LCA。 ```java public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {