diff --git a/notes/Java 基础.md b/notes/Java 基础.md
index 5375ce09..34bd383e 100644
--- a/notes/Java 基础.md
+++ b/notes/Java 基础.md
@@ -269,7 +269,7 @@ set.add(e2);
System.out.println(set.size()); // 2
```
-理想的散列函数应当具有均匀性,即不相等的实例应当均匀分不到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来,可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。
+理想的散列函数应当具有均匀性,即不相等的实例应当均匀分布到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来,可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。
一个数与 31 相乘可以转换成移位和减法:31\*x == (x<<5)-x。
@@ -506,7 +506,7 @@ protected 用于修饰成员,表示在继承体系中成员对于子类可见
设计良好的模块会隐藏所有的实现细节,把它的 API 与它的实现清晰地隔离开来。模块之间只通过它们的 API 进行通信,一个模块不需要知道其他模块的内部工作情况,这个概念被称为信息隐藏或封装。因此访问权限应当尽可能地使每个类或者成员不被外界访问。
-如果子类的方法覆盖了父类的方法,那么子类中该方法的访问级别不允许低于父类的访问级别。这是为了确保可以使用父类实例的地方都可以使用子类实例,也就是确保满足里式替换原则。
+如果子类的方法覆盖了父类的方法,那么子类中该方法的访问级别不允许低于父类的访问级别。这是为了确保可以使用父类实例的地方都可以使用子类实例,也就是确保满足里氏替换原则。
字段决不能是公有的,因为这么做的话就失去了对这个字段修改行为的控制,客户端可以对其随意修改。可以使用公有的 getter 和 setter 方法来替换公有字段。
diff --git a/notes/Java 并发.md b/notes/Java 并发.md
index edc9dc0f..583df5aa 100644
--- a/notes/Java 并发.md
+++ b/notes/Java 并发.md
@@ -715,10 +715,10 @@ java.util.concurrent(J.U.C)大大提高了并发性能,AQS 被认为是 J.
public class CountdownLatchExample {
public static void main(String[] args) throws InterruptedException {
- final int totalTread = 10;
- CountDownLatch countDownLatch = new CountDownLatch(totalTread);
+ final int totalThread = 10;
+ CountDownLatch countDownLatch = new CountDownLatch(totalThread);
ExecutorService executorService = Executors.newCachedThreadPool();
- for (int i = 0; i < totalTread; i++) {
+ for (int i = 0; i < totalThread; i++) {
executorService.execute(() -> {
System.out.print("run..");
countDownLatch.countDown();
@@ -747,12 +747,11 @@ run..run..run..run..run..run..run..run..run..run..end
```java
public class CyclicBarrierExample {
-
public static void main(String[] args) throws InterruptedException {
- final int totalTread = 10;
- CyclicBarrier cyclicBarrier = new CyclicBarrier(totalTread);
+ final int totalThread = 10;
+ CyclicBarrier cyclicBarrier = new CyclicBarrier(totalThread);
ExecutorService executorService = Executors.newCachedThreadPool();
- for (int i = 0; i < totalTread; i++) {
+ for (int i = 0; i < totalThread; i++) {
executorService.execute(() -> {
System.out.print("before..");
try {
@@ -1507,7 +1506,7 @@ public class ThreadLocalExample1 {
-每个 Thread 都有一个 TreadLocal.ThreadLocalMap 对象,Thread 类中就定义了 ThreadLocal.ThreadLocalMap 成员。
+每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象,Thread 类中就定义了 ThreadLocal.ThreadLocalMap 成员。
```java
/* ThreadLocal values pertaining to this thread. This map is maintained
@@ -1595,7 +1594,7 @@ public static String concatString(String s1, String s2, String s3) {
## 轻量级锁
-轻量级锁是 JDK 1.6 之中加入的新型锁机制,它名字中的“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的,因此传统的锁机制就称为“重量级”锁。首先需要强调一点的是,轻量级锁并不是用来代替重要级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。
+轻量级锁是 JDK 1.6 之中加入的新型锁机制,它名字中的“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的,因此传统的锁机制就称为“重量级”锁。首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。
要理解轻量级锁,以及后面会讲到的偏向锁的原理和运作过程,必须从 HotSpot 虚拟机的对象(对象头部分)的内存布局开始介绍。HotSpot 虚拟机的对象头(Object Header)分为两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄(Generational GC Age)等,这部分数据是长度在 32 位和 64 位的虚拟机中分别为 32 bit 和 64 bit,官方称它为“Mark Word”,它是实现轻量级锁和偏向锁的关键。另外一部分用于存储指向方法区对象类型数据的指针,如果是数组对象的话,还会有一个额外的部分用于存储数组长度。
diff --git a/notes/Java 虚拟机.md b/notes/Java 虚拟机.md
index 852e171c..4b72b865 100644
--- a/notes/Java 虚拟机.md
+++ b/notes/Java 虚拟机.md
@@ -123,7 +123,7 @@ objB.instance = objA;
### 2. 可达性
-通过 GC Roots 作为起始点进行搜索,能够到达到的对象都是都是可用的,不可达的对象可被回收。
+通过 GC Roots 作为起始点进行搜索,能够到达到的对象都是可用的,不可达的对象可被回收。
diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md
index 3520557c..0292fb99 100644
--- a/notes/Leetcode 题解.md
+++ b/notes/Leetcode 题解.md
@@ -3465,7 +3465,7 @@ class MinStack {
public void pop() {
dataStack.pop();
minStack.pop();
- min = minStack.isEmpty() ? min = Integer.MAX_VALUE : minStack.peek();
+ min = minStack.isEmpty() ? Integer.MAX_VALUE : minStack.peek();
}
public int top() {
@@ -3710,8 +3710,8 @@ s = "rat", t = "car", return false.
```java
public boolean isAnagram(String s, String t) {
int[] cnts = new int[26];
- for(int i = 0; i < s.length(); i++) cnts[s.charAt(i) - 'a'] ++;
- for(int i = 0; i < t.length(); i++) cnts[t.charAt(i) - 'a'] --;
+ for(int i = 0; i < s.length(); i++) cnts[s.charAt(i) - 'a']++;
+ for(int i = 0; i < t.length(); i++) cnts[t.charAt(i) - 'a']--;
for(int i = 0; i < 26; i++) if(cnts[i] != 0) return false;
return true;
}
@@ -5719,19 +5719,21 @@ x ^ 1s = ~x x & 1s = x x | 1s = 1s
x ^ x = 0 x & x = x x | x = x
```
-- 利用 x ^ 1s = \~x 的特点,可以将位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数;
-- 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask :00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位;
-- 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1 。
+- 利用 x ^ 1s = \~x 的特点,可以将位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数。
+- 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask :00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。
+- 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。
-\>\> n 为算术右移,相当于除以 2n;
-\>\>\> n 为无符号右移,左边会补上 0。
-<< n 为算术左移,相当于乘以 2n。
+位与运算技巧:
-n&(n-1) 该位运算是去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110 **100** ,减去 1 得到 10110**011**,这两个数相与得到 10110**000**。
+- n&(n-1) 去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110 **100** ,减去 1 得到 10110**011**,这两个数相与得到 10110**000**。
+- n-n&(\~n+1) 去除 n 的位级表示中最高的那一位。
+- n&(-n) 得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110 **100** ,-n 得到 01001**100**,相与得到 00000**100**
-n-n&(\~n+1) 运算是去除 n 的位级表示中最高的那一位。
+移位运算:
-n&(-n) 该运算得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110 **100** ,-n 得到 01001**100**,相与得到 00000**100**
+- \>\> n 为算术右移,相当于除以 2n;
+- \>\>\> n 为无符号右移,左边会补上 0。
+- << n 为算术左移,相当于乘以 2n。
**2. mask 计算**
@@ -5743,57 +5745,7 @@ n&(-n) 该运算得到 n 的位级表示中最低的那一位。-n 得到 n 的
要得到 1 到 i 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 \~(1<<(i+1)-1)。
-**3. 位操作举例**
-
-① 获取第 i 位
-
-num & 00010000 != 0
-
-```java
-(num & (1 << i)) != 0;
-```
-
-② 将第 i 位设置为 1
-
-num | 00010000
-
-```java
-num | (1 << i);
-```
-
-③ 将第 i 位清除为 0
-
-num & 11101111
-
-```java
-num & (~(1 << i))
-```
-
-④ 将最高位到第 i 位清除为 0
-
-num & 00001111
-
-```java
-num & ((1 << i) - 1);
-```
-
-⑤ 将第 0 位到第 i 位清除为 0
-
-num & 11110000
-
-```java
-num & (~((1 << (i+1)) - 1));
-```
-
-⑥ 将第 i 位设置为 0 或者 1
-
-先将第 i 位清零,然后将 v 左移 i 位,执行“位或”运算。
-
-```java
-(num & (1 << i)) | (v << i);
-```
-
-**4. Java 中的位操作**
+**3. Java 中的位操作**
```html
static int Integer.bitCount(); // 统计 1 的数量
@@ -5805,6 +5757,19 @@ static String toBinaryString(int i); // 转换为二进制表示的字符串
[Leetcode : 461. Hamming Distance (Easy)](https://leetcode.com/problems/hamming-distance/)
+```html
+Input: x = 1, y = 4
+
+Output: 2
+
+Explanation:
+1 (0 0 0 1)
+4 (0 1 0 0)
+ ↑ ↑
+
+The above arrows point to positions where the corresponding bits are different.
+```
+
对两个数进行异或操作,位级表示不同的那一位为 1,统计有多少个 1 即可。
```java
@@ -5819,6 +5784,20 @@ public int hammingDistance(int x, int y) {
}
```
+使用 z&(z-1) 去除 z 位级表示最低的那一位。
+
+```java
+public int hammingDistance(int x, int y) {
+ int z = x ^ y;
+ int cnt = 0;
+ while (z != 0) {
+ z &= (z - 1);
+ cnt++;
+ }
+ return cnt;
+}
+```
+
可以使用 Integer.bitcount() 来统计 1 个的个数。
```java
@@ -5827,6 +5806,25 @@ public int hammingDistance(int x, int y) {
}
```
+**数组中唯一一个不重复的元素**
+
+[Leetcode : 136. Single Number (Easy)](https://leetcode.com/problems/single-number/description/)
+
+```html
+Input: [4,1,2,1,2]
+Output: 4
+```
+
+两个相同的数异或的结果为 0,对所有数进行异或操作,最后的结果就是单独出现的那个数。
+
+```java
+public int singleNumber(int[] nums) {
+ int ret = 0;
+ for (int n : nums) ret = ret ^ n;
+ return ret;
+}
+```
+
**找出数组中缺失的那个数**
[Leetcode : 268. Missing Number (Easy)](https://leetcode.com/problems/missing-number/description/)
@@ -5837,13 +5835,37 @@ Output: 2
```
题目描述:数组元素在 0-n 之间,但是有一个数是缺失的,要求找到这个缺失的数。
- `
```java
public int missingNumber(int[] nums) {
int ret = 0;
- for (int i = 0; i <= nums.length; i++) {
- ret = i == nums.length ? ret ^ i : ret ^ i ^ nums[i];
+ for (int i = 0; i < nums.length; i++) {
+ ret = ret ^ i ^ nums[i];
+ }
+ return ret ^ nums.length;
+}
+```
+
+**数组中不重复的两个元素**
+
+[Leetcode : 260. Single Number III (Medium)](https://leetcode.com/problems/single-number-iii/description/)
+
+两个不相等的元素在位级表示上必定会有一位存在不同。
+
+将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。
+
+diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
+
+```java
+public int[] singleNumber(int[] nums) {
+ int diff = 0;
+ for (int num : nums) diff ^= num;
+ // 得到最右一位
+ diff &= -diff;
+ int[] ret = new int[2];
+ for (int num : nums) {
+ if ((num & diff) == 0) ret[0] ^= num;
+ else ret[1] ^= num;
}
return ret;
}
@@ -5906,8 +5928,6 @@ b = a ^ b;
a = a ^ b;
```
-令 c = a ^ b,那么 b ^ c = b ^ b ^ a = a,a ^ c = a ^ a ^ b = b。
-
**判断一个数是不是 2 的 n 次方**
[Leetcode : 231. Power of Two (Easy)](https://leetcode.com/problems/power-of-two/description/)
@@ -5932,24 +5952,7 @@ public boolean isPowerOfTwo(int n) {
[Leetcode : 342. Power of Four (Easy)](https://leetcode.com/problems/power-of-four/)
-该数二进制表示有且只有一个奇数位为 1 ,其余的都为 0 ,例如 16 :10000。可以每次把 1 向左移动 2 位,就能构造出这种数字,然后比较构造出来的数与要判断的数是否相同。
-
-```java
-public boolean isPowerOfFour(int num) {
- int i = 1;
- while(i > 0){
- if(i == num) return true;
- i = i << 2;
- }
- return false;
-}
-```
-
-```java
-public boolean isPowerOfFour(int num) {
- return Integer.toString(num, 4).matches("10*");
-}
-```
+这种数在二进制表示中有且只有一个奇数位为 1,例如 16(10000)。
```java
public boolean isPowerOfFour(int num) {
@@ -5957,52 +5960,32 @@ public boolean isPowerOfFour(int num) {
}
```
-**数组中唯一一个不重复的元素**
-
-[Leetcode : 136. Single Number (Easy)](https://leetcode.com/problems/single-number/description/)
-
-两个相同的数异或的结果为 0,对所有数进行异或操作,最后的结果就是单独出现的那个数。
-
-类似的有:[Leetcode : 389. Find the Difference (Easy)](https://leetcode.com/problems/find-the-difference/description/),两个字符串仅有一个字符不相同,使用异或操作可以以 O(1) 的空间复杂度来求解,而不需要使用 HashSet。
+也可以使用正则表达式进行匹配。
```java
-public int singleNumber(int[] nums) {
- int ret = 0;
- for(int n : nums) ret = ret ^ n;
- return ret;
+public boolean isPowerOfFour(int num) {
+ return Integer.toString(num, 4).matches("10*");
}
```
-**数组中不重复的两个元素**
-
-[Leetcode : 260. Single Number III (Medium)](https://leetcode.com/problems/single-number-iii/description/)
-
-两个不相等的元素在位级表示上必定会有一位存在不同。
-
-将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。
-
-diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
-
-```java
-public int[] singleNumber(int[] nums) {
- int diff = 0;
- for(int num : nums) diff ^= num;
- // 得到最右一位
- diff &= -diff;
- int[] ret = new int[2];
- for(int num : nums) {
- if((num & diff) == 0) ret[0] ^= num;
- else ret[1] ^= num;
- }
- return ret;
-}
-```
**判断一个数的位级表示是否不会出现连续的 0 和 1**
[Leetcode : 693. Binary Number with Alternating Bits (Easy)](https://leetcode.com/problems/binary-number-with-alternating-bits/description/)
-对于 10101 这种位级表示的数,把它向右移动 1 位得到 1010 ,这两个数每个位都不同,因此异或得到的结果为 11111。
+```html
+Input: 10
+Output: True
+Explanation:
+The binary representation of 10 is: 1010.
+
+Input: 11
+Output: False
+Explanation:
+The binary representation of 11 is: 1011.
+```
+
+对于 1010 这种位级表示的数,把它向右移动 1 位得到 101,这两个数每个位都不同,因此异或得到的结果为 1111。
```java
public boolean hasAlternatingBits(int n) {
@@ -6021,15 +6004,15 @@ Output: 2
Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2.
```
-不考虑二进制表示中的首 0 部分。
+题目描述:不考虑二进制表示中的首 0 部分。
对于 00000101,要求补码可以将它与 00000111 进行异或操作。那么问题就转换为求掩码 00000111。
```java
public int findComplement(int num) {
- if(num == 0) return 1;
+ if (num == 0) return 1;
int mask = 1 << 30;
- while((num & mask) == 0) mask >>= 1;
+ while ((num & mask) == 0) mask >>= 1;
mask = (mask << 1) - 1;
return num ^ mask;
}
@@ -6039,7 +6022,7 @@ public int findComplement(int num) {
```java
public int findComplement(int num) {
- if(num == 0) return 1;
+ if (num == 0) return 1;
int mask = Integer.highestOneBit(num);
mask = (mask << 1) - 1;
return num ^ mask;
@@ -6070,7 +6053,9 @@ public int findComplement(int num) {
[Leetcode : 371. Sum of Two Integers (Easy)](https://leetcode.com/problems/sum-of-two-integers/description/)
-a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。
+a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。
+
+递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。
```java
public int getSum(int a, int b) {
@@ -6090,12 +6075,11 @@ The two words can be "abcw", "xtfn".
题目描述:字符串数组的字符串只含有小写字符。求解字符串数组中两个字符串长度的最大乘积,要求这两个字符串不能含有相同字符。
-解题思路:本题主要问题是判断两个字符串是否含相同字符,由于字符串只含有小写字符,总共 26 位,因此可以用一个 32 位的整数来存储每个字符是否出现过。
+本题主要问题是判断两个字符串是否含相同字符,由于字符串只含有小写字符,总共 26 位,因此可以用一个 32 位的整数来存储每个字符是否出现过。
```java
public int maxProduct(String[] words) {
int n = words.length;
- if (n == 0) return 0;
int[] val = new int[n];
for (int i = 0; i < n; i++) {
for (char c : words[i].toCharArray()) {
@@ -6118,7 +6102,7 @@ public int maxProduct(String[] words) {
[Leetcode : 338. Counting Bits (Medium)](https://leetcode.com/problems/counting-bits/description/)
-对于数字 6(110),它可以看成是数字 (10) 前面加上一个 1 ,因此 dp[i] = dp[i&(i-1)] + 1;
+对于数字 6(110),它可以看成是 4(100) 再加一个 2(10),因此 dp[i] = dp[i&(i-1)] + 1;
```java
public int[] countBits(int num) {
diff --git a/notes/MySQL.md b/notes/MySQL.md
index 56335138..7adfd878 100644
--- a/notes/MySQL.md
+++ b/notes/MySQL.md
@@ -40,7 +40,7 @@ InnoDB 是 MySQL 默认的事务型存储引擎,只有在需要 InnoDB 不支
## MyISAM
-提供了大量的特性,包括全文索引、压缩表、空间数据索引等。应该注意的是,MySQL 5.6.4 添加了对 InnoDB 引擎的全文索引支持。
+MyISAM 提供了大量的特性,包括全文索引、压缩表、空间数据索引等。应该注意的是,MySQL 5.6.4 也添加了对 InnoDB 引擎的全文索引支持。
不支持事务。