双指针-位运算--离散化--区间合并

- 双指针算法

1.核心
优化时间? 两个指针扫描一个序列,时间花费是O(n);
双指针-位运算--离散化--区间合并_第1张图片

for(int i=0,j=0;i<n;i++){
	while(j<(根据题意)&& check(){
		....
	}
}

模板:

for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;

    // 具体问题的逻辑
}
常见问题分类:
    (1) 对于一个序列,用两个指针维护一段区间
    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
  1. 题目一
    双指针-位运算--离散化--区间合并_第2张图片

做法:
第一步:先想一下朴素做法,也就是暴力做法
java

package 大学菜;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/*
*
数据范围
1≤n≤105
输入样例:
5
1 2 2 3 5
3
**/
public class 最长不重复子序列 {
    static int num = 100010;
    static int[] A = new int[num];
    static int[] S = new int[num];
    static int[] G = new int[num];
    static int[] B = {1, 2, 2, 3, 5};
    //hash表的使用,在原来的地方加一
    public static int find(int[] A,int l,int r){
        //
        int res = 0;
        for(int i=0,j=0;i<r;i++){
            S[A[i]] = S[A[i]]+1;
            while (j<i && S[A[i]]>1){
                S[A[j]]--;//这个是一开始的i,所以这个i此时已经不在这个,当前的i=0
                j++;
            }
            res = Math.max(res,i-j+1);
        }
        return res;
    }
    public static void main(String[] args) throws IOException {
       //双指针算法来找
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        //
        //5
        //1 2 2 3 5
        String str1 = reader.readLine();
        int n = Integer.valueOf(str1);
        //接收数组
        String[] str2 = reader.readLine().split(" ");
        for(int i=0;i<n;i++){
            A[i] = Integer.valueOf(str2[i]);
        }
        //查找
        int res = find(A,0,n);
        System.out.println(res);
    }
}

第二步:
看看有没有单调关系,进行缩小时间复杂度,重复元素的判断。:
暴力判断是每次都是j=0;i=0;而更好的做法是j不用重新从零判断,从i当前的位置判断。
check函数是用来判断是否存在重复元素。
就是使用hashmap的方法进行使用在同样的值的地方进行数组上面的++。

- 位运算

二进制的转换:
x 变成二进制
-x 取反
+1
x &(-x+1) = 最后的一位1 的运算
//应用到数据可以检查有多少个1,每次剪掉1;

c++(lowbit运算)使用java进行实现

    public static int lowbit(int x){//与运算
        return (x & -x);
    }

题目:
双指针-位运算--离散化--区间合并_第3张图片

代码:

package 大学菜;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/*
给定一个长度为n的数列,请你求出数列中每个数的二进制表示中1的个数。
输入格式:第一行包含整数 n。第二行包含 n 个整数,表示整个数列。
输出格式:
共一行,包含 n 个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中 1 的个数。
5
1 2 3 4 5

1 1 2 1 2
*/
public class 二进制中的1个数 {
    static int num = 10010;
    public static int lowbit(int x){//与运算
        return (x & -x);
    }
    public static void main(String[] args) throws IOException {
        //int
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String str = reader.readLine();
        int n = Integer.valueOf(str);
        //数组
        String[] str1 = reader.readLine().split(" ");
        for(int i=0;i<n;i++){
            int q = Integer.valueOf(str1[i]);
            int res=0;
            //对q进行使用lowbit
            while (q!=0){
                q = q - lowbit(q);
                res = res+1;
            }
            System.out.print(res+" ");
        }
    }
}

题目:
人类做题:
dfs的过程

- 离散化

值的跨度特别大,导致的空间浪费。

要进行离散化----数据个数的映射-
从稀疏到密集的变化。
1.去重
2.
模板

vector<int> alls; // 存储所有待离散化的值
//java--键值对
class Pairs{
    int l,r;
    public Pairs(int fir,int sec){
        this.l = fir;
        this.r = sec;

    }
}
//c++
sort(alls.begin(), alls.end()); // 将所有值排序
//java
Collections.sort(alls);
//
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; // 映射到1, 2, ...n

作者:yxc

双指针-位运算--离散化--区间合并_第4张图片
题目:
双指针-位运算--离散化--区间合并_第5张图片
去掉重复元素:

题目:双指针-位运算--离散化--区间合并_第6张图片
代码:

package 大学菜;

import java.util.*;

//3 3
//1 2
//3 6
//7 5
//1 3
class Pairs{
    int l,r;
    public Pairs(int fir,int sec){
        this.l = fir;
        this.r = sec;

    }
}

public class 离散化2 {
    static int le = 300010;
    static int[] A = new int[le];//存储原先的数
    static int[] S = new int[le];//存储前缀和
    //unique函数
    public static int unique(List<Integer> list){//此时alls已经是有序的,使用什么方法进行重复
        //双指针
        int j =0;
        for(int i=0;i<list.size();i++){
            //找到一个重复的就之后往后移动,否则前指针等于后指针
            //后指针是i,因为每次都会进行移动,前指针是j,只有不重复时才可以移动
            if(i==0 || list.get(i) != list.get(i-1)){
                 list.set(j,list.get(i));
                 j++;
            }
            //最后的j就是数组最后的右边界
        }
        return j;
    }
    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>>1;
            if(list.get(mid)<=x){
                l = mid;
            }else {
                r = mid-1;
            }
        }
        return l+1;//下标
    }
    //数轴点:第一次的输入的x,第二输入的l,r。
    //存储区间点加操作的的,点和值(x,c)
    //存储区间点求和操作的,左右区间(l,r)
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n= sc.nextInt();
        int m = sc.nextInt();
        //定义结构体
        List<Integer> alls = new ArrayList<>();//放所有的x,l,r
        //定义成对存放的数据
        List<Pairs> xc = new ArrayList<>();
        List<Pairs> lr = new ArrayList<>();
        //n
        for(int i=0;i<n;i++){//n个数据对
            int x = sc.nextInt();
            int c =sc.nextInt();
            Pairs y = new Pairs(x,c);
            xc.add(y);
            alls.add(x);
        }
        //m
        for(int i=0;i<m;i++){
            int l = sc.nextInt();
            int r = sc.nextInt();
            Pairs y = new Pairs(l, r);
            lr.add(y);
            alls.add(l);
            alls.add(r);
        }
        //alls已经放好了,数据对,和查询对也都放好,对alls进行离散化
        //1.排序-集合的排序-list可以使用
        Collections.sort(alls);
        //2.去重
        int end = unique(alls);
        alls.subList(0,end);
        //放置数据
        for(Pairs p:xc){
            int x = p.l;
            int c = p.r;
            int index = find(x,alls);
            A[index] = A[index]+c;
        }
        //求前缀和
        for(int i=1;i<=alls.size();i++){//   因为,all是离散化后的数据
            S[i] = S[i-1]+A[i];
        }
        for(Pairs p:lr){
            int l = find(p.l,alls);
            int r = find(p.r,alls);
            System.out.println(S[r] - S[l-1]);
        }
    }
}

双指针-位运算--离散化--区间合并_第7张图片

- 区间合并:

很快求并集区间 ,快速进行求解区间段个数。
模板:

c++:

// 将所有存在交集的区间合并
void merge(vector<PII> &segs)//存储
{
    vector<PII> res;

    sort(segs.begin(), segs.end());//排序

    int st = -2e9, ed = -2e9;
    for (auto seg : segs)
        if (ed < seg.first)
        {
            if (st != -2e9) res.push_back({st, ed});//看看左端点和有端点的关系。
            st = seg.first, ed = seg.second;
        }
        else ed = max(ed, seg.second);

    if (st != -2e9) res.push_back({st, ed});

    segs = res;
}

作者:yxc

java:

class Pair2s{
    int fir;
    int sec;
    public Pair2s(int fir,int sec){
        this.fir=fir;
        this.sec=sec;
    }
}
Pair2s[] pa= new Pair2s[n];//数组里面放的是pair类型的数组
//对数组进行排序
Arrays.sort(pa,(o1, o2) -> {return o1.fir - o2.fir;});//sheng序排xu
//比较端点
int num = 0;
int max = Integer.MIN_VALUE;
for(int i=0;i<n;i++){
//比较
	if(max<pa[i].fir){//右边没有左边大,说明有一个新的区间
     			 num++; }
     max = Math.max(max,pa[i].sec);//max>=说明有交集或者正好借接住区间,需要	合并,max需要变化到,此时的有端点。
    }
 return num;
}

题目:
双指针-位运算--离散化--区间合并_第8张图片
代码:

package 大学菜;

import java.lang.reflect.Array;
import java.util.*;

/*
5
1 2
2 4
5 6
7 8
7 9*/
class Pair2s{
    int fir;
    int sec;
    public Pair2s(int fir,int sec){
        this.fir=fir;
        this.sec=sec;
    }
}
public class 区间合并 {
    public static int hebing(Pair2s[] pa,int n){
        //对数组进行排序
        Arrays.sort(pa,(o1, o2) -> {return o1.fir - o2.fir;});//生序排序
        //比较端点
        int num = 0;
        int max = Integer.MIN_VALUE;
        for(int i=0;i<n;i++){
            //比较
            if(max<pa[i].fir){//右边没有左边大,说明有一个新的区间
                num++;
            }
            max = Math.max(max,pa[i].sec);//max>=说明有交集或者正好借接住区间,需要合并,max需要变化到,此时的有端点。
        }
        return num;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        Pair2s[] pa= new Pair2s[n];//数组里面放的是pair类型的数组
        for(int i=0;i<n;i++){
            int l = sc.nextInt();
            int r = sc.nextInt();
            pa[i] = new Pair2s(l,r);
        }
        //按左端点排序
        int ans = hebing(pa,n);
        //比较左端点
        System.out.println(ans);

    }


}

你可能感兴趣的:(算法,排序算法,java)