JAVA基础算法例题+模板(下)

位运算
  1. 获取数字 n的二进制的第 k位是多少
    步骤
  • 先将二进制数向右移动 k位(此时 k位移动到了第一位), 操作: x >> k
  • 获取最后一位的值, 操作: 移动后的值 & 1
  • 两步和为一步操作: x >> k & 1

获取 n的二进制

for (int i = n; i >= 0; i--) {
    System.out.print(x >> i & 1);
}
  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} 0109

      输入样例:
      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);
    	}
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值