蓝桥杯Java算法笔记

一、Java基础语法

1.一般输入输出 和 快速输入输出

摘要
本文主要介绍快速输入输出, 文中提到了几个IO类,这里推荐使用BufferedReader输入,BufferedWriter输出,当输入输出的数据量大于一百万左右就必须使用快速IO不能直接使用Scanner和System.out.print

  1. 主类的命名必须是Main
    形如:
    public class Main{

}

2.输入输出:
2.1输入:
(1)使用Scanner类进行输入
首先需要定义一个可以在控制台从键盘接收数据的Scanner对象: (Scanner类的包名是 java.util.Scanner)

Scanner in = new Scanner(System.in); // 用于控制台从键盘读入数据
然后使用这个in对象配合in.nextXXX()方法接收数据:

不同类型的数据使用不同的in.nextXXX()方法。

如:
在这里插入图片描述

字符串需要用
在这里插入图片描述
或者
在这里插入图片描述
需要注意的是:
in.next() 从缓冲区接收字符遇到空格后停止。 相当于 cin 和 scanf
in.nextLine() 从缓冲区接收字符,并且接收空格,遇到换行才停止,并且会自动舍弃换行。 相当于 gets()

import java.util.Scanner;
public class Main{
	public static void main(String[] args){
		Scanner in = new Scanner(System.in);
		String s1 = in.next();  // -》 C++中 cin/scanf
		String s2 = in.nextLine(); // -> C++中  gets()
		System.out.println("s1:"+s1);
		System.out.println("s2:"+s2);
		in.close();
	}
}

上述代码定义了两个字符串类:s1 和 s2。 分别用 in.next() 和 in.nextLine() 输入。
结果如下:
在这里插入图片描述
in.next()将从缓冲区内接收了abc赋值给了s1 , 遇到空格后停止,缓冲区内还剩下了一个空格和qwe ,in.nextLine()将缓冲区剩下的字符赋值给 s2。
类比scanf和gets。

(2) hasNext()方法
in.hasNext用法:
in.hasNext()的返回值是bool值,作用是当在缓冲区内扫描到字符时,会返回true, 否则会发生阻塞,等待数据输入。
所以in.hasNext()是不会返回false的
所以遇到多组输入时,可以使用 while + in.hasNext() 相当于 while(scanf())
如:每次输入三个整数,输出三数之和。

import java.util.Scanner;
 
public class Main1 {
	public static void main(String[] args){
		Scanner in = new Scanner(System.in);	
		int a, b, c; 
		while(in.hasNext()){	
		    a = in.nextInt();
		    b = in.nextInt();
		    c = in.nextInt();
		    System.out.printf("%d\n",a+b+c);
		}
	}
}

和in.nextXXX()方法类似,in.hasNextXXX()也有针对不同类型变量的方法。

如:

in.hasNext() // 判断缓存区中还有没有数据,有返回true, 否则等待输入。
in.hasNextInt() // 判断输入的是不是int型的数据,是的话返回true 否则继续扫描缓冲区,或者等待输入。
in.hasNextDouble() // 判断输入的是不是double型的数据,是的话返回true 否则继续扫描缓冲区,或者等待输入。

2.2 输出
java中往控制台输出的几种常用函数

System.out.printf(); //和C/C++中的printf一样。 可使用格式控制符进行格式化输出。
// 例如: 输出一个int类型变量 System.out.printf("%d",a);
System.out.print() //不能使用格式控制符进行格式化输出,仅输出变量
System.out.println() //不能使用格式控制符进行格式化输出,仅输出变量,但会自动输出一个换行。
3 快速输入输出
(不想看函数介绍的,可以直接看最下面的程序实例)
3.1使用StreamTokenizer 和 PrintWriter实现快速输入输出 (非推荐)


StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

这种方式只能读取数字和字母字符串, 不能读取空格和其他字符。

实例1:

import java.io.*;
 
public class Main {
    public static void main(String[] args) throws IOException {
        //快速输入  
        StreamTokenizer in =new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        //快速输出
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        while(in.nextToken() != StreamTokenizer.TT_EOF){
            int a = (int)in.nval; // 获取一个整型数据
            in.nextToken();  //从流中获取下一个数据, 相当于scanf()读入一个数据然后存在缓存区
            String s=in.sval; //  获取一个字符串  
            in.nextToken();
            double n=in.nval;
            out.println(n);
            out.flush();//将输出缓冲区清空。
        }  
    }
}

PrintWriter类中 包含 print() printf() writer() 方法 printf()可用于格式化输出 但速度是最慢的 write()的速度是最快的

注意要在最后刷新输出缓冲区, 就是记得加上 out.flush() 否则会什么也不输出

*3.2 使用BufferedReader和BufferedWriter实现快速输入输出(推荐)
BufferedReader 和 BufferedWriter 都在 java.io.包内。

BufferedReader

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
成员方法
方法                                                   描述
int read()                                            读取单个字符。
int read(char[] cbuf, int off, int len)     将字符读入数组的某一部分。
String readLine()                                读取一个文本行。
long skip(long n)                                 跳过字符。
boolean ready()                                  判断此流是否已准备好被读取。
void close()                                         关闭该流并释放与之关联的所有资源。
void mark(int readAheadLimit)           标记流中的当前位置。
boolean markSupported()                   判断此流是否支持 mark() 操作(它一定支持)。
void reset()                                          将流重置到最新的标记。

主要使用read()readLine()

String s = in.read() // 读入一个字符 可读入空格回车 但不抛弃回车
String s1 = in.readLine(); // 读入一行 可读入空格可读入回车 但会将回车抛弃
string s2[] = in.readLine().Split(" "); // 使用Split通过空格分割读入的一行字符串,存在s2中

需要注意的是 在windows中按一下回车键 一共有两个字符 “\n\r” 而read()只能读取一个字符所以如要要用read来达到吸收回车的目的,需要用两个read(); 如果用readLine()的话会将"\n\r"全部吸收 , 所以只需要一个readLine()来吸收回车。

详见下面实例2.
BufferedWriter
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
主要使用 BufferedWriter类中的 write() 类进行输出。 当数据量大的时候一定要使用这个类进行输出,谨记!

需要注意的是 write() 不能直接输出int类型, 因为write(int a) 会输出其对应的ASCii码的字符 ,比如输出 65 会显示 A 详见代码:

int a = 65;
char b = '2';
String c = "3";
 
out.write(a);
out.write("\n");
out.write(b);
out.write("\n");
out.write(c);
out.write("\n");
out.flush();
 
输出:
A
2
3

所以当需要输出一个int类型的变量时, 可以用Integer.toString(int a)方法 将其变为字符串形式输出。

或者使用 + 拼接一个字符串,这样 参数整体就是一个字符串了,比如加一个换行符。详见代码:

int a = 65;

out.write(a + "\n");
out.write(Integer.toString(a));
out.flush();
 
输出:
65
65

实例2:

import java.io.*;
import java.util.*;
 
 
public class Main{
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException{
        //测试writr 不能直接输出int类型
    	int a = 65;
        out.write(a);
        out.write("\n");
    	out.write(a + "\n");  // 使用 + 号拼接个字符串 使参数整体为一个字符串 
        out.write(Integer.toString(a)); // 输出a的字符串形式
        out.write("\n");
       
        //测试 read() 和  readLine();
        int b = in.read();   // read()只读取一个字符
        int c = in.read();   // 吸收 \n
        int x = in.read();   // 吸收 \r
       // String e = in.readLine();
        String d = in.readLine();
        out.write("\n");
        out.write(b + "\n");
        out.write(c + "\n");
        out.write(x + "\n");
        out.write(d + "\n");
        //out.write(e);
        out.flush();
    }
}

输出:

在这里插入图片描述

一共输入了:

1 (按回车键)

ABC DEF

然后下面输出了

49(1的ASCii码)

13(回车键的ACSii码)

10 (换行键的ASCII码)

2. java编程规范和常用数据类型

摘要
本文主要介绍了最基本的java程序规则,和常用数据类型,其中侧重说了数组的一些操作。

面向java编程
java是纯面向对象语言,所有的程序都要定义在类中,类中只能包含成员变量,成员函数,以及代码块。
不能在类体内直接出现其他执行性语句。
java程序从和C/C++一样都从main函数开始执行
java中一个java文件中只能至多包含一个public class 并且当有public clas 类时 main函数只能定义在该类中
xxx.java 文件的文件名必须必须与main函数所在类的类名相同
java中main函数的定义方式为

public static void main(String[] args){…}
示例程序:斐波那契数列

import java.util.*;
public class Main {
	public static int arr[] = new int[1000005];
	public static void main(String[] args){
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		arr[1] = arr[2] = 1;
		for(int i = 3; i <= 1000004; i++){
			arr[i] = (arr[i-1] + arr[i-2])%10007;
		}
		System.out.print(arr[n]);
	}
}

常用数据类型:

int  float double  long char boolean byte
定义方式形如: int a;  float b;  double c;  boolean  f = true

数组:
定义:
int arr[] = new int[100];
或者

int arr[]; arr = new int[100];
初始化:
int arr[] = {1, 2, 3, 4};
或者
Arrays.fill(int[] arr, int x); //将arr内的每个元素赋值为x
数组的拷贝

clone()
System.arraycopy
Arrays.copyOf
Arrays.copyOfRange

a.clone(int[] b); // 将b数组的值拷贝给a数组
int[] a = {1,2,3};

int[] b = new int[10];
b = a.clone();
 
for(int i = 0; i < 3; i++){
    System.out.print(a[i]+" ");
}
System.out.print("\n");
for(int i = 0; i < 3; i++){
    System.out.print(b[i]+" ");
}

输出:
1 2 3
1 2 3

System.arraycopy(Object a, int begin_a, Object b, int begin_b, int length);//从a复制到b,复制从a数组指定的位置begin_a开始,到begin_a+length-1结束。放置从b的begin_b开始,到begin_b+length-1结束。

int[] a = {1, 2, 3, 4};
int[] b = new int[10];
System.arraycopy(a, 0, b, 0, 3);
for(int i = 0; i < 4; i++)
    System.out.println(b[i]);
 
输出:
1
2
3
0

Arrays.copyOf(int[] a, int length);//从a数组的第一个元素开始复制,复制length个元素。

int[] a = {1, 2, 3, 4};
int[] b = new int[10];
b = Arrays.copyOf(a, 4);
System.arraycopy(a, 0, b, 0, 3);
for(int i = 0; i < 4; i++)
    System.out.println(b[i]);
 
输出:
1
2
3
4

Arrays.copyOfRange(int[] a, int begin, int to);//从a数组begin开始复制,到to-1位置结束。


int[] a = {1, 2, 3, 4};
int[] b = new int[10];
b = Arrays.copyOfRange(a, 0, 4);
for(int i = 0; i < 4; i++)
    System.out.println(b[i]);
 
输出:
1
2
3
4

String
String 的创建(注意是大写S!):

1.String 定义方式形如: String s1 = "qwe";
2.因为String是一个类, 所以也可以通过构造函数初始化, 形如 String s2 = new String("asd"); 
注意, 通过第一种方式创建字符串对象,内存分配在“常量池” 中。在常量池中数据是共享的, 当存在qwe这个常量时,如果再 String s2 = "qwe"; 时,此时JVM不会创建新的字符串对象,而是会让 s2指向qwe这个常量的地址,也就是说 s1和s2的内存地址是相同的。
对于第二种方式,每创建一个字符串对象,都会在堆区重新开辟一个空间保存字符串常量。

String类型的比较:
1. ==, != :
== , != 判断的是两个字符串地址是否相等。

前面已经说过,两个直接用=赋值的字符串对象,如果字符串常量相同则地址相同。

所以:
  String s1 = "qwe"; 
  String s2 = "qwe";   
  s1 == s2   // true
 
  String s1 = new String("qwe"); 
  String s2 = new String("qwe");   
  s1 == s2 // false 

2. str1.equals(String str2);
equals()方法是直接比较两个字符串对象的内容,相等返回 true,否则返回false

 String s1 = new String("qwe"); 
 String s2 = new String("qwe");   
 s1.equals(s2); //true

所以判断字符串内容是否相同要用 equals 方法而不能直接用 == 。谨记

String对象的遍历:
在java中String不可直接通过索引或者说下标遍历,而要先转化为字符数组或者使用charAt()方法。

String s1 = "123456";
char[] s2 = s1.toCharArray();
for(int i = 0; i < s2.length; i++){ 
    System.out.println(s2[i]);
}

注意s2.length后面没有括号, 因为length是char数组的属性,而不是方法。

3.常用功能符以及循环结构和分支结构

1.常用功能符
注释
(“文字”是被注释的部分)
//文字 单行注释
/文字/ 多行注释

算术运算符

+ - * /
/ 整数相除仍是整数向下取整 即 3/2 = 1

取模运算符
% 求两数相除的余数 5%3 = 2
自增自减运算符

 int a = 2, b = 0;
 a++;  先参与运算而后加1    b = a++; 则 b == 2 
 ++a;  先加1后参与运算      b = ++a; 则 b == 3
 a--;    先参与运算而后减1  b = a--; 则 b == 2
 --a; 先减1后参与运算      b = --a; 则 b == 1

关系运算符
== != <= >= > <
逻辑运算符

 &()   1 & 1 = 1   1 & 0 = 0
 |()   1 | 1 = 1   1 | 0 = 1  0 | 0 = 0 
 !()  !1 = 0  0! = 1  
  (0表示false 1表示true) 
 &&(短路与)  布尔表达式1 && 布尔表达式2  如果表达式1为假  则 表达式2不计算  
 ||(短路或)   布尔表达式1 || 布尔表达式2  如果表达式1为真  则 表达式2不计算      

三目运算符

x = 布尔表达式 ?表达式1:表达式2;
当布尔表达式为true  x的值为表达式1;
当布尔表达式为false x的值为表达式2;
x = 1 < 5 ? 3 : 2;
则 x = 3

2.分支结构
if…else

if(布尔表达式){ 
	表达式; // 布尔表达式为真执行
}
else{
	表达式; // 布尔表达式为假执行
}

ifelse ifelse
if(布尔表达式){
	表达式;
}
else if(布尔表达式){
	表达式;
}
else if(布尔表达式){
	表达式;
}
else{
	表达式;
}
从上到下,当有一个布尔表达式为真,则执行相应代码块,其余均不执行。

switch - case

switch(a){
case b1: 表达式;break;
case b2: 表达式;break;case bn: 表达式;break;
default: 表达式; break;
}
注意a,b1,b2…bn的取值可以是byteshortintchar、String,还有枚举类型,例如:
int a = 3;
switch(a){
case 1: System.out.print(1);break;
case 2: System.out.print(2);break;
case 3: System.out.print(3);break;
default: System.out.print(4);break;
输出为:3

3.循环结构

while
 > while(布尔表达式){
 	}
 	唯一和C/C++有区别的是 java中有专门的布尔类型,
 	所以int a = 5while(a--) 这种写法是错误的,因为整型a不是布尔类型。

dowhile
do{
表达式;
}while(布尔表达式)

for
for的两种遍历方式:

for(int i = 0; i < n; i++){ 
表达式;
}
int[] a = {1,2,3,4};
for(int i : a){
System.out.print(i+" ");
}
结果为:1 2 3 4
也就是for(循环类型:循环类型名)
break
break 可以直接结束一层循环
continue
continue 可以直接进行下一次循环

  	for(int i = 1; i <= 3; i++){
		System.out.print(i);
		System.out.print(i);
	}
	输出为:112233	
	for(int i = 1; i <= 3; i++){
		System.out.print(i);
		break;
		System.out.printf(i);
	}
	输出为:1
	for(int i = 1; i <= 3; i++){
		System.out.print(i);
		continue;
		System.out.print(i)
	}
	输出为:123

4.函数(方法)、类和对象

函数

在java中函数也称为方法,是一段具备某种功能的可重用代码块。 一个函数包括这几部分:

函数头
函数头包括函数访问修饰符,函数返回值类型, 函数名,参数
代码块
返回值

具体看个实例:

public class Main{
	public static void main(String[] args){
		int a = 1;
		int b = 2;
		int c = add(a, b);
		System.out.print(c);
	}
	public static int add(int a, int b){
		return a+b;
	}
}

上面的代码演示了一个add()函数,实现了两个整型变量相加。
public static int add(int a, int b) 是函数头
其中 public 是访问限定符 public 译作公有 也就说以public修饰的函数可以被其它类其他函数直接调用。
同时java中返回值以return关键字返回,并支持函数重载,递归,和C/C++区别不大。

static修饰符

static 是静态修饰符, 被static修饰的函数是静态函数,并且被static修饰的函数只能调用被static修饰的函数
然后在java里面没有全局变量的概念, 如果需要用全局变量,就在main函数外面声明static 变量。

类和对象

蓝桥杯主要考察一些算法,会写简单的类就可以了,这里就简单说一下对象和类的概念以及java中类的定义。

类是具有共同特征的对象的集合,对象是类的实例。

比如 人是一个类 张三是其中一个对象。

类中包含成员方法和成员变量,方法(函数)可以理解为人的行为方式,成员变量(字段)可以理解为人的状态。

方法操作对象内部状态的改变,对象之间的相互调用也通过方法来完成。

以人为例创建一个类,并直接创建张三对象,构造方法则为其行为,name则是其属性。

实例:

class Person{//  Person类
    String name;
    int age;
    String sex;
    Person(String name){
        System.out.println("my name is:" + name);     
    }
}
public class Main{
    public static void main(String[] args){ 
        Person zhangsan = new Person("zhangsan");                                                      
    }
}

之前已经说过, java是纯面向对象语言, 所有的程序都由类体组成,一个java文件可以包含很多个类,但只能有一个public 类, main函数也在其中。
类可以在类内创建,也可以在类外创建, 类内创建称为内部类,类外创建称为外部类。

注意创建类的时候一定别忘了加 new 。

二、算法竞赛常用的JAVA API

1.大数类

摘要

java中的基础数据类型能存储的最大的二进制数是 2 ^ 63 - 1,
对应的十进制数是9223372036854775807,也就是说只要运算过程中会超过这个数,就会造成数据溢出,从而造成错误.

而java.math.*包中提供了大数类,其理论上可以存储无限位的大数,只要内存足够的话。
大数类又分为整数和浮点数.即BigInteger and BigDecimal
大数类的对象不能直接进行运算,需要调用类中相应的方法,并且方法的参数必须和调用的类相同,BigInteger不能调用BigDecimal, 不能作为其方法参数, 即整数和浮点数不能混合运算.
本文举例了一些常用的方法,不需要背会,需要用的时候查java API就行了。

BigInteger 和 BigDecimal
创建

//1.直接声明 
BigInteger a;
BigDecimal b;
//2.使用构造函数初始化
BigInteger a = new BigInteger("123456789101112131415");
BigDecimal b = new BigDecimal("123456.123456");

赋值

BigInteger.valueOf(long val);
BigDecimal.valueOf(double val);
BigInteger a;
BigDecimal b;
//注意 val 不能超过 long 类型的最大取值9223372036854775807, 超过int时要在数后面加L如:
a = BigInteger.valueOf(123456789101112L); //大于int范围的要加L
b = BigDecimal.valueOf(123456.12341235); // 超出的小数位数会自动舍弃 

使用 = 将同类型变量的值赋给另一个变量


BigInteger a;
BigInteger b = new BigInteger("123456");
a = b;
System.out.print(a);

输出:
123456

加法

BigInteger.add(BigInteger);
BigDecimal.add(BigDecimal);

BigInteger a, b, c;
a = BigInteger.valueOf(123456789); // 赋值为 123456789
b = BigInteger.valueOf(987654321); // 赋值为 987654321
c = a.add(b);
System.out.print(c);
输出:
1111111110 
1111111110 

减法

BigInteger.subtract(BigInteger);
BigDecimal.sbutract(BigDecimal);

BigInteger a, b, c;
a = BigInteger.valueOf(123456789); // 赋值为 123456789
b = BigInteger.valueOf(987654321); // 赋值为 987654321
c = a.subtract(b);
System.out.print(c);

输出:
-864197532

乘法

BigInteger.multiply(BigInteger);
BigDecimal.multiply(BigDecimal);

BigInteger a, b, c;
a = BigInteger.valueOf(123456789); // 赋值为 123456789
b = BigInteger.valueOf(987654321); // 赋值为 987654321
c = a.multiply(b);
System.out.print(c);
输出:
121932631112635269

除法

BigInteger.divide(BigInteger);
BigDecimal.divide(BigDecimal);

BigInteger a, b, c;
a = BigInteger.valueOf(987654321); // 赋值为 987654321
b = BigInteger.valueOf(123456789); // 赋值为 123456789
c = a.divide(b); // 整数相除仍为整数
System.out.print(c);

输出:8

*取余

BigInteger.mod(BigInteger);

BigInteger a, b, c;
a = BigInteger.valueOf(987654321); // 赋值为 987654321
b = BigInteger.valueOf(123456789); // 赋值为 123456789
c = a.mod(b);
System.out.print(c);

输出:9

*求最大公因数

BigInteger.gcd(BigInteger);

BigInteger a, b, c;
a = BigInteger.valueOf(987654321); // 赋值为 987654321
b = BigInteger.valueOf(123456789); // 赋值为 123456789
c = a.gcd(b);
System.out.print(c);

输出:9

求最值

BigInteger.max(BigInteger) , BigDecimal.max(BigDecimal) 最大值
BigInteger.min(BigInteger) , BigDecimal.min(BigDecimal) 最小值

BigInteger a, b, c, d;
a = BigInteger.valueOf(987654321); // 赋值为 987654321
b = BigInteger.valueOf(123456789); // 赋值为 123456789
c = a.max(b); //a,b中的最大值
d = a.min(b); //a,b中的最小值
System.out.println(c);
System.out.println(d);

输出:
987654321
123456789

*(a^b)%mod

BigInteger.modPow(BigInteger, BigInteger);

BigInteger a, b, c, mod;
a = BigInteger.valueOf(987654321); // 赋值为 987654321
b = BigInteger.valueOf(123456789); // 赋值为 123456789
mod = BigInteger.valueOf(10007);  
c = a.modPow(b, mod); //(a^b)%mod
System.out.println(c);
输出:718

比较大小

BigInteger.compareTo(BigInteger);
BigDecimal.compareTo(BigDecimal);

BigInteger a, b;
int c;
a = BigInteger.valueOf(987654321); // 赋值为 987654321
b = BigInteger.valueOf(123456789); // 赋值为 123456789
c = a.compareTo(b);  // a 和 b
System.out.println(c); 
c = b.compareTo(b);  // b 和 b
System.out.println(c);
c = b.compareTo(a);   // c 和 c
System.out.println(c);
输出:
1   
0
-1

可见, 对于a.compareTo(b), a和b进行比较如果:

a > b  返回 1  
a == b 返回 0 
a < b  返回-1

*进制转化

使用构造函数BigInteger(String, int index);可以把一个index进制的字符串,转化为10进制的BigInteger;

BigInteger a = new BigInteger("111110", 2);111110变为10进制赋值给a
System.out.println(a.toString(16));把a转化为16进制的字符串输出

类型转化

BigInteger.toBigDecimal() //把BigInteger 转化为 BigDecimal
BigDecimal.toBigInteger() //把BigDecimal 转化为 BigInteger

	BigInteger a = new BigInteger(1);
	BigDecimal b = new BigDecimal(2);
	b.toBigInteger(); // 把BigDecimal转为BigInteger
	a.toBigDecimal(); // 把BigInteger转为BigDecimal

-BigDecimal的精度问题

BigDecimal的舍入模式
想象一个数轴,从负无穷到正无穷,向哪舍入,就是趋向于哪,0就是舍入后要更趋近于0.

ROUND_DOWN 向零舍入。 即1.55 变为 1.5 , -1.55 变为-1.5
ROUND_CEILING 向正无穷舍入.1.55 变为 1.6 , -1.55 变为 -1.5
ROUND_FLOOR 向负无穷舍入.1.55 变为 1.5 , -1.55 变为 -1.6
ROUND_HALF_UP 四舍五入 即1.55 变为1.6, -1.55变为-1.6
ROUND_HALF_DOWN 五舍六入 即 1.55 变为 1.5, -1.5变为-1.5
ROUND_HALF_EVEN 如果舍入前一位的数字为偶数,则采用HALF_DOWN奇数则采用HALF_UP 如1.55 采用HALF_UP 1.45采用HALF_DOWN
ROUND_UP 向远离0的方向舍入 即 1.55 变为 1.6 , -1.55 变为-1.6
ROUND_UNNECESSARY 有精确的位数时,不需要舍入

在需要精确舍入的方式时可以使用以上的舍入模式。
(另:Math 类的 ceil()和 floor方法对应普通浮点型的上取整和下取整.)
BigDecimal进行加减乘除时可以进行舍入

如 除法

divide(BigDecimal divisor, int scale, RoundingMode roundingMode) 返回一个
BigDecimal ,其值为 (this / divisor) ,其小数位数为scale。

import java.math.*;
import java.util.*;
import java.io.*;
public class Main{
    public static void main(String[] args){ 
    	BigDecimal a, b, c;
    	a = BigDecimal.valueOf(1.51);
    	b = BigDecimal.valueOf(1.37);
    	c = a.divide(b,100,BigDecimal.ROUND_DOWN);//采用向0舍入并并保留100位小数
    	System.out.println(c);
    }
}
输出:
1.1021897810218978102189781021897810218978102189781021897810218978102189781021897810218978102189781021

保留n位小数

setScale(int newScale, RoundingMode roundingMode);

import java.math.*;
import java.util.*;
import java.io.*;
public class Main{
    public static void main(String[] args){ 
    	BigDecimal a = new BigDecimal("1.10218");
    	a = a.setScale(4,BigDecimal.ROUND_HALF_UP);//四舍五入保留四位小数
    	System.out.println(a);
    }
}

2.Math类

求最值
最小值

Math.min(int a, int b)
Math.min(float a, float b)
Math.min(double a, doubleb)
Math.min(long a, long b)

最大值

Math.max(int a, int b)
Math.max(float a, float b)
Math.max(double a, double b)
Math.max(long a, long b)

Math.min() 和 Math.max() 方法分别返回一个最小值和一个最大值。
实例:

public class Main{
	public static void main(String[] args){
		int a = 10;
		int b = 20;
		System.out.println(Math.min(a, b));
		System.out.println(Math.max(a, b));
	}
}

求平方根

Math.sqrt(double a)
返回正确舍入的 double 值的正平方根。

求绝对值

Math.abs(double a)
Math.abs(int a)
Math.abs(flota)
Math.abs(long)
返回一个类型和参数类型一致的绝对值

public class Main{
	public static void main(String[] args){
		int a = -10;
		System.out.println(Math.abs(a));
	}
}

求幂(a^b)
Math.pow(double a, double b) 注意无论是传入int还是long都会返回一个double类型的数

实例:
在这里插入图片描述

所以要求int类型的幂时,要对结果进行类型转换。

取整

Math.ceil(double x)
向上取整,返回大于该值的最近 double 值
System.out.println(Math.ceil(1.4)); // 2.0
System.out.println(Math.ceil(-1.6)); // -1.0

Math.floor(double x)
向下取整,返回小于该值的最近 double 值
System.out.println(Math.floor(1.6)); // 1.0
System.out.println(Math.floor(-1.6)); // -2.0

Math.round(double x);
四舍五入取整
System.out.println(Math.round(1.1)); // 1
System.out.println(Math.round(1.6)); // 2
System.out.println(Math.round(-1.1)); // -1
System.out.println(Math.round(-1.6)); // -2

得到一个随机数

Math.random()
生成一个[0,1)之间的double类型的伪随机数

所以为了得到一个[1, b] 之间的整数可以这样做:
int a = (int)(Math.random()*b + 1); // [1, b]

如果要得到[a, b]的一个整数则是:
int a = (int)(Math.random()*(b - a + 1) + a)  // + 1 是因为random()最大取不到1,所以上限取整后就会少1.   

三角函数

Math.cos(double a) 余弦
Math.acos(double a) 反余弦
Math.sin(double a) 正弦值
Math.asin(double a) 反正弦值
Math.tan(double a) 正切值
Math.atan(double a) 反正切
我们可以用acos()方法求π
因为
cos(π) = -1
所以
acos(-1) = π

3.Calendar日期类

摘要
在蓝桥杯中有关于日期计算的问题,正好java中的Date类和Calendar类提供了对日期处理的一些方法。Date类大部分方法已经废弃了,所以本文将详细介绍Calendar类。

Calendar类

Calendar 类是一个抽象类,它为特定瞬间与一组诸如YEAR、MONTH、DAY_OF_MONTH、HOUR 等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。
瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 197011 日的 00:00:00.000,格里高利历)的偏移量。

常用的日历字段

YEAR 指示年的 get 和 set 的字段数字。
MONTH 指示月份的 get 和 set 的字段数字。
DAY_OF_MONTH get 和 set 的字段数字, 指示一个月中的某天。
DAY_OF_WEEK get 和 set 的字段数字, 指示一个星期中的某天。
DAY_OF_YEAR get 和 set 的字段数字, 指示当前年中的天数。
DAY_OF_WEEK_IN_MONTH get 和 set 的字段数字, 指示当前月中的第几个星期。
HOUR get 和 set 的字段数字,指示当天中的某小时
MINUTE get 和 set 的字段数字,指示当前小时中的某分钟
SECOND get 和 set 的字段数字,指示当前分钟中的某秒
time 以毫秒为单位,表示自格林威治标准时间 1970110:00:00 后经过的时间。
(字段就是Calendar类的成员变量,用于存储当前日历的年月日等时间信息。)

Calendar类的实例化
getInstance();//返回一个默认时区和语言环境的日历

Calendar calendar = Calendar.getInstance();//赋值给calendar

设置特定日期

set(int field, int value);//第一个参数是日期字段,诸如YEAR、MONTH 等将给定的日历字段设置为给定值。
set(int year, int month, int date)// 设置日历字段年月日的值

Calendar calendar = Calendar.getInstance();//创建个实例
int year = 2020;
int month = 1;//1是二月 0是1月
int day = 1;
calendar.set(Calendar.YEAR, year);// 将year的值赋给calender的YEAR字段
calendar.set(Calendar.MONTH, month);//将month的值赋给calender的MONTH字段
calendar.set(Calendar.DAY_OF_MONTH, day);//将day的值赋值给calendder的DAT_OF_MONTH字段

//以上就完成了对calender的字段设置。

有趣的是MONTH字段是从0月开始计数的,所以12月对应的值是11。DAY_OF_WEEK中星期天对应的是1,星期一对应的是2,星期六对应的是7,而YEAR和DAY_OF_MONTH都是从1开始计数

获取当前Calender实例的字段信息

get(int field);// 获取给定字段的值

Calendar calendar = Calendar.getInstance();
// 设置日期为: 2020.1.21
calendar.set(Calendar.YEAR, 2020);
calendar.set(Calendar.MONTH, 0);
calendar.set(Calendar.DAY_OF_MONTH, 21);
// 获取2020.1.21是星期几
System.out.print(calendar.get(Calendar.DAY_OF_WEEK));

输出:
3 // 3代表星期二

增减时间
add(int field, int amount);// 根据日历的规则,为给定的日历字段添加或减去指定的时间量。

4.String 、StringBuilder、StringBuffer常用方法和区别)

摘要
本文将介绍String、StringBuilder类的常用方法。
在java中String类不可变的,创建一个String对象后不能更改它的值。所以如果需要对原字符串进行一些改动操作,就需要用StringBuilder类或者StringBuffer类,StringBuilder比StringBuffer更快一些,缺点是StringBuilder不是线程安全的,但在算法竞赛中一般我们用不到多线程。所以,主要推荐使用StringBuilder类。

String:

方法概述:
String 类包括的方法可用于检查序列的单个字符、比较字符串、
搜索字符串、提取子字符串、创建字符串副本并将所有字符全部转换为大写或小写。

String的遍历

String有两种遍历方式,第一种charAt()方法
第二种是先转化为字符数组,再挨个遍历
charAt(int i);//返回索引i处的字符
length();//返回此字符串的长度
isEmpty();//判空 当length()为0时返回true


String s = "123456";
for(int i = 0; i < s.length(); i++)
    System.out.println(s.charAt(i)+" ");// 

输出:1 2 3 4 5 6

toCharArray();//返回此字符串的字符数组

String s = "123456";
char[] s1 = new char[10];
s1 = s.toCharArray();
for(int i = 0; i < s1.length; i++){
	System.out.print(s1[i]+" ");// 1 2 3 4 5 6

输出:1 2 3 4 5 6

String的比较

compareTo(String anotherString)//按字典顺序比较两个字符串
compareToIgnoreCase(String anotherString)//按字典顺序且不区分大小写比较两个字符串
equals(String anotherString)//判断两个字符串是否相等,相等返回true否则返回false
equalsIgnoreCase(String str)//同上,不区分大小写。
String s = "abcde";
String s1 = "Abcde";

int f = s.compareTo(s1);
int f1 = s1.compareToIgnoreCase(s);
Boolean f2 = s.equals(s1);
Boolean f3 = s.equalsIgnoreCase(s1);
	
System.out.println(f);// 32
System.out.println(f1); // 0
System.out.println(f2); // false
System.out.println(f3); // true
输出:
32
0
false
true

compareTo()和compareToIgnoreCase()方法的返回值:
a.compareTo(b)
如果a > b 返回大于0的整数
如果a == b 返回0
如果a < b 返回小于0的整数
其实是返回a和b第一个不同字符的差值。

搜索子字符串

indexOf(int ch);// 返回指定字符在此字符串中第一次出现的索引
indexOf(int ch, int fromindex); // 同上, 从指定索引开始搜索
indexOf(String str);//返回子串在此字符串中第一次出现的索引
indexOf(String str, int fromindex);同上,从指定索引开始搜索

lastIndexOf(int ch);//返回指定字符在此字符串最后一次出现的索引
lastIndexOf(int ch, int fromindex);//同上, 从指定索引开始搜索
lastIndexOf(String str);//返回子串在此字符串最后一次出现的索引
lastIndexOf(String str, int fromindex);//同上, 从指定索引开始搜索

startsWith(String prefix);// 检查是否以某一前缀开始

(以上如果不存在,均返回 -1,如果要判断某个字符,应传入字符的Unicode编码)

String s = "12345346";
String s1 = "34";
int f = s.indexOf(s1); // 从索引0开始搜索
int f1 = s.indexOf(s1, 6); // 从索引6开始搜索
int f2 = s.lastIndexOf(s1); 
Boolean f3 = s.startsWith("12");
System.out.println(f); // 2
System.out.println(f1);// -1
System.out.println(f2);// 5
System.out.println(f3);// true

输出:
在这里插入图片描述
字符串拆分
split(String regex); // 根据正则表达式拆分

String s = "ABC DEF";
String s1[] = s.split(" ");//根据空格拆分
System.out.println(s1[0]);// ABC
System.out.println(s1[1]);// DEF

输出:
在这里插入图片描述

提取子字符串
substring(int beginIndex, int endIndex);//返回从begin开始到end-1结束的子串


String s = "123456";
String s1 = s.substring(0,3);// 123
System.out.println(s1);
输出:123

子串的替换
replaceAll(String s1,String s2);//用s2替换目标字符串中出现的所有s1
replaceFirst(String s1,String s2);//用s2替换目标字符串中出现的第一个s1

String s = "11123456";
String s1 = s.replaceAll("1", "a");
String s2 = s.replaceFirst("1","a");
System.out.println(s1);///aaa23456
System.out.println(s2);///a1123456
输出:
aaa23456
a1123456

转换大小写

toUpperCase(); //将此字符串中的所有字母都换为大写
toLowerCase()//将此字符串中的所有字母都换为小写

将其他类型的数据转化为字符串

valueOf(char[] data);//返回 char数组的字符串表示形式
valueOf(char[] data,int offset, int count)//返回 char 数组参数的特定子数组的字符串表示形式。
valueOf(int i);//返回 int 参数的字符串表示形式。

int a = 10;
String s = String.valueOf(a);
System.out.print(s); // 10
输出:
10

StringBulider
一个可变的字符序列。

构造方法

StringBuilder();//构建一个空的可变字符串。
StringBuilder(String str);//构建一个值为str的可变字符串。

StringBuilder s = new StringBuilder();//
StringBuilder s1 = new StringBuilder("123456");//123456

遍历

charAt(int i);// 返回索引i位置的字符
length();//返回此字符串的长度

StringBuilder s = new StringBuilder("123");
for(int i = 0; i < s.length(); i++)
	System.out.println(s.charAt(i)+" ");// 1 2 3
	输出:123

增加

append(String str);//在此字符串追加str。
append(StringBuilder str);//在此字符串追加str。
append(char[] str, int offset, int len);//将char的子数组追加到此字符串

 StringBuilder s = new StringBuilder("123");
StringBuilder s1 = new StringBuilder("456");
s.append(s1);
System.out.print(s);// 123456

输出:123456

删除

delete(int start, int end);//移除此序列从start到end-1的字符串
deleteCharAt(int index);//移除指定索引上的char

StringBuilder s = new StringBuilder("123");
		StringBuilder s1 = new StringBuilder("456");
		s.delete(0, 1);
		s1.deleteCharAt(1); 
		System.out.println(s);// 23
		System.out.println(s1);// 46

输出:
23
46

修改
setCharAt(int index, char ch);//将指定索引处的字符替换为ch

查找

indexOf(String str);//返回子字符串第一次出现的索引
indexOf(String str, int fromIndex);//同上,从指定位置查找

lastIndexOf(String str);//返回子字符串最后一次出现的索引
lastIndexOf(String str, int fromIndex);//同上,从指定位置查找

字符串反转
reverse();//将此字符串反转

字符串截取

substring(int start);//返回此字符串从start开始至length-1结束的String
substring(int start, int end);//返回此字符串从start开始至end-1结束的String
toString();//返回此序列中的String表示形式。
(注意以上方法的返回值都是String而不是StringBuilder)

5.ArrayList(Vector)

摘要:
本文主要介绍ArrayList(Vector)和LinkedList的常用方法, 也就是动态数组和链表。

ArrayList
ArrayList 类可以实现可增长的对象数组。

构造方法

ArrayList();//构造一个空向量,使其内部数据数组的大小为 10,其标准容量增量为零。
ArrayList(int initialCapacity);//使用指定的初始容量和容量增量构造一个空的向量。

增加元素

add(E e);//将指定元素添加到末尾
add(int index, E element)//在此向量的指定位置插入指定的元素

删除元素

remove(int index);//移除此向量中指定位置的元素
clear();//从此向量中移除所有元素。

修改元素

set(int index, E element);//用指定的元素替换此向量中指定位置处的元素

查找元素

get(int index)//返回向量中指定位置的元素
indexOf(Object o)//返回此向量中第一次出现的指定元素的索引,如果此向量不包含该元素,则返回 -1。
lastIndexOf(Object o)返回此向量中最后一次出现的指定元素的索引;如果此向量不包含该元素,则返回 -1

容器大小

size();//返回此向量中的组件数。

判空

isEmpty();//测试此向量是否不包含组件

转化为数组

toArray();//返回一个数组,包含此向量中以恰当顺序存放的所有元素。

转化为字符串

toString();//返回此向量的字符串表示形式,其中包含每个元素的 String 表示形式。

拷贝
使用等号拷贝

ArrayList<Integer> Arr1 = new ArrayList<>();
ArrayList<Integer> Arr2 = new ArrayList<>();
// 将Arr2的值拷贝给Arr1
Arr1 = Arr2; // Arr1之前的值会自动被垃圾回收

以下两种方式也适用于其他集合的拷贝

使用构造函数拷贝

只要该集合的构造函数支持参数为集合就可以使用此方法拷贝

ArrayList<Integer> Arr2 = new ArrayList<>();
// 将Arr2的值拷贝给Arr1
ArrayList<Integer> Arr1 = new ArrayList<>(Arr2);

clone()

只要该集合支持clone,就可以使用此方法拷贝

ArrayList<Integer> Arr1 = new ArrayList<>();
ArrayList<Integer> Arr2 = new ArrayList<>();
// 将Arr2的值拷贝给Arr1
Arr1 = (ArrayList<Integer>) Arr2.clone();

实例

ArrayList<Integer> V = new ArrayList<>();
V.add(1);
V.add(1);
V.set(0, 0);
System.out.print(V.toString());

输出:
[0, 1]

LinkedList

LinkedList是List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

add(E e)//在末尾添加一个元素
addFirst(E e)//在开头添加一个元素
addLast(E e)//在末尾添加一个元素
offer(E e)//将指定元素添加到此列表的末尾(最后一个元素)
clear()//从此列表中移除所有元素
element()获取但不移除此列表的头(第一个元素)。
peek()//获取但不移除此列表的头(第一个元素)。
poll()//获取并移除此列表的头(第一个元素)
getFirst()返回此列表的第一个元素。
getLast()返回此列表的最后一个元素
indexOf(Object o)返回此列表中首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1lastIndexOf(Object o)返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1pop()从此列表所表示的堆栈处弹出一个元素。
push(E e)将元素推入此列表所表示的堆栈。
remove()获取并移除此列表的头(第一个元素)。
remove(int index)移除此列表中指定位置处的元素
size()//返回元素个数

大体上来说LinkedList和ArrayList的方法用法都一样,无非是get,set等方法,不过LinkedList可以用作栈和队列(链表实现),因为它包含了(
push,pop)栈的进出,(offer,poll)队列的进出等方法。
最后说一下遍历:

9. 通过迭代器

Iterator iterator=students.iterator();
    while(iterator.hasNext()){
        System.out.println(iterator.next());
    }

for循环和get方法

for(int i = 0; i < n; i++)
    System.out.println(E.get(i))

for each

for(int x : E){  //构造容器时用的什么类型,就定义什么类型。
	System.out.println(x);
}

stack和queue
上面已经说过,LinkedList可以用来实现栈和队列。
在JAVA中Stack也有专门的类,是通过Vector实现的。

实例:


Stack<Integer> Stack = new Stack<>();  //第一种栈的实现方式
LinkedList<Integer> Stack = new LinkedList<>(); // LinkedList没有实现Stack接口,但是我们只需要用其中的关于栈的方法。
Queue<Integer> Queue = new LinkedList<>();//LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。

在使用这些集合的时候,我们不需要背会所有的方法(函数),只需要知道一个大概就行了,因为编译器有方法提示嘛。

6.HashMap 和 TreeMap

摘要
本文主要介绍Map接口下的HashMap和TreeMap。

HashMap

HashMap是基于哈希表的 Map 接口的实现,是无序的

clear()//清空。
containsKey(Object key)//如果包含指定键,返回true
containsValue(Object value)//如果包含指定值, 返回true
get(Object key)//返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null。
isEmpty()//如果此映射不包含键-值映射关系,则返回 true
put(K key, V value)//在此映射中关联指定值与指定键。
remove(Object key)//从此映射中移除指定键的映射关系(如果存在)。
size()返回此映射中的键-值映射关系数。

实例

HashMap<Integer, Integer> m = new HashMap<>();//定义格式
m.put(100,1);
System.out.print(m.get(100)); // 1

TreeMap

TreeMap是基于红黑树实现的,是有序的, 可进行排序。
此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销

clear()//清空
containsKey(Object key)//如果包含指定键,返回true
containsValue(Object value)//如果包含指定值, 返回true
get(Object key)//返回指定键的值, 如果不存在返回null
firstKey()//返回此映射中当前第一个(最低)键。
lastKey()返回映射中当前最后一个(最高)键
ceilingKey(K key)返回大于等于给定键的最小键;如果不存在这样的键,则返回 null。
floorKey(K key)返回小于等于给定键的最大键;如果不存在这样的键,则返回 null。

HashMap和TreeMap的遍历方式
HashMap和TreeMap可以根据迭代器遍历,但,它俩不能直接遍历,
可以遍历键的集合, 值的集合, 键值对的集合。

通过keySet()获得键,然后根据键遍历值
使用while + Iterator

TreeMap<Integer, String> m = new TreeMap<>();
m.put(1,"A");
m.put(2,"B");
Set<Integer> s = m.keySet();
Iterator it = s.iterator();
Integer key = null; // 这里必须必须声明为类。
while(it.hasNext()){
	key = (Integer)it.next();
	String value = m.get(key);	
	System.out.print(value);
}

使用增强for

TreeMap<Integer, String> m = new TreeMap<>();
m.put(1,"A");
m.put(2,"B");
for(int k : m.keySet()){
	System.out.print(m.get(k));
}

直接通过values()遍历值
使用while 和 Iterator

TreeMap<Integer, String> m = new TreeMap<>();
m.put(1,"A");
m.put(2,"B");
Collection<String> s = m.values();
Iterator it = s.iterator();
String value = null; // 这里必须必须声明为类。
while(it.hasNext()){
	value = (String)it.next();
	System.out.print(value);
}

使用增强for


TreeMap<Integer, String> m = new TreeMap<>();
m.put(1,"A");
m.put(2,"B");
for(String value : m.values()){
	System.out.print(value);
}

7.HashSet 和 TreeSet

set容器的特点是不包含重复元素,也就是说自动去重。

HashSet

HashSet基于哈希表实现,无序。

add(E e)//如果容器中不包含此元素,则添加。
clear()//清空
contains(Object o)//查询指定元素是否存在,存在返回true
isEmpty()// 判空
iterator()//返回此容器的迭代器
remove// 如果指定元素在此set中则移除
size()//返回元素数量

TreeSet

基于红黑树实现。有序。

add(E e)// 如果不存在,则添加。
clear()//清空
contains(Object o)//查询指定元素是否存在,存在返回true
isEmpty()// 判空
iterator()//返回此容器的迭代器
remove// 如果指定元素在此set中则移除
size()//返回元素数量

以上方法都和HashSet一致,由于是红黑树实现的,所以TreeSet和TreeMap可以二分查找一个比当前元素大的最小元素,或者比当前元素小的最大元素。

ceiling(E e)//返回一个大于等于当前元素的最小元素,不存在返回null
floor(E e)//返回一个小于等于当前元素的最大元素,不存在返回null

higher(E e)//返回此 set 中严格大于给定元素的最小元素,不存在返回null
lower(E e)//返回此set中严格小于给定元素的最大元素,不存在返回null

first()//返回第一个元素
last()//返回最后一个元素

声明:

TreeSet<Integer> s = new TreeSet<>(); 
HashSet<Integer> s = new HashSet<>(); 

实例:


static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
	static TreeSet<Integer> s = new TreeSet<>();
	public static void main(String[] args) throws IOException {
		s.add(10);
		s.add(20);
		s.add(30);
		
		Iterator<Integer> it = s.iterator();
		while(it.hasNext()){ // 遍历
			int x = it.next();
			out.write(x+"\n");
		}
		out.write("\n");
		out.write(s.lower(20)+"\n"); // lower
		out.flush();
	}

输出:
10
20
30

10

8.PriorityQueue(优先队列)

PriorityQueue
翻译过来就是优先队列,本质是一个堆, 默认情况下堆顶每次都保留最小值,每插入一个元素,仍动态维护堆顶为最小值。

初始化

PriorityQueue()//使用默认的初始容量(11)创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。

PriorityQueue<Integer> Q = new PriorityQueue<>(); // 初始化

常用函数

add(E e)//将指定的元素插入此优先级队列。
clear()//清空
contains(Object o) // 如果包含指定元素返回true
iterator()//返回在此队列中的元素上进行迭代的迭代器。
offer(E e) // 将指定元素插入此优先队列
peek() // 获取第一个元素,及最小或最大元素
poll() // 获取并移除第一个
remove(Object o) // 移除指定元素
size() // 返回元素个数

实现大根堆的两种方式

因为java中的优先队列默认是小根堆,要实现大根堆可以用以下两种方法:

使用自定义比较器
将所有数据变为之前自身的负数之后在插入, 因为添加个负号就相当于是逆序了嘛。1 < 2 < 3 取负之后变为 -1 > - 2 > - 3

实例

public static void main(String[] args){
		Scanner in = new Scanner(new InputStreamReader(System.in));
		PriorityQueue<Integer> Q = new PriorityQueue<>();
		int a;
		for(int i = 0; i < 10; i++){
			a = in.nextInt();
			Q.add(a);
			System.out.print(Q.peek()+" ");// 输出当前队列的最小元素
		}
	}

输入: 1 -1 -2 -3 10 -4 -5 -6 -7 -8
输出: 1 -1 -2 -3 -3 -4 -5 -6 -7 -8

9.sort方法和自定义比较器的写法

摘要
在做一些算法题时常常会需要对数组、自定义对象、集合进行排序. 在java中对数组排序提供了Arrays.sort()方法,对集合排序提供Collections.sort()方法。对自定义对象排序时要自己重写比较器,对象数组则调用Arrays.sort(),对象集合则调用Collections.sort()。两个方法默认都是升序,也可以重写比较器,实现降序。

对数组排序
sort函数模板, 以int型数组arr为例:

Arrays.sort(arr, new Comparator<Integer>() { // arr是数组名,<>中是待排序集合所包含的数据类型
	public int compare(int a, int b){  // 待排序集合中的元素是什么数据类型,这里的两个函数参数就定义为什么数据类型
		 return a - b;   升序
		// return b - a;   降序
		// a - b > 0 交换ab位置,反之不变, 即返回值为正数时,交换数组中正在比较的
		//两个元素的位置,返回值为负数时,不交换。 
		}
	})

例题: 快速排序


给定你一个长度为n的整数数列。

请你使用快速排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式
输入共两行,第一行包含整数 n。

第二行包含 n 个整数(所有整数均在1~109范围内),表示整个数列。

输出格式
输出共一行,包含 n 个整数,表示排好序的数列。

数据范围
1≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5

可以将字符串数组转化为整型数组之后在排序,为了演示自定义比较器的写法这里直接对字符串数组进行排序:


import java.io.*;
import java.util.*;

public class Main{
	// 输入输出模板
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
	
	static int n;
	public static void main(String[] args) throws IOException {
		n = Integer.parseInt(in.readLine());
		String s[] = in.readLine().split(" ");// 读入数据
		
		Arrays.sort(s, new Comparator<String>() { // 排序
			public int compare(String a, String b) {  
	        	if(a.length() == b.length()){ // 如果长度相等则直接比较字典序
	        		return a.compareTo(b);
	        	}
	        	else{ // 长度长的一定大
	        		return a.length() - b.length();
	        	}
			}
		});
		
		for(String p : s){
			out.write(p+" ");
		}
		
		out.flush();
	}
}

对集合进行排序
创建TreeSet实例,对其从大到小排序。
因为TreeSet是自动排序和去重的, 默认为升序,我们可以重写比较器构造一个降序的TreeSet, 之后添加数据就会自动排序。

import java.io.*;
import java.util.*;

public class Main{
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
	
	public static void main(String[] args) throws IOException {
		
		TreeSet<Integer> s = new TreeSet<>(new Comparator<Integer>(){
			public int compare(Integer a, Integer b) {  
	        	return b - a; 
			}
		});
		
		s.add(10);
		s.add(5);
		s.add(4);
		s.add(6);
		s.add(7);
		s.add(8);
		s.add(1);
		s.add(2);
		s.add(3);
		s.add(9);
		
		for(Integer p : s){
			out.write(p+" ");
		}
		out.flush();
	}
}
输出:
10 9 8 7 6 5 4 3 2 1

对自定义对象数组排序
创建学生类, 按照年龄从小到大排序

import java.io.*;
import java.util.*;

class student{ 
	int age;
	String name;
	student(int a, String b){
		this.age = a;
		this.name = b;
	}
}
public class Main{
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
	
	public static void main(String[] args) throws IOException {
		student s[] = new student[4]; //创建学生对象数组
		s[0] = new student(10, "1");
		s[1] = new student(9, "2");
		s[2] = new student(8, "3");
		s[3] = new student(7, "4");
		
		Arrays.sort(s, new Comparator<student>() {
			public int compare(student a, student b) {  //
	        	return a.age - b.age; // 按照年龄大小升序排列
			}
		});
		for(student p : s){
			out.write(p.age+" "+p.name + "\n");
		}
		out.flush();
	}
}

输出:
7 4
8 3
9 2
10 1

大致就是这样了,还可以对要排序的类实现Comparable接口,重写compareTo方法。
代码:

//对pair类实现Comparable接口后,直接调用sort函数排序就行了。
static class pair implements Comparable<pair>{
        int a, b, w;
        pair(int u, int v, int x){
            a = u;
            b = v;
            w = x;
        }
        public int compareTo(pair p) {
    		return this.w - p.w;
    	}
    }
  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值