双指针、位运算、离散化、区间合并的手动模拟

双指针、位运算、离散化、区间合并的手动模拟

双指针算法:

题目详情

模拟:

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

的过程为离散化

有两个问题:

  1. a[]中可能有重复元素---需要去重
  2. 如何算出x在离散化后a中的值 ---二分
  3. 离散化中的二分模板最后return r+1,---加映射从1 开始,不加1 映射从0开始

该题的解题思路以及用到的知识点:

确定数轴上的点:

  1. 保存输入的下标
  2. 排序
  3. 去重

查找数轴上的点:

  1. 手写二分查找所需数的位置

上面是离散化的过程

在数轴区间上+c:

  1. 前缀和

题目详情:

模拟:

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;
    }

}

区间合并:

  1. 按区间左端点排序
  2. 扫描整个区间,再扫描中把可能有交集的区间合并
  3. 先维护一个区间,在看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位是几;

  1. 先把第k位移到最后一位
  2. 看个位是几

求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;
    }
    
}

结束:

如果有错误欢迎指正,感谢各位看到最后!

posted @ 2021-02-26 23:04  Xbhog  阅读(76)  评论(0编辑  收藏  举报