目录
1.快速排序
import java.util.*;
public class Main{
private static int N = 100010;
private static int[] q = new int[N];
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
for(int i = 0; i <n; i++){
q[i] = scanner.nextInt();
}
int l = 0, r = n - 1;
quicksort(q, l, r);
for(int i = 0; i < n; i++){
System.out.print(q[i] + " ");
}
}
//快排模版
public static void quicksort(int[] q, int l, int r){
if (l >= r){
return;
}
int x = q[l + r >> 1];
int i = l - 1;
int j = r + 1;
while(i < j){
do i ++; while (q[i] < x);//找到第一个大于等于x的数,不能取=,因为第一个数可能比后面所有数都大,必须交换第一个数
do j --; while (q[j] > x);//找到第一个小于等于x的数,不能取=
// 交换两个数,保证i左边数小于等于x,j右边数大于等于x
if(i < j){
int tmp = q[i];
q[i] = q[j];
q[j] = tmp;
}
}
quicksort(q, l ,j); // j有可能小于i,所以取j
quicksort(q, j + 1, r);
}
}
2.归并排序
import java.util.*;
public class Main{
private static int N = 100010;
private static int[] q = new int[N];
private static int[] tmp = new int[N];
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
for(int i = 0; i < n; i ++){
q[i] = scan.nextInt();
}
int l = 0, r = n - 1;
merge_sort(q, l, r);
for(int i = 0; i < n; i++){
System.out.print(q[i] + " ");
}
}
//归并模版
public static void merge_sort(int[] q, int l, int r){
if(l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int i = l, j = mid + 1, id = 0;
while(i <= mid && j <= r){
if(q[i] <= q[j]) tmp[id ++] = q[i ++];
else tmp[id ++] = q[j ++];
}
while(i <= mid) tmp[id ++] = q[i ++];
while(j <= r) tmp[id ++] = q[j ++];
for(i = l, id = 0; i <= r; i ++, id ++) q[i] = tmp[id];
}
}
3.整数二分(两个模版)
// 区间[l, r]被划分成 [l, mid] 和 [mid+1, r]时使用
int bsearch_1(int l, int r) {
while (l < r) {
int mid = l + r >> 1;
if (check[mid) // check() 判断 mid是否满足性质
r = mid;
else
l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成 [l, mid-1] 和 [mid, r]时使用
int bsearch_2(int l, int r) {
while (l < r) {
int mid = l + r + 1 >> 2; // 注意
if (check[mid) // check() 判断 mid是否满足性质
l = mid;
else
r = mid - 1;
}
return l;
}
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int q = scan.nextInt();
int arr[] = new int[n];
for(int i = 0; i < n; i++){
arr[i] = scan.nextInt();
}
while(q-- != 0){
int target = scan.nextInt();
int l = 0, r = n - 1;
while(l < r){
int mid = l + r >> 1;
if(arr[mid] >= target) r = mid;
else l = mid + 1;
}
if(arr[l] != target){
System.out.println("-1 -1");
}else{
System.out.print(l + " ");
int i = 0, j = n - 1;
while(i < j){
int mid = i + j + 1 >>1;
if(arr[mid] <= target) i = mid;
else j = mid - 1;
}
System.out.println(j);
}
}
}
}
4.浮点数二分模版
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
double target = scan.nextDouble();
double l = -10000, r = 10000;
while(r - l > 1e-8){
double mid = (l + r) / 2;
if(mid * mid * mid >= target) r = mid;
else l = mid;
}
System.out.println(String.format("%.6f", l));
}
}
ps:模版
double bsearch(double l, double r) {
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求, 一般比所求精度高 2
while (r - l > eps) {
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
5.高精度计算
1)加法
import java.util.*;
public class Main{
public static String sum(List<Integer> a,List<Integer> b) {
int[] c = new int[100010];
int t = 0,i;
for(i = 0;i < a.size() || i < b.size(); i ++) {
if(i < a.size()) c[i] = t += a.get(i);
if(i < b.size()) c[i] = t += b.get(i);
c[i] = t % 10;
t = t/10;
}
if(t > 0) c[i] = t;
while(i >= 1 && c[i] == 0) i --;
StringBuilder stb = new StringBuilder();
for(int j = i; j >= 0; j --) {
stb.append(c[j]);
}
return stb.toString();
}
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
String a = sc.nextLine();
String b = sc.nextLine();
List<Integer> A =new ArrayList<>(a.length());
List<Integer> B =new ArrayList<>(b.length());
for(int i = a.length() - 1;i >= 0;i --) {
A.add(a.charAt(i) - '0');
}
for(int i = b.length() - 1;i >= 0;i --) {
B.add(b.charAt(i) - '0');
}
System.out.print(sum(A,B));
}
}
2)减法
import java.util.*;
public class Main {
// 判断是否有 A >= B
public static boolean cmp(List<Integer> A, List<Integer> B) {
if (A.size() != B.size()) return A.size() > B.size();
for (int i = A.size() - 1; i >= 0; i--) {
if (A.get(i) != B.get(i)) return A.get(i) > B.get(i);
}
return true;
}
public static List<Integer> sub(List<Integer> A, List<Integer> B) {
List<Integer> C = new ArrayList();
int t = 0; // 差
for (int i = 0; i < A.size(); i++) {
t += A.get(i);
if (i < B.size()) t -= B.get(i);
C.add((t + 10) % 10); // t >= 0: t; t < 0 : t + 10(借位)
if (t < 0) t = -1;
else t = 0;
}
while(C.size() > 1 && C.get(C.size() - 1) == 0) C.remove(C.size() - 1); // 删除前导0
return C;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String a = scan.nextLine();
String b = scan.nextLine();
List<Integer> A = new ArrayList(), B = new ArrayList();
for (int i = a.length() - 1; i >= 0; i--) A.add(a.charAt(i) - '0');
for (int i = b.length() - 1; i >= 0; i--) B.add(b.charAt(i) - '0');
if (cmp(A, B)) {
List<Integer> C = sub(A, B);
for (int i = C.size() - 1; i >= 0; i--) System.out.print(C.get(i));
}
else {
System.out.print("-");
List<Integer> C = sub(B, A);
for (int i = C.size() - 1; i >= 0; i--) System.out.print(C.get(i));
}
}
}
3)乘法
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
String a = scan.nextLine();
int b = scan.nextInt();
List<Integer> A = new ArrayList<>();
for(int i = a.length() - 1; i >= 0; i--) A.add(a.charAt(i) - '0');
List<Integer> C = sub(A, b);
for(int i = C.size() - 1; i >= 0; i--) System.out.print(C.get(i));
}
public static List<Integer> sub(List<Integer> A, int b){
int t = 0;
for(int i = 0; i < A.size(); i++){
t += A.get(i) * b;
A.set(i, t % 10);
t = t / 10;
}
if(t != 0) A.add(t);
while(A.size() > 1 && A.get(A.size() - 1) == 0) A.remove(A.size() - 1);
return A;
}
}
4)除法
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
String a =scan.next();
int b = scan.nextInt();
int[] A = new int[a.length()];
//除法不需要逆向输入
for(int i = 0; i < a.length(); i ++){
A[i] = a.charAt(i) - '0';
}
div(A, b);
}
public static void div(int[] a, int b){
int i;
int r = 0;
int[] c = new int[a.length + 1];
for(i = 0; i < a.length; i++){
r = r * 10 + a[i];
c[i] = r / b;
r %= b;
}
int len = i;
i = 0;
while(c[i] == 0 && i < a.length - 1 ) i ++; //去前导0;
for(int j = i; j < len; j++){
System.out.print(c[j]);
}
System.out.println();
System.out.print(r);
}
}
6.前缀和与差分
1)一维数组前缀和
// 求a[]数组的前缀和:
for(int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i - 1]; // 前缀和下标[1, n],数组a下标[0, n - 1]
sum[l, r] = s[r] - s[l - 1] // 第l个数 到 第r个数 的和
import java.util.*;
public class Main {
private static int N = 100010; // 定义数组大小, 防止越界
public static void main(String[] args) {
// 初始化输入值
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
int[] arr = new int[N];
// 注意这里是从 1开始的
for (int i = 1; i <= n; i++)
arr[i] = in.nextInt();
// s[i]代表 arr的前 i项和
int s[] = new int[N];
s[0] = 0;
// 计算出 s[i]
for (int i = 1; i <= n; i++)
s[i] = s[i - 1] + arr[i]; // 注意运算方式
// 循环输出
while (m-- > 0) {
int l = in.nextInt();
int r = in.nextInt();
System.out.println(s[r] - s[l - 1]); // 关键
}
}
}
2)二维前缀和
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int q = scan.nextInt();
int[][] arr = new int[n + 1][m + 1];
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
arr[i][j] = scan.nextInt();
}
}
int[][] s = new int[n + 1][m + 1];
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + arr[i][j];
}
}
while(q -- > 0){
int x1 = scan.nextInt();
int y1 = scan.nextInt();
int x2 = scan.nextInt();
int y2 = scan.nextInt();
System.out.println(s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
}
}
}
// 求a[][]数组左上角元素的和:
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m;j++)
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i - 1][j - 1];
int x1, y1, x2, y2;
s[x2][y2] - s[x1 - 1][y2] - s[x1][y1 - 1] + s[x1 - 1][y1 - 1] // 矩形区域和
3)差分
差分就是前缀和的逆运算
import java.util.*;
public class Main{
private static int N = 100010;
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int[] a = new int[N];
for(int i = 1; i <= n; i++) a[i] = scan.nextInt();
int[] b = new int[N];
for(int i = 1; i <= n; i++) b[i] = a[i] - a[i - 1];
while(m -- > 0){
int l = scan.nextInt();
int r = scan.nextInt();
int c = scan.nextInt();
b[l] += c;
b[r + 1] -= c;
}
for(int i = 1; i <= n; i++) b[i] += b[i - 1];
for(int i = 1; i <= n; i++) System.out.print(b[i]+" ");
}
}
//1:初始化差分数组b[]
int[] b = new int[N];
for(int i = 1 ; i <= n ; i++) b[i] = s[i] - s[i-1];
//2:在前缀和数组的[l,r]区间上加c 等价于 在差分数组上b[l] += c;b[r+1] -= c;
b[l] += c;
b[r + 1] -= c;
//3:把差分数组转化为为操作完后的前缀和数组
for(int i = 1 ; i <= n ; i++) b[i] = b[i-1] + b[i];
4)差分矩阵
import java.util.*;
public class Main{
private static int N = 1010;
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int q = scan.nextInt();
int[][] arr = new int[N][N];
int[][] dif = new int[N][N];
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
arr[i][j] = scan.nextInt();
insert(i, j, i, j, dif, arr[i][j]);
}
}
while(q -- > 0){
int x1 = scan.nextInt();
int y1 = scan.nextInt();
int x2 = scan.nextInt();
int y2 = scan.nextInt();
int t = scan.nextInt();
insert(x1, y1, x2, y2, dif, t);
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
dif[i][j] = dif[i][j] + dif[i - 1][j] + dif[i][j - 1] - dif[i - 1][j - 1];
System.out.print(dif[i][j] + " ");
}
System.out.println();
}
scan.close();
}
private static void insert(int x1, int y1, int x2, int y2, int[][] dif, int t){
dif[x1][y1] += t;
dif[x1][y2 + 1] -= t;
dif[x2 + 1][y1] -= t;
dif[x2 + 1][y2 + 1] += t;
}
}
7.双指针算法
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
import java.util.*;
public class Main{
private static int N = 100010;
private static int[] a = new int[N];
private static int[] s = new int[N];
public static void main(String[] args){
int ans = 0;
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
for(int i = 0; i < n ;i ++){
a[i] = scan.nextInt();
}
for(int i = 0,j = 0;i < n;i++){
s[a[i]]++;
while(s[a[i]] > 1){
s[a[j]]--;
j++;
}
ans = Math.max(ans, i - j + 1);
}
System.out.println(ans);
}
}
8.位运算
1)获取数字 n的二进制的第 k位是多少
for (int i = n; i >= 0; i--) {
System.out.print(x >> i & 1);
}
//先将二进制数向右移动 k位(此时 k位移动到了第一位), 操作: x >> i
//获取最后一位的值, 操作: 移动后的值 & 1
//两步和为一步操作: x >> i & 1
2)lowBit
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
while(n -- > 0){
int tmp = scan.nextInt();
int ans = 0;
while(tmp != 0){
tmp -= tmp & (-tmp);
ans++;
}
System.out.print(ans + " ");
}
}
}
模版:int lowbit(int x){
return x & -x;
//-x = ~x + 1 //(x取反加1)
}
9.离散化
import java.util.*;
// 存储add中的x, c; query中的l, r
class Pair {
int x, y;
Pair(int x, int y) {
this.x = x;
this.y = y;
}
}
public class Main {
public static void main(String[] args) {
List<Pair> add = new ArrayList(); // 在x位置加c
List<Pair> query = new ArrayList(); // 询问[l, r]区间和
Set<Integer> set = new TreeSet(); // 涉及到的位置 x, l, r
Map<Integer, Integer> map = new HashMap(); // 将位置映射到[1, n]
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
for (int i = 0; i < n; i++) {
int x = scan.nextInt();
int c = scan.nextInt();
add.add(new Pair(x, c)); // 输入x, c
set.add(x); // 坐标x加入有序set
}
for (int i = 0; i < m; i++) {
int l = scan.nextInt();
int r = scan.nextInt();
query.add(new Pair(l, r)); // 输入l, r
set.add(l); // 坐标l, r加入set
set.add(r);
}
int index = 1;
for (int x : set) {
map.put(x, index++); // 将坐标映射到[1, n]
}
int k = set.size();
int[] nums = new int[k + 1];
int[] sum = new int[k + 1];
// 坐标x映射到index,给num[index]加上c
for (int i = 0; i < n; i++) {
Pair p = add.get(i);
index = map.get(p.x);
nums[index] += p.y;
}
// 求区间和
for (int i = 1; i <= k; i++) {
sum[i] = sum[i - 1] + nums[i];
}
// 将询问映射到[1, n]的坐标,求解区间和
for (int i = 0; i < m; i++) {
int l = query.get(i).x;
int r = query.get(i).y;
int ans = sum[map.get(r)] - sum[map.get(l) - 1];
System.out.println(ans);
}
}
}
10.区间合并
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[][] a = new int[n][2];
for(int i = 0 ;i < n; i++){
a[i][0] = scan.nextInt();
a[i][1] = scan.nextInt();
}
Arrays.sort(a, new Comparator<int[]>(){
public int compare(int[] o1, int[] o2){
return o1[0] - o2[0];
}
});
int res = 0;
int r = Integer.MIN_VALUE;
for(int i = 0; i < n; i++){
if(r < a[i][0]){
res ++;
}
r = Math.max(r, a[i][1]);
}
System.out.println(res);
}
}