一、常用数据类型及其方法
队列
//两种初始化方法(ArrayDeque和LinkedList都是Deque的子类)
Queue<Integer> queue = new LinkedList<Integer>();
Queue<Integer> queue = new ArrayDeque<Integer>();
queue.offer(1); //元素入队
queue.poll(1); //队头元素出队
queue.peek(); //返回队头元素,元素不出队
BigInteger类(数据范围理论上没有限制,只取决于电脑内存)
BigInteger a = new BigInteger("2");
BigInteger b = new BigInteger("3"); //初始化
a.add(b);
a.subtract(b);
a.multiply(b);
a.devide(b); // 加减乘除
a.compareTo(b); //比较大小,返回值是1、-1、0对应大于、小于、等于
a.remainder(b); //取余
a.abs(); //取绝对值
a.pow(3); //取几次幂
a.toString(); //转为字符串
文件操作相关
// 改变System.out.println()的输出流,让它输出到一个文件里
PrintStream ps = new PrintStream(new FileOutputStream("D:\\work.txt"));
System.setOut(ps);
字符串转为整数:Integer.parseInt()、Integer.valueOf()
整数转字符串:Integer.toString()、String.valueOf()、"+"号(注意:3+5+"8"和3+""+5+"8"的区别)
字符串转为字符数组: String.toCharArray()
拆分字符串:String.split()
截取字符串:String.substring(int beginIndex, int endIndex)
比较字符串:String 中 == 比较引用地址是否相同,equals() 比较字符串的内容是否相同
判断字符串中是否存在某个字符或子串:String.indexOf()、String.lastIndexOf()、String.contains()
二维list: List<List<Integer>> ans = new ArrayList<List<Integer>>()
数组排序: Arrays.sort()(给静态数组排序)、Collections.sort()(给动态数组排序,如ArrayList、LinkedList、HashSet等)
Scanner.nextLine():用nextLine()读数据,注意要不要额外接收一个enter键
数组全体赋值: Arrays.fill()
进制转换:
Integer.toBinaryString(i) 十进制转二进制
Integer.toOctalString(i) 十进制转八进制
Integer.toHexString(i) 十进制转十六进制
Integer.toString(int i, int radix) 十进制i转为radix进制(radix默认值是10)
Integer.parseInt((String) s,(int) a) a进制的字符串s转为十进制
模运算法则:
加法:( a + b ) % p = ( a % p + b % p ) % p
减法:( a - b ) % p = ( a % p - b % p + p ) % p
乘法:( a * b ) % p = ( a % p * b % p ) % p
乘方:( a^b ) % p = ( ( a % p )^b ) % p
日期:
//Date类
Date date=new Date();
System.out.print("当前时间"+date);
System.out.print("年份"+(date.getYear()+1900));
System.out.print("月份"+(date.getMonth()+1));
System.out.print("日"+(date.getDate()));
正则:
①构造一个模式.
Pattern p=Pattern.compile("[a-z]*");
②建造一个匹配器
Matcher m = p.matcher(str);
③进行判断,得到结果
boolean b = m.matches();
boolean b = m.find();
//分割字符串
Pattern.split(String s)
StringBuffer、StringBuilder
StringBuffer是线程安全的,而StringBuilder不是线程安全的
因为在StringBuffer的方法上使用synchronized做了修饰。这样的方法一次只能一个人用。
HashSet、TreeSet
HashSet<String> sites = new HashSet<String>();
sites.add("Google"); //增加元素
sites.remove("Taobao"); //删除元素
sites.clear(); //删除集合中所有元素
sites.contains("Taobao"); //判断元素是否在集合当中
sites.size(); //计算HashSet中的元素数量
HashMap、TreeMap
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
Sites.put(1, "Google"); // 添加键值对
Sites.remove(1); // 删除key对应的键值对
Sites.clear(); // 删除所有键值对
Sites.size(); // 计算HashMap中的元素数量
//遍历哈希表
//第一种
for (Integer i : Sites.keySet()) {
System.out.println("key: " + i + " value: " + Sites.get(i));
}
//第二种
for (Map.Entry<Integer, Integer> entry : sites.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
二、双指针
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例:输入: "abcabcbb"
输出: 3
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>(); //哈希表用于判断每个字符是否出现过
int n = s.length();
int ans = 0;
int r = -1;
for(int i = 0; i < n; i++) {
if(i != 0) {
//左指针就是i,每次循环加1,并在set中删掉之前指着的元素
set.remove(s.charAt(i-1));
}
//如果不包含重复字符,右指针就右移
while(r + 1 < n && !set.contains(s.charAt(r+1))) {
r++;
set.add(s.charAt(r));
}
//左指针为i,最长无重复子串长度就是r-i+1
ans = Math.max(ans, r-i+1);
}
return ans;
}
}
三、DFS
在一个n×n的棋盘中,放入n个皇后,要求皇后之间不能互相攻击,输出所有的方案。
一个皇后,能攻击同一行,同一列,同一对角线的的所有格子。
输入格式
第一行输入一个整数n。
输出格式
输出若干行,每行输出n个整数,依次表示第1,2,…,n行的皇后的所在的列。
按字典序从小到大输出。
优化:结果先存起来,最后一起输出,比中间多次print要快很多
import java.util.Scanner;
public class Main {
static int n;
static int[] col;
static StringBuilder ans = new StringBuilder(); //记录答案
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
col = new int[n+1];
dfs(1);
System.out.println(ans);
sc.close();
}
public static void dfs(int row) {
if(row == n+1) {
for(int i = 1; i <= n; i++) {
ans.append(col[i]+" ");
}
ans.append("\n");
return;
}
for(int i = 1; i <= n; i++) {
int j;
for(j = 1; j < row; j++) {
if(i == col[j] || Math.abs(row-j) == Math.abs(col[j]-i)) {
break;
}
}
if(j == row) {
col[row] = i;
dfs(row+1);
}
}
}
}
四、BFS
一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。
输入格式
第一行两个整数代表矩阵大小 n 和 m。
接下来 n 行,每行一个长度为 m 的只含字符 0
到 9
的字符串,代表这个 n×m 的矩阵。
输出格式
一行一个整数代表细胞个数。
输入
4 10
0234500067
1034560500
2045600671
0000000089
输出:4
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
static int n, m;
static char[][] map; // 存储地图数据
static int[][] dirs = { { -1, 0 }, { 1, 0 }, { 0, 1 }, { 0, -1 } };
static boolean[][] vis;
static int ans = 0;
private static class Point {
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
sc.nextLine();
map = new char[n][m];
vis = new boolean[n][m];
for (int i = 0; i < n; i++) {
map[i] = sc.nextLine().toCharArray();
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!vis[i][j] && map[i][j] != '0') {
bfs(i, j); // 如果没被搜索过且是数字1-9,BFS
}
}
}
System.out.println(ans);
sc.close();
}
private static void bfs(int x, int y) {
ans++; // 新的细胞
vis[x][y] = true;
Queue<Point> queue = new LinkedList<>();
queue.offer(new Point(x, y));
while (!queue.isEmpty()) {
Point head = queue.poll();
for (int i = 0; i < 4; i++) {
int nx = head.x + dirs[i][0];
int ny = head.y + dirs[i][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && !vis[nx][ny] && map[nx][ny] > '0') {
queue.offer(new Point(nx, ny));
vis[nx][ny] = true;
}
}
}
}
}
五、动态规划
线性dp
给定一个长度为 nn 的数组 a1,a2,…,an,问其中的最长上升子序列的长度。也就是说,我们要找到最大的 m 以及数组 p1,p2,…,pm,满足 1≤p1<p2<⋯<pm≤n 并且 ap1<ap2<⋯<apm。
输入格式
第一行一个数字 n。
接下来一行 n 个整数 a1,a2,…,an。
输出格式
一个数,表示答案。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n + 1];
int[] dp = new int[n + 1]; // dp[i]表示以ai为"终点"的最长上升子序列的长度
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
dp[i] = 1;
}
// i=1, dp[i] = 1
// i>1,dp[i] = max{ dp[j]: 1 <= j < i 且 aj < ai} + 1
// 如果aj(1<j<i)都大于ai,那么dp[i] = 1
for (int i = 2; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (a[i] > a[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
int max = 0;
for (int i = 1; i <= n; i++) {
max = Math.max(max, dp[i]);
}
System.out.println(max);
sc.close();
}
}
背包
有n种物品要放到一个袋子里,袋子的总容量为m,第i种物品的体积为vi,把它放进袋子里会获得wi的收益,每种物品至多能用一次,问如何选择物品,使得在物品的总体积不超过m的情况下,获得最大的收益?请求出最大收益。
输入格式
第一行两个整数n,m。
接下来n行,每行两个整数vi,wi。
输出格式
一个整数,表示答案
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[] v = new int[n + 1];
int[] w = new int[n + 1];
int[] dp = new int[m + 1]; // 用dp[i]表示容积还剩i,用最优法取得的价值总和
for (int i = 1; i <= n; i++) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
System.out.print(dp[m]);
sc.close();
}
}
有n种物品要放到一个袋子里,袋子的总容量为m,第i种物品的体积为vi,把它放进袋子里会获得wi的收益,每种物品能用无限多次,问如何选择物品,使得在物品的总体积不超过m的情况下,获得最大的收益?请求出最大收益。
输入格式
第一行两个整数n,m。
接下来n行,每行两个整数vi,wi。
输出格式
一个整数,表示答案。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[] v = new int[n + 1];
int[] w = new int[n + 1];
int[] dp = new int[m + 1]; // 用dp[i]表示容积还剩 i,用最优法取得的价值总和
for (int i = 1; i <= n; i++) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = v[i]; j <= m; j++) {
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
System.out.print(dp[m]);
sc.close();
}
}
区间dp
有 n 堆石子排成一排,第 i 堆石子有 ai 颗,每次我们可以选择相邻的两堆石子合并,代价是两堆石子数目的和,现在我们要一直合并这些石子,使得最后只剩下一堆石子,问总代价最少是多少?
输入格式
第一行一个数字n。
接下来一行n个整数a1,a2,…,an。
输出格式
一行一个整数,表示答案。
import java.util.Scanner;
public class Main {
static int[] stones;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
stones = new int[n + 1];
int[][] dp = new int[n + 1][n + 1]; // dp[i][j]表示区间[ i,j]内的石子合并的最优解
int[] prefix_sum = new int[n + 1]; // 用前缀和计算区间元素和
for (int i = 1; i <= n; i++) {
stones[i] = sc.nextInt();
prefix_sum[i] = prefix_sum[i - 1] + stones[i];
}
// dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum(i,j));(i<=k<=j-1)
// sum(i,j)表示区间[i,j]所有元素之和
// namely,把i到j看成一堆,这一堆的最优解是由 由这堆分割的两小堆合并的
// 枚举区间长度,从2开始,如果从1开始的话就只有一堆了不需要合并
for (int interval = 2; interval <= n; interval++) {
for (int i = 1; i + interval - 1 <= n; i++) {
int j = i + interval - 1; // 右边界
dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j] + prefix_sum[j] - prefix_sum[i - 1]);
}
}
}
System.out.println(dp[1][n]);
sc.close();
}
}
六、图论
七、其它
全排列模板
public class Main {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4 };
all_permutation(arr, 0); // 全排列
}
public static void all_permutation(int[] arr, int index) {
if (index == arr.length) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
return;
}
// index从0到arr.length-1,arr[index]的值用arr[index]到arr[arr.length-1]各试一遍
for (int i = index; i < arr.length; i++) {
swap(arr, index, i);
all_permutation(arr, index + 1);
swap(arr, index, i);
}
}
private static void swap(int[] arr, int index, int i) {
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
最大公约数
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a, b;
while (sc.hasNextInt()) {
a = sc.nextInt();
b = sc.nextInt();
System.out.println(gcd(a, b));
}
}
private static int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
}
最小公倍数
public static int lcm(int a,int b) {
int A = a, B = b;
while(B!=0) {
int temp = B;
B = A%B;
A = temp;
}
return a*b/A;
}
快速幂
给定正整数 n, 求 1^8 + 2^8 + ··· + n^8 mod 123456789 。
import java.util.Scanner;
public class Main {
static int mod = 123456789;
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int n = s.nextInt();
long ans = 0;
for(long i = 1; i <= n; ++i) {
ans = (ans+quickPow(i,8))%mod; //long型的i防止计算时溢出
}
System.out.print(ans);
s.close();
}
static long quickPow(long a, int b) {
long ans = 1;
while(b>0) {
if((b&1) == 1) {
ans = (ans*a)%mod;
}
a = (a*a)%mod; //记录位权
b >>= 1;
}
return ans;
}
}
组合数
//求组合数
private static long C(long a, long b) {
long res = 1;
for (long i = a, j = 1; j <= b; i --, j ++ ) {
res = res * i / j;
}
return res;
}