> For the complete documentation index, see [llms.txt](https://blog.mcyou.cc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blog.mcyou.cc/java-xiang-guan/java-yu-suan-fa-jing-sai-zhu-yi-shi-xiang-zhai-lu.md).

# Java与算法竞赛——注意事项摘录

<<<<<<< HEAD

## Java与算法竞赛——语言相关事项摘录 上

***

> 此处摘录总结一些网上看到的用Java打算法竞赛的常用注意事项和语言相关代码优化。虽然记下来不一定考场上记得就是了。上篇记录一些基本输入输出相关内容，下篇主要介绍容器之类相关语言方向要适应的点。

### 一、输入输出

#### 1. 输入

标准输入方法：

`Scanner s = new Scanner (System.in);`

优化输入方法：

`Scanner s = new Scanner (new BufferedInputStream(System.in));`

用流处理在数据量大的时候会快一点。

接下来用`Scanner`对象提供的方法来输入：

* 整数：`int a = s.nextInt()`
* 浮点数：`double t = nextDouble()`
* 字符串：`String str = s.nextInt()`

特别地，可以使用`nextLine()`来读取一整行：

`String str = s.nextLine()`

要判断输入是否结束，可以使用`s.hasNext()`：

```java
while(s.hasNext()){
    int a = s.nextInt();
    ...
}
```

#### 2. 输出

标准输出方法：

`System.out.println(n);`

优化输出方法：

`PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));`

`out.println(n);`

`out.printf("%.2f\n", ans);` (可用类似`printf`的方式输出)

如果要格式化输出，可以使用格式类，如`NumberFormat` 或 `DecimalFormat`类：

```java
	public static void main(String[] args) {  
		NumberFormat   formatter   =   new   DecimalFormat( "000000");   
        String  s  =   formatter.format(-1234.567);     //   -001235    
        formatter   =   new   DecimalFormat( "##");   
        s   =   formatter.format(-1234.567);             //   -1235   
        s   =   formatter.format(0);                      //   0   
        formatter   =   new   DecimalFormat( "##00");   
        s   =   formatter.format(0);                     //   00   
        formatter   =   new   DecimalFormat( ".00");   
        s   =   formatter.format(-.567);               //   -.57   
        formatter   =   new   DecimalFormat( "0.00");   
        s   =   formatter.format(-.567);              //   -0.57   
        formatter   =   new   DecimalFormat( "#.#");   
        s   =   formatter.format(-1234.567);         //   -1234.6   
        formatter   =   new   DecimalFormat( "#.######");   
        s   =   formatter.format(-1234.567);        //   -1234.567   
        formatter   =   new   DecimalFormat( ".######");   
        s   =   formatter.format(-1234.567);       //   -1234.567   
        formatter   =   new   DecimalFormat( "#.000000");   
        s   =   formatter.format(-1234.567);      //   -1234.567000           
        formatter   =   new   DecimalFormat( "#,###,###");   
        s   =   formatter.format(-1234.567);      //   -1,235   
        s   =   formatter.format(-1234567.890);  //   -1,234,568   
    }
```

#### 3. 文件读写

输入

```java
FileInputStream fis = new FileInputStream("b.in");  
System.setIn(fis); 
```

输出

```java
FileInputStream fis = new FileInputStream("b.in");  
System.setIn(fis); 
```

这样就可以用重定向的方式在标准输出流读写文件了。

### 二、高精相关

~~y1s1 Java啥都可以用自带类来解决真的爽~~

在Java中没有提供long long，最大提供到long，高于long数据范围的需使用高精度。不过好在Java中提供了简单的高精度方式，不用像某C语言一样要手写。

Java中主要通过调用`BigInteger`和`BigDecimal`来实现高精度。

```java
        BigInteger a = new BigInteger("123456789");
        BigDecimal b = new BigDecimal("233.333");
        BigInteger c = new BigInteger("123");
        System.out.println(c.add(a)); //加
        System.out.println(a.subtract(c)); //减
        System.out.println(a.multiply(c)); //乘
        System.out.println(b.divide(b)); //除
        System.out.println(a.remainder(c)); //取余数
        System.out.println(a.mod(c)); //取余
        System.out.println(c.pow(3)); //c的3次方
        System.out.println(a.gcd(c)); //求最大公约数
        System.out.println(a.compareTo(c)); //比大小，大于则>0，小于则<0，等于则=0
        long d = a.longValue(); //转换为long
        System.out.println(d);
```

输出

> 123456912 123456666 15185185047 1 90 90 1860867 3 1 123456789

注意：使用这两个类时只能使用其内部的方法来进行加减，不能直接使用运算符。

\#补充：关于取余（remainder)与取模（mod）的区别：

> 例子：
>
> -14 取余 3 = -2
>
> -14 mod 3 = 1

取余运算为数学意义上的取余，其结果可以为负，目的是使商尽可能大的情况下获得余数。

而取模运算返回值一定为正，且要求第二个参数（如本例的3）必须>0。目的是使商尽量小的情况下获得正余数。

> Java默认的%运算符是取余，而python是取模。

### 三、常用容器

#### 1. Set

使用`java.util.Hashtag`来实现。

```java
Set<Integer> s = new HashSet<>();
s.add(233);
System.out.println(s.contains(233)); //true
```

#### 2. Map

使用`java.util.HashMap`来实现

```java
Map<Integer,Integer> m = new HashMap<>();
m.put(1, 233);
m.put(2, 666);
m.remove(1);
System.out.println(m.get(1));
```

因为map是无序的，所以需要使用迭代器来遍历map。

```java
Iterator<Map.Entry<Integer, Integer>> it = m.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<Integer, Integer> e = it.next();
            System.out.println(e.getKey() + " " + e.getValue());
        }
```

#### 3. vector\&list

c++的`vector`对应java中的容器为`Arraylist`。使用`Arraylist`可以用数组的数据结构实现元素的增删查找等等。`ArrayList`是通过**数组**的实现方式实现的，和数组类似，适合查改而不适合插入。

使用`Arraylist`下的方法对其进行增删查改：

```java
ArrayList<Integer> a = new ArrayList<>();
        a.add(123);
        a.set(0,888);
        a.remove(0);
        a.clear();
        a.add(999);
```

其中需要注意，`set(int index, int value)`方法是用于修改`Arraylist`的现有元素的值，不能用于添加元素。如果搜索到的index没有值，会报错。

还可以通过其自带的方法对其进行排序、拷贝：

```java
ArrayList<Integer> b = (ArrayList<Integer>) a.clone();
a.add(666);
Collections.sort(a);
```

至于输出，可以用get()方法来获取单个元素值，通过for循环遍历；也可以和普通数组一样用foreach方式输出：

```java
System.out.println(a.get(1)); 

        for(int i : a){
            System.out.println(i); 
        }
```

> 结果：
>
> 999 666 999

注意：`ArrayList`只能存放引用数据类型，不能存放基础数据类型。

> java还提供另外一种容器 `LinkedList`，与`ArrayList`不同，`LinkedList`是通过**链表**实现的，因此可以直接使用内置的方法执行插入元素操作。当然同列表一样，相对于数组它更适合插入/删除中间元素，而不适合查改。

#### 4. 优先队列

优先队列通过`PriorityQueue`类来实现。优先队列会自动将加入的元素排序，其默认顺序是升序（队首最小）。

定义与增删元素：

```java
        PriorityQueue<String> pq = new PriorityQueue<>();
        pq.add("mcyou");
        pq.add("abc");
        pq.add("233");
        pq.remove("abc");
```

> 可以使用offer()方法代替add()方法，区别是offer()不会产生报错。

读取队首：

```java
System.out.println(pq.peek());
System.out.println(pq.poll());
System.out.println(pq.peek());
System.out.println(pq.size());
```

peak()方法可取出队首元素但不删除它，poll()方法则会取出首元素的同时删除它。size()方法可以取出队中的元素数量。

> 输出效果：
>
> 233 233 mcyou 2

如果需要降序排序（队首最大），则需要在定义时声明：

`PriorityQueue<String> pq = new PriorityQueue<>(Collections.reverseOrder());`

修改后重新运行，输出如下：

> qq qq mcyou 2

#### 5. 队列/双向队列

java提供了`queue`类和`deque`两种类，不过既然有deque那就直接用deque吧。

注意：对应的是`ArrayDeque`类。

```java
ArrayDeque<Integer> queue = new ArrayDeque<Integer>()
queue.offer(1); //尽量还是用offer()
queue.offer(2);
queue.offer(3);
System.out.println(queue.peek()); //返回第一个元素
while (!queue.isEmpty()) {
	System.out.println(queue.pop());
```

作为双向队列，相比于普通的queue：

还可以使用`offerFist()`或者`offerLast()` 来在队首或者队尾添加元素。

还可以使用`peekFirst()`或者`peekLast()` 获得队尾或者队首的元素。

还可以使用`pollFirst()`或者`pollLast()`移除并返回队尾或队首的元素。

其他容器的`pop()`、`push()`、`foreach遍历`等等方法这里也适用。

> 队列的意义在于新添加的数据置于队尾，先pop的数据位于队首。所以一般是用offer()来直接在队尾添加元素、用peek()或者poll()取元素，尽量不用push()之类本身与单向队列顺序相违背的方法，否则容易导致混乱。

#### 6. 栈

java也提供了`Stack`类，用以处理先进先出的数据结构。不过使用`deque`来代替也没什么毛病就是了。

栈内置方法介绍：

| 序号 | 方法描述                                            |
| -- | ----------------------------------------------- |
| 1  | boolean empty() 测试堆栈是否为空。                       |
| 2  | Object peek( ) 查看堆栈顶部的对象，但不从堆栈中移除它。             |
| 3  | Object pop( ) 移除堆栈顶部的对象，并作为此函数的值返回该对象。          |
| 4  | Object push(Object element) 把项压入堆栈顶部。           |
| 5  | int search(Object element) 返回对象在堆栈中的位置，以 1 为基数。 |

声明示例：`Stack<Integer> st = new Stack<Integer>();`

> 参考
>
> [【经验总结】Java在ACM算法竞赛编程中易错点 - Angel\_Kitty](https://www.cnblogs.com/ECJTUACM-873284962/p/7342030.html)

\=======

## Java与算法竞赛——语言相关事项摘录 上

***

> 此处摘录总结一些网上看到的用Java打算法竞赛的常用注意事项和语言相关代码优化。虽然记下来不一定考场上记得就是了。上篇记录一些基本输入输出相关内容，下篇主要介绍容器之类相关语言方向要适应的点。

### 一、输入输出

#### 1. 输入

标准输入方法：

`Scanner s = new Scanner (System.in);`

优化输入方法：

`Scanner s = new Scanner (new BufferedInputStream(System.in));`

用流处理在数据量大的时候会快一点。

接下来用`Scanner`对象提供的方法来输入：

* 整数：`int a = s.nextInt()`
* 浮点数：`double t = nextDouble()`
* 字符串：`String str = s.nextInt()`

特别地，可以使用`nextLine()`来读取一整行：

`String str = s.nextLine()`

要判断输入是否结束，可以使用`s.hasNext()`：

```java
while(s.hasNext()){
    int a = s.nextInt();
    ...
}
```

#### 2. 输出

标准输出方法：

`System.out.println(n);`

优化输出方法：

`PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));`

`out.println(n);`

`out.printf("%.2f\n", ans);` (可用类似`printf`的方式输出)

如果要格式化输出，可以使用格式类，如`NumberFormat` 或 `DecimalFormat`类：

```java
	public static void main(String[] args) {  
		NumberFormat   formatter   =   new   DecimalFormat( "000000");   
        String  s  =   formatter.format(-1234.567);     //   -001235    
        formatter   =   new   DecimalFormat( "##");   
        s   =   formatter.format(-1234.567);             //   -1235   
        s   =   formatter.format(0);                      //   0   
        formatter   =   new   DecimalFormat( "##00");   
        s   =   formatter.format(0);                     //   00   
        formatter   =   new   DecimalFormat( ".00");   
        s   =   formatter.format(-.567);               //   -.57   
        formatter   =   new   DecimalFormat( "0.00");   
        s   =   formatter.format(-.567);              //   -0.57   
        formatter   =   new   DecimalFormat( "#.#");   
        s   =   formatter.format(-1234.567);         //   -1234.6   
        formatter   =   new   DecimalFormat( "#.######");   
        s   =   formatter.format(-1234.567);        //   -1234.567   
        formatter   =   new   DecimalFormat( ".######");   
        s   =   formatter.format(-1234.567);       //   -1234.567   
        formatter   =   new   DecimalFormat( "#.000000");   
        s   =   formatter.format(-1234.567);      //   -1234.567000           
        formatter   =   new   DecimalFormat( "#,###,###");   
        s   =   formatter.format(-1234.567);      //   -1,235   
        s   =   formatter.format(-1234567.890);  //   -1,234,568   
    }
```

#### 3. 文件读写

输入

```java
FileInputStream fis = new FileInputStream("b.in");  
System.setIn(fis); 
```

输出

```java
FileInputStream fis = new FileInputStream("b.in");  
System.setIn(fis); 
```

这样就可以用重定向的方式在标准输出流读写文件了。

### 二、高精相关

~~y1s1 Java啥都可以用自带类来解决真的爽~~

在Java中没有提供long long，最大提供到long，高于long数据范围的需使用高精度。不过好在Java中提供了简单的高精度方式，不用像某C语言一样要手写。

Java中主要通过调用`BigInteger`和`BigDecimal`来实现高精度。

```java
        BigInteger a = new BigInteger("123456789");
        BigDecimal b = new BigDecimal("233.333");
        BigInteger c = new BigInteger("123");
        System.out.println(c.add(a)); //加
        System.out.println(a.subtract(c)); //减
        System.out.println(a.multiply(c)); //乘
        System.out.println(b.divide(b)); //除
        System.out.println(a.remainder(c)); //取余数
        System.out.println(a.mod(c)); //取余
        System.out.println(c.pow(3)); //c的3次方
        System.out.println(a.gcd(c)); //求最大公约数
        System.out.println(a.compareTo(c)); //比大小，大于则>0，小于则<0，等于则=0
        long d = a.longValue(); //转换为long
        System.out.println(d);
```

输出

> 123456912 123456666 15185185047 1 90 90 1860867 3 1 123456789

注意：使用这两个类时只能使用其内部的方法来进行加减，不能直接使用运算符。

\#补充：关于取余（remainder)与取模（mod）的区别：

> 例子：
>
> -14 取余 3 = -2
>
> -14 mod 3 = 1

取余运算为数学意义上的取余，其结果可以为负，目的是使商尽可能大的情况下获得余数。

而取模运算返回值一定为正，且要求第二个参数（如本例的3）必须>0。目的是使商尽量小的情况下获得正余数。

> Java默认的%运算符是取余，而python是取模。

### 三、常用容器

#### 1. Set

使用`java.util.Hashtag`来实现。

```java
Set<Integer> s = new HashSet<>();
s.add(233);
System.out.println(s.contains(233)); //true
```

#### 2. Map

使用`java.util.HashMap`来实现

```java
Map<Integer,Integer> m = new HashMap<>();
m.put(1, 233);
m.put(2, 666);
m.remove(1);
System.out.println(m.get(1));
```

因为map是无序的，所以需要使用迭代器来遍历map。

```java
Iterator<Map.Entry<Integer, Integer>> it = m.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<Integer, Integer> e = it.next();
            System.out.println(e.getKey() + " " + e.getValue());
        }
```

#### 3. vector\&list

c++的`vector`对应java中的容器为`Arraylist`。使用`Arraylist`可以用数组的数据结构实现元素的增删查找等等。`ArrayList`是通过**数组**的实现方式实现的，和数组类似，适合查改而不适合插入。

使用`Arraylist`下的方法对其进行增删查改：

```java
ArrayList<Integer> a = new ArrayList<>();
        a.add(123);
        a.set(0,888);
        a.remove(0);
        a.clear();
        a.add(999);
```

其中需要注意，`set(int index, int value)`方法是用于修改`Arraylist`的现有元素的值，不能用于添加元素。如果搜索到的index没有值，会报错。

还可以通过其自带的方法对其进行排序、拷贝：

```java
ArrayList<Integer> b = (ArrayList<Integer>) a.clone();
a.add(666);
Collections.sort(a);
```

至于输出，可以用get()方法来获取单个元素值，通过for循环遍历；也可以和普通数组一样用foreach方式输出：

```java
System.out.println(a.get(1)); 

        for(int i : a){
            System.out.println(i); 
        }
```

> 结果：
>
> 999 666 999

注意：`ArrayList`只能存放引用数据类型，不能存放基础数据类型。

> java还提供另外一种容器 `LinkedList`，与`ArrayList`不同，`LinkedList`是通过**链表**实现的，因此可以直接使用内置的方法执行插入元素操作。当然同列表一样，相对于数组它更适合插入/删除中间元素，而不适合查改。

#### 4. 优先队列

优先队列通过`PriorityQueue`类来实现。优先队列会自动将加入的元素排序，其默认顺序是升序（队首最小）。

定义与增删元素：

```java
        PriorityQueue<String> pq = new PriorityQueue<>();
        pq.add("mcyou");
        pq.add("abc");
        pq.add("233");
        pq.remove("abc");
```

> 可以使用offer()方法代替add()方法，区别是offer()不会产生报错。

读取队首：

```java
System.out.println(pq.peek());
System.out.println(pq.poll());
System.out.println(pq.peek());
System.out.println(pq.size());
```

peak()方法可取出队首元素但不删除它，poll()方法则会取出首元素的同时删除它。size()方法可以取出队中的元素数量。

> 输出效果：
>
> 233 233 mcyou 2

如果需要降序排序（队首最大），则需要在定义时声明：

`PriorityQueue<String> pq = new PriorityQueue<>(Collections.reverseOrder());`

修改后重新运行，输出如下：

> qq qq mcyou 2

#### 5. 队列/双向队列

java提供了`queue`类和`deque`两种类，不过既然有deque那就直接用deque吧。

注意：对应的是`ArrayDeque`类。

```java
ArrayDeque<Integer> queue = new ArrayDeque<Integer>()
queue.offer(1); //尽量还是用offer()
queue.offer(2);
queue.offer(3);
System.out.println(queue.peek()); //返回第一个元素
while (!queue.isEmpty()) {
	System.out.println(queue.pop());
```

作为双向队列，相比于普通的queue：

还可以使用`offerFist()`或者`offerLast()` 来在队首或者队尾添加元素。

还可以使用`peekFirst()`或者`peekLast()` 获得队尾或者队首的元素。

还可以使用`pollFirst()`或者`pollLast()`移除并返回队尾或队首的元素。

其他容器的`pop()`、`push()`、`foreach遍历`等等方法这里也适用。

> 队列的意义在于新添加的数据置于队尾，先pop的数据位于队首。所以一般是用offer()来直接在队尾添加元素、用peek()或者poll()取元素，尽量不用push()之类本身与单向队列顺序相违背的方法，否则容易导致混乱。

#### 6. 栈

java也提供了`Stack`类，用以处理先进先出的数据结构。不过使用`deque`来代替也没什么毛病就是了。

栈内置方法介绍：

| 序号 | 方法描述                                            |
| -- | ----------------------------------------------- |
| 1  | boolean empty() 测试堆栈是否为空。                       |
| 2  | Object peek( ) 查看堆栈顶部的对象，但不从堆栈中移除它。             |
| 3  | Object pop( ) 移除堆栈顶部的对象，并作为此函数的值返回该对象。          |
| 4  | Object push(Object element) 把项压入堆栈顶部。           |
| 5  | int search(Object element) 返回对象在堆栈中的位置，以 1 为基数。 |

声明示例：`Stack<Integer> st = new Stack<Integer>();`

> 参考
>
> [【经验总结】Java在ACM算法竞赛编程中易错点 - Angel\_Kitty](https://www.cnblogs.com/ECJTUACM-873284962/p/7342030.html)
>
> > > > > > > 2db037fcf6de5bcf588074205a43fc7a350195a0 [算法竞赛中的JAVA使用笔记](https://www.myblog.link/2016/11/14/Note-of-java/)
