环形数组

循环遍历

i = ( i + 1 ) % cap

i = (i + 1) % cap 是一个常见的用于实现循环遍历数组或环形缓冲区的技巧。
在这个表达式中,i 是当前的索引,cap 是数组或缓冲区的容量。使用 (i + 1) % cap 可以将索引 i 增加1,并在达到容量上限时循环回到索引0。
具体来说,当 i 增加1时,如果超过了容量 cap(i + 1) % cap 将会得到取余后的结果作为新的索引值。如果 i 达到了 cap - 1,则 (i + 1) % cap 的结果为0,从而实现了循环遍历的效果。
这种循环遍历的方式常用于需要按顺序访问数组或缓冲区的场景,而且希望在达到边界时能够循环回到起点。
注意,使用 (i + 1) % cap 进行循环遍历时,要确保 cap 大于0,以避免除以0的错误。

元素索引

nums[i] 的前一个元素是 nums[(i-1)%n] ,下一个元素是 nums[(i + 1) % n]

增强for

遍历二维数组
1
2
3
4
5
6
int[][] nums = {{1, 2, 10}, {3, 5, 20}, {6, 8, 15}};

for (int[] num : nums) {
System.out.println(Arrays.toString(nums));
}

二维数组(矩阵)的坐标映射

只要知道二维数组的的行数 m 和列数 **n**,二维数组的坐标  ==(i, j)==  可以映射成一维的 ==index = i * n + j==;反过来也可以通过一维 index 反解出二维坐标 ==i = index / n, j = index % n==。

扩展:多维坐标之间的映射转换

任何多维数组都可以被映射到一维,所以甭管几维数组,你统一把多维的坐标转化成一维,然后再从一维坐标转化到多维

判断某个字符或字符串是否属于一个特定的字符集合

"+-*/".contains(token) 是一个条件表达式,用于检查字符串 token 是否包含在字符串 “+-*/“ 中。

具体来说,它会返回一个布尔值:

  • 如果 token 包含在 “+-*/“ 字符串中,则返回 true
  • 如果 token 不包含在 “+-*/“ 字符串中,则返回 false

这个表达式通常用于判断某个字符或字符串是否属于一个特定的字符集合。在这个例子中,它可以用于检查 token 是否为 “+”, “-“, “*”, 或 “/“ 中的任意一个。

以下是一个使用示例:

1
2
3
String token = "+";
boolean containsOperator = "+-*/".contains(token);
System.out.println(containsOperator); // 输出: true

在上述示例中,我们定义了一个 token 字符串并赋值为 “+”。然后,我们使用 "+-*/".contains(token) 表达式来检查 token 是否包含在 “+-*/“ 字符串中。

由于 “+” 确实包含在 “+-*/“ 中,所以 containsOperator 的值将为 true。最后,我们将结果打印出来。

通过这样的表达式,我们可以方便地检查一个字符或字符串是否属于某个字符集合,以便根据需要进行相应的处理。

类型转换

Java 提供了一些类型转换方法,用于将字符串转换为不同的数据类型。

以下是一些常见的类型转换示例:

  1. 将字符串转换为整型(int):

    1
    2
    String intValue = "10";
    int i = Integer.parseInt(intValue);
  2. 将字符串转换为浮点数(floatdouble):

    1
    2
    3
    String floatValue = "3.14";
    float f = Float.parseFloat(floatValue);
    double d = Double.parseDouble(floatValue);
  3. 将字符串转换为长整型(long):

    1
    2
    String longValue = "1234567890";
    long l = Long.parseLong(longValue);
  4. 将字符串转换为布尔值(boolean):

    1
    2
    String boolValue = "true";
    boolean b = Boolean.parseBoolean(boolValue);
  5. 将字符串转换为字符(char):

    1
    2
    String charValue = "A";
    char c = charValue.charAt(0); // 取第一个字符

需要注意的是,在进行类型转换时,要确保字符串的格式与目标类型相匹配。否则,可能会抛出相应的异常或得到不正确的结果。

另外,Java 还提供了许多其他类型之间的转换方法,例如整数到字符串的转换、日期到字符串的转换等。这些转换方法可以根据具体的需求选择和使用。

List在转换时不会按照栈的顺序来转换

原因如下:
在 Java 中,Stack 类是继承自 Vector 类的,而 Vector 类实现了 List 接口,因此 Stack 类也可以被视为是一个列表(List)。然而,在 Stack 类中,元素的存储顺序是遵循后进先出(LIFO)的原则,而不是列表(List)的顺序。

当您使用 ArrayList 的构造函数 ArrayList(Collection<? extends E> c) 来将 Stack 转换为 ArrayList 时,实际上是将 Stack 视为一个集合(Collection)来处理。在这种情况下,ArrayList 的构造函数会按照传入集合的迭代顺序来添加元素,而不会考虑集合本身的特殊存储顺序。

因此,当您将 Stack 转换为 ArrayList 时,ArrayList 会按照 Stack 的迭代顺序来添加元素,而不是按照栈的后进先出(LIFO)顺序来添加。这就是为什么转换后的 ArrayList 中的元素顺序并不是栈的顺序的原因。

要按照栈的后进先出的顺序转换为列表,您需要手动将栈中的元素弹出,并按照逆序添加到列表中。这样才能确保列表中的元素顺序符合栈的后进先出顺序。

循环不变量

  • 统一遍历规则:例如左闭右开

Arrays.asList() 的报错

Arrays.asList() 返回的是一个固定大小的列表,它不支持添加或删除元素。当你试图在 threeSum 方法中往 tuple 列表中添加 nums[i] 时,会引发这个异常。

1
for (List<Integer> tuple : tuples) {     tuple.add(nums[i]);  // 这里会导致 UnsupportedOperationException     res.add(tuple); }

为了避免这个问题,你可以在添加元素前,将 tuple 转换为一个可以动态修改的列表,例如 ArrayList。修改后的代码如下:

1
2
3
4
5
for (List<Integer> tuple : tuples) {     
List<Integer> temp = new ArrayList<>(tuple); // 创建一个新的 ArrayList
temp.add(nums[i]); // 现在可以安全地添加元素
res.add(temp);
}