双指针、位运算、离散化、区间合并的手动模拟
双指针、位运算、离散化、区间合并的手动模拟
双指针算法:
模拟:
import java.util.Scanner;
public class Main{
static int N = 100010;
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] array = new int[N];
int[] s = new int[N];
for(int i = 0;i < n; i++){
array[i] = sc.nextInt();
}
int res = 0;
for(int i = 0,j = 0;i < n;i++){
s[array[i]]++;
while(j<i && s[array[i]]>1){
s[array[j++]]--;
}
res = Math.max(res,i-j+1);
}
System.out.println(res);
}
}
import java.util.Scanner;
public class Main{
static int N = 100010;
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int x = sc.nextInt();
// 定义两个数组
int[] a = new int[N];
int[] b = new int[N];
// 初始化两个数组
for(int i = 0; i < n;i++){
a[i] = sc.nextInt();
}
for(int j = 0; j < m;j++){
b[j] = sc.nextInt();
}
// 法一 j不需要回退
for(int i =0,j=m-1;i < n;i++){
while(j>=0 && a[i]+b[j] >x) j--;
if(j>=0 && a[i]+b[j] ==x) System.out.println(i+" "+j);
}
// 法二 超时的原因是j需要每次循环需要回退为0,这样大大增加计算量
// for(int i =0,j=0;i < n;i++){
// while(j < m && a[i] + b[j] < x) j++;
// if(j < m && a[i]+b[j] == x){
// System.out.println(i+" "+j);
// }
// j=0;
// }
}
}
离散化:
表示整数的离散化,
给定值域为0~10^9 个数为10^5
假定 a[]中的元素 1 3 100 2000 50000
映射到下标为 0 1 2 3 4
的过程为离散化
有两个问题:
- a[]中可能有重复元素---需要去重
- 如何算出x在离散化后a中的值 ---二分
- 离散化中的二分模板最后return r+1,---加映射从1 开始,不加1 映射从0开始
该题的解题思路以及用到的知识点:
确定数轴上的点:
- 保存输入的下标
- 排序
- 去重
查找数轴上的点:
- 手写二分查找所需数的位置
上面是离散化的过程
在数轴区间上+c:
- 前缀和
模拟:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
class Pairs{
int first;
int second;
public Pairs(int first,int second){
this.first = first;
this.second = second;
}
}
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n,m;
// n次操作,m次询问
n = sc.nextInt();
m = sc.nextInt();
int N = 300010; //因为需要将所有x,l,r存在数组中,这样就是n + 2m <= 300000
//离散化的数组
int[] a = new int[N];
// 前缀和数组
int[] s = new int[N];
List<Integer> alls = new ArrayList<>();
List<Pairs> add = new ArrayList<>(); //用来存n次操作
List<Pairs> query = new ArrayList<>(); //用来存储m次询问
// 将x(下标值),c需要加的值,放到add数组中,下标值放到alis中
for(int i = 0;i < n; i++){
int x = sc.nextInt();
int c = sc.nextInt();
add.add(new Pairs(x,c));
alls.add(x);
}
// m次询问
for(int i = 0; i < m; i++){
int l = sc.nextInt();
int r = sc.nextInt();
query.add(new Pairs(l,r));
alls.add(l);
alls.add(r);
}
//将题目中的所需要的数轴上的点(下标值)已经存好,后面进行离散化的操作
//1.排序,2.去重
Collections.sort(alls);
//去重,返回不重复数组的最后一个元素下标
int last_index = unique(alls);
//该subList()方法获取索引 0 到 index(不包括 index)的元素
alls = alls.subList(0,last_index);
//读取add中的下标x与c值,并且将c插入到对应数轴上
for(Pairs item:add){
//很巧妙,找到数轴中x的位置
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 unique(List<Integer> list){
int j = 0;
for(int i = 0; i < list.size(); i++){
//直接在数组上进行修改,将不重复的元素从0-n进行排列;
if(i == 0 || list.get(i) != list.get(i-1)){
list.set(j,list.get(i));
j++;
}
}
return j;
}
// 离散化 采用二分方法
public static int find(int x,List<Integer> alls){
int l = 0,r = alls.size()-1;
while(l < r){
int mid = l+r>>1;
if(alls.get(mid) >= x){
r = mid;
}else{
l = mid+1;
}
}
//因为该题后面需要采用前缀和,前缀和的下标从1开始,为了方便,离散化映射从1开始
return r+1;
}
}
区间合并:
- 按区间左端点排序
- 扫描整个区间,再扫描中把可能有交集的区间合并
- 先维护一个区间,在看I个区间的关系:包含、交 、无交集;
import java.util.*;
import java.io.*;
class pairs{
int l;
int r;
public pairs(int l,int r){
this.l = l;
this. r = r;
}
}
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
pairs[] all = new pairs[n];
for(int i = 0; i < n;i++){
int l = sc.nextInt();
int r = sc.nextInt();
all[i] = new pairs(l,r);
}
Arrays.sort(all, (p, q)->{return p.l - q.l;});
int sum =0;
int max = Integer.MIN_VALUE;
for(int i = 0;i < n; i++){
if(max < all[i].l) sum++;
max = Math.max(max, all[i].r);
}
System.out.println(sum);
}
}
个人感觉比较好理解:
import java.util.*;
public class Main{
private static int N = 100010;
private static int[] a;
private static ArrayList<int[]> list = new ArrayList();
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
for(int i = 0;i < n;i++){
a = new int[2];
a[0] = scanner.nextInt();
a[1] = scanner.nextInt();
list.add(a);
}
//对列表中每个数组0位置元素升序排序
list.sort(new Comparator<int[]>(){
@Override
//排序规则:如果传递的参数是第一个是o1,第二个是o2,比较的时候也是用o1-o2进行比较,那么就是升序;如果比较的时候是用反过来o2-o1进行比较,那么就是降序
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);
}
}
位运算:
N的二进制表示中第k位是几;
- 先把第k位移到最后一位
- 看个位是几
求n的第k位(0开始)数字: n >> k & 1
返回n的最后一位1:lowbit(n) = n & -n
-n为n的反码 取反+1; 与运算 有0得0
例子 x = 1010 --->10
x = 101000 ----> 1000;
作用:求x中1得个数
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n,m;
n = sc.nextInt();
for(int i = 0; i < n; i++){
m = sc.nextInt();
int count = 0;
while(m != 0){
m -= lowbit(m);
count++;
}
System.out.print(count+" ");
}
}
public static int lowbit(int x){
return x & -x;
}
}
结束:
如果有错误欢迎指正,感谢各位看到最后!