位运算
- 获取数字 n的二进制的第 k位是多少
步骤
- 先将二进制数向右移动 k位(此时 k位移动到了第一位), 操作: x >> k
- 获取最后一位的值, 操作: 移动后的值 & 1
- 两步和为一步操作: x >> k & 1
获取 n的二进制
for (int i = n; i >= 0; i--) {
System.out.print(x >> i & 1);
}
- lowBit
返回 x的二进制数的最后为 1的后面的数
如 100100返回 100, 十进制直接返回十进制
代码
class Solution {
public int NumberOf(int n) {
int res = 0;
while (n != 0) {
n -= lowBit(n); // 减去并记录
res++;
}
return res;
}
private int lowBit(int x) {
return x & -x;
}
}
-
例题一: 二进制中1的个数
-
题目
-
给定一个长度为 n的数列,请你求出数列中每个数的二进制表示中 1 的个数。
输入格式
第一行包含整数 n。
第二行包含 n 个整数,表示整个数列。
输出格式
共一行,包含 n 个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中 1 的个数。
数据范围
1≤n≤100000
0 ≤ 数 列 中 元 素 的 值 ≤ 1 0 9 0≤数列中元素的值≤10^{9} 0≤数列中元素的值≤109输入样例:
5 1 2 3 4 5
输出样例:
1 1 2 1 2
-
题解
-
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner=new Scanner(System.in); int n=scanner.nextInt(); while (n-- >0) { int x; x=scanner.nextInt(); int res=0; while(x>0) { x-=lowBit(x); res++; } System.out.print(res+" "); } } private static int lowBit(int x) { return x&-x; } }
-
-
补充
-
原码: 数字 n的二进制
如: 10的原码:1010
- 反码: 将原码取反
如: 10的反码:0101
- 补码: 反码 +1
如: 10的反码:0110
-
-
总结
求 n的第 k位数字: n >> k & 1
返回 n的最后一位 1: lowbit(n) = n & -n
双指针算法
核心模板
-
for (int i = 0, j = 0; i < n; i ++ ) { while (j < i && check(i, j)) j ++ ; // 具体问题的逻辑 } 常见问题分类: (1) 对于一个序列,用两个指针维护一段区间 (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
例题
-
例题一 最长连续不重复子序列
给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
输入格式
第一行包含整数 n。
第二行包含 n 个整数(均在 0∼105 范围内),表示整数序列。
输出格式
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
数据范围
1≤n≤105
输入样例:
5 1 2 2 3 5
输出样例:
3
-
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class Main { static int N=100010; static int[] a=new int[N]; static int[] s=new int[N]; public static void main(String[] args) throws NumberFormatException, IOException { BufferedReader reader=new BufferedReader(new InputStreamReader(System.in)); int n=Integer.parseInt(reader.readLine()); String[] strings=reader.readLine().split(" "); for (int i = 0; i < n; i++) { a[i]=Integer.parseInt(strings[i]); } int res=0; for(int i=0,j=0;i<n;i++) { s[a[i]]++; while(s[a[i]]>1) { s[a[j]]--; j++; } res=Math.max(res,i-j+1); } System.out.println(res); } }
-
例题二:数组元素的目标和
-
给定两个升序排序的有序数组 A和 B,以及一个目标值 x。
数组下标从 0开始。
请你求出满足 A[i]+B[j]=x的数对 (i,j)。
数据保证有唯一解。
输入格式
第一行包含三个整数 n,m,x分别表示 A 的长度,B 的长度以及目标值 x。
第二行包含 n 个整数,表示数组 A。
第三行包含 m 个整数,表示数组 B。
输出格式
共一行,包含两个整数 i和 j。
数据范围
数组长度不超过 105。
同一数组内元素各不相同。
1≤数组元素≤109输入样例:
4 5 6 1 2 4 7 3 4 6 8 9
输出样例:
1 1
-
题解
-
import java.util.Scanner; public class Main { static int n, m, x; static int N = 100010; static int[] a = new int[N], b = new int[N]; public static void main(String[] args){ Scanner sc = new Scanner(System.in); n = sc.nextInt(); m = sc.nextInt(); x = sc.nextInt(); for(int i = 0; i < n; i ++){ a[i] = sc.nextInt(); } for(int j = 0; j < m; j ++){ b[j] = sc.nextInt(); } for(int i = 0, j = m - 1; i < n; i ++){ while(j > 0 && a[i] + b[j] > x) j --; if(a[i] + b[j] == x) { System.out.println(i + " " + j); break; } } } }
-
例题三:判断子序列
-
给定一个长度为 n的整数序列 a1,a2,…,an以及一个长度为 mm 的整数序列 b1,b2,…,bm。
请你判断 a序列是否为 b 序列的子序列。
子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5} 是序列 {a1,a2,a3,a4,a5} 的一个子序列。
输入格式
第一行包含两个整数 n,m。
第二行包含 n 个整数,表示 a1,a2,…,an。
第三行包含 m 个整数,表示 b1,b2,…,bm。
输出格式
如果 aa序列是 b 序列的子序列,输出一行
Yes
。否则,输出
No
。数据范围
1≤n≤m≤105
-(1e9)≤ai,bi≤1e9输入样例:
3 5 1 3 5 1 2 3 4 5
输出样例:
Yes
-
题解
-
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 a[]=new int[n]; int b[]=new int[m]; for(int i=0;i<a.length;i++) { a[i]=sc.nextInt(); } for(int i=0;i<b.length;i++) { b[i]=sc.nextInt(); } int i=0; int j=0; while(i<a.length&&j<b.length) { if(b[j]==a[i]) { i++; j++; }else{ j++; } } if(i==a.length) { System.out.println("Yes"); }else { System.out.println("No"); } } }
离散化
核心内容
// 存储所有待离散化的值
List<Integer> alls = new ArrayList<>();
// 将所有值排序
Collections.sort(alls);
// 去掉重复元素
public static int unique(List<Integer> list) {
int j = 0;
for (int i = 0; i < list.size(); i++) {
if (i == 0 || list.get(i) != list.get(i - 1)) {
list.set(j, list.get(i));
j++;
}
}
return j;
}
// 二分求出x对应的离散化的值
public static int find(int x, List<Integer> list) {
int l = 0;
int r = list.size() - 1;
while (l < r) {
int mid = l + r >> 1;
if (list.get(mid) >= x) {
r = mid;
} else {
l = mid + 1;
}
}
return l + 1; //因为要考虑到前缀和
}
//自定义类
class Pairs {
int first;
int second;
public Pairs(int first, int second) {
this.first = first;
this.second = second;
}
例题
-
区间和
假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。
现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。
接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r][l,r] 之间的所有数的和。
输入格式
第一行包含两个整数 n 和 m。
接下来 n 行,每行包含两个整数 x和 c。
再接下来 m 行,每行包含两个整数 l 和 r。
输出格式
共 m 行,每行输出一个询问中所求的区间内数字和。
数据范围
−1000000000≤x≤1000000000
1≤n,m≤105
−1000000000≤l≤r≤1000000000
−10000≤c≤10000输入样例:
3 3 1 2 3 6 7 5 1 3 4 6 7 8
输出样例:
8 0 5
-
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * * * 802. 区间和 * * @author 29670 * */ public class Main{ static class Pairs { int first; int second; public Pairs(int first, int second) { this.first = first; this.second = second; } } static int N = 300010;//因为需要将所有x,l,r存在数组中,这样就是n + 2m <= 300000 public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String[] strings = reader.readLine().split(" "); int n = Integer.parseInt(strings[0]); //n次操作 int m = Integer.parseInt(strings[1]);//m次询问 int[] a = new int[N]; int[] s = new int[N]; List<Integer> alls = new ArrayList<>();//将所有的使用到的数存在alls中,比如x,l,r //但其中会有先后顺序的差别,以及重复,所以需要排序和去重 List<Pairs> add = new ArrayList<>();//用来存n次操作 List<Pairs> query = new ArrayList<>();//用来存m次询问 for (int i = 0; i < n; i++) { String[] strings2 = reader.readLine().split(" "); int x = Integer.parseInt(strings2[0]); int c = Integer.parseInt(strings2[1]); add.add(new Pairs(x, c));//存入alls中,为后续操作做准备 alls.add(x); } for (int i = 0; i < m; i++) { String[] strings2 = reader.readLine().split(" "); int l = Integer.parseInt(strings2[0]); int r = Integer.parseInt(strings2[1]); query.add(new Pairs(l, r)); alls.add(l); alls.add(r); } //到此为止,alls中存好了所有会被用到的数轴上的点,可以进行离散化操作 // 1. 排序 2. 去重 Collections.sort(alls); int unique = unique(alls); //将去重后的List保存下来,或者此处也可以将unique作为最后一个数,用r作为二分 alls = alls.subList(0, unique); for (Pairs item : add) { int index = find(item.first, alls); a[index] += item.second; } //求前缀和 for (int i = 1; i <= alls.size(); i++) s[i] = s[i - 1] + a[i]; for (Pairs item : query) { int l = find(item.first, alls); int r = find(item.second, alls); System.out.println(s[r] - s[l - 1]); } } public static int find(int x, List<Integer> list) { int l = 0; int r = list.size() - 1; while (l < r) { int mid = l + r >> 1; if (list.get(mid) >= x) { r = mid; } else { l = mid + 1; } } return l + 1; //因为要考虑到前缀和 } //去重 public static int unique(List<Integer> list) { int j = 0; for (int i = 0; i < list.size(); i++) { if (i == 0 || list.get(i) != list.get(i - 1)) { list.set(j, list.get(i)); j++; } } return j; } }
区间合并
思路
- 本题思路很简单,采用的思想是贪心,多个区间[l1, r1] [12, r2] … [ln, rn],先对左端点l进行排序,初始化头start = -1e8 end = -1e8 记录开头结尾位置
如果两个区间之间有交集(第一个区间的尾部 >= 第二个区间的头部),则合并,更新尾部end = max(end, 第二个区间和第一个区间哪个尾部值最大)
否则重新标记新的区间头start和尾部end为第二个区间的头和尾
例题
-
区间合并
给定 n 个区间 [li,ri][li,ri],要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
例如:[1,3][1,3] 和 [2,6][2,6] 可以合并为一个区间 [1,6][1,6]。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含两个整数 l 和 r。
输出格式
共一行,包含一个整数,表示合并区间完成后的区间个数。
数据范围
1≤n≤100000
−109≤li≤ri≤109输入样例:
5 1 2 2 4 5 6 7 8 7 9
输出样例:
3
-
题解
-
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Comparator; public class Main { static int N=100010; static int[] a; static ArrayList<int[]> list=new ArrayList<int[]>(); public static void main(String[] args) throws IOException { BufferedReader reader=new BufferedReader(new InputStreamReader(System.in)); int n=Integer.parseInt(reader.readLine()); for (int i = 0; i < n; i++) { a=new int[2]; String[] strings=reader.readLine().split(" "); a[0]=Integer.parseInt(strings[0]); a[1]=Integer.parseInt(strings[1]); list.add(a); } list.sort(new Comparator<int[]>(){ @Override public int compare(int[] o1,int[] o2){ return o1[0] - o2[0]; } }); int k = 0; int r = Integer.MIN_VALUE; for(int a[] : list){ if(a[0] > r){ k++; } r = Math.max(r,a[1]); } System.out.println(k); } }