Java算法常用模板(更新ing)

本文概述了几个关键的IT技术概念,包括辗转相除法(gcd),并查集的实现,线段树的数据结构,Dijkstra和Floyd算法,以及大数计算中的扩展欧几里得和分数取模运算,还有KMP算法和字典树(前缀树)在字符串处理中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

gcd

 private static long gcd(long a,long b){
	   return (a==0?b:gcd(b%a,a));
   }

并查集

class UnionFind {
    private int[] parent;
    private int[] size;
    private int count;

    public UnionFind(int n) {
        this.parent = new int[n];
        this.size = new int[n];
        this.count = n;
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }

    public int find(int p) {
        while (p != parent[p]) {
            parent[p] = parent[parent[p]];
            p = parent[p];
        }
        return p;
    }

    public void union(int p, int q) {
        int rootP = find(p);
        int rootQ = find(q);
        if (rootP == rootQ) {
            return;
        }
        if (size[rootP] > size[rootQ]) {
            parent[rootQ] = rootP;
            size[rootP] += size[rootQ];
        } else {
            parent[rootP] = rootQ;
            size[rootQ] += size[rootP];
        }
        count--;
    }

    public int getCount() {
        return count;
    }
}

线段树

import java.util.*;
 
class Node{
    int l,r;               //区间边界
    int sum,lazyTag;       //区间和,lazy标记
    public Node(int l,int r){
        this.l=l;
        this.r=r;
    }
}
 
public class segmentTree{
    static Node[] tree = new Node[20];
    static int[] arr = {1,3,5,7,9};
    public static void main(String[] args) {
        segmentTree.create(0,0,4);
        segmentTree.update(0,1,3,1);
        System.out.println(segmentTree.query(0,0,3));
    }
    public static void create(int node, int l, int r) {
        tree[node] = new Node(l,r);
        if (l == r) {                   //到叶子节点
            tree[node].sum = arr[l];
            return;
        }
        else {
            int mid = (l + r) >> 1;
            int left_node = (node << 1) + 1;
            int right_node = (node << 1) + 2;
            create(left_node, l, mid);
            create(right_node, mid + 1, r);
            tree[node].sum = tree[left_node].sum + tree[right_node].sum;
        }
    }
 
    public static void update(int node,int l, int r,int val) {
        if(tree[node].l > r || tree[node].r < l) {                                                       //不在需要修改的区间里
            return;
        }else if (tree[node].l >= l && tree[node].r <= r) {                //完全包含于要求修改的区间
            tree[node].sum += (tree[node].r-tree[node].l+1)*val;
            tree[node].lazyTag+=val;
            return;
        } else {                              //不完全包含,需要向下查
            if(tree[node].lazyTag!=0)
                pushDowm(node);                      //懒标记下放
            int left_node = (node<<1)+1;
            int right_node = (node<<1)+2;
            update(left_node,l,r,val);
            update(right_node,l,r,val);
            tree[node].sum = tree[left_node].sum + tree[right_node].sum;         //更新完将数据传回给父节点
        }
    }
 
    public static void pushDowm(int node) {
        int left_node = (node<<1)+1;
        int right_node = (node<<1)+2;
        int val =tree[node].lazyTag;
        tree[left_node].sum += (tree[left_node].r - tree[left_node].l + 1) * val;
        tree[right_node].sum += (tree[right_node].r - tree[right_node].l + 1) * val;
        tree[left_node].lazyTag += val;
        tree[right_node].lazyTag += val;
        tree[node].lazyTag = 0;
    }
 
    public static int query(int node,int l,int r) {
        if (tree[node].l > r || tree[node].r < l) {
            return 0;
        }
        else if (tree[node].l >= l && tree[node].r <= r) {
            return tree[node].sum;
        }
        else {
            if(tree[node].lazyTag != 0)
                pushDowm(node);
            int mid = (l + r) / 2;
            int left_node = (node << 1) + 1;
            int right_node = (node << 1) + 2;
            return query(left_node, l, r) + query(right_node, l, r);
        }
    }
}

Dijkstra

package text2;

import java.util.*;
import java.io.*;
public class Dijkstra {
	static int n,m;
	static int g[][] = new int[10005][10005];//存放图
//	static int dis[] = new int[n];//起点到每个点的最短距离
	static int vis[] = new int[10005];//是否被标记过
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scanner = new Scanner(System.in);
		n = scanner.nextInt();//顶点数
		m = scanner.nextInt();//边数
		int s = scanner.nextInt();//图的起点
//		Arrays.fill(dis, 0x3f3f3f3f);
		for (int i = 0; i < n; i++) {
			Arrays.fill(g[i], 0x3f3f3f3f);
			g[i][i]=0;
		}
		Arrays.fill(vis, 0);
		for(int i=0;i<m;i++) {
			int v1=scanner.nextInt();
			int v2=scanner.nextInt();
			int weight=scanner.nextInt();
			g[v1][v2]=weight;
			g[v2][v1]= weight; 
		}
		dijkstra(0);
		System.out.print(g[0][2]);
	}
	//固定模板
	public static void dijkstra(int s) {
		for(int i=0;i<n;i++) {
			int t=-1;
			int min = 0x3f3f3f3f;
			//找距离起点最近的一个点t(而且未被访问过)
			for(int j=0;j<n;j++) {
				if(vis[j]==0&&(t==-1||g[s][j]<min)) {
					min = g[s][j];
					t = j;
				}
			}
			vis[t] = 1;
			//以u为中间点,修正从【起点s】到未访问各点的距离 
			for(int j=0;j<n;j++) {
				if(vis[j]==0) {
					if(g[s][t]+g[t][j]<g[s][j]) {
						g[s][j] = g[s][t]+g[t][j];
						//此处还可以加路径
					}
				}
			}
		}
	}
}


Floyd

import java.util.Scanner;

public class Main{
   
    static int m,n,u,v,w;
    static final int N=150;
    static final int INF=0x3f3f3f3f;
    static long[][] g=new long[N][N];
    static void floyd(){
   
        for(int k=1;k<=n;k++){
   
            for(int i=1;i<=n;i++){
   
                for(int j=1;j<=n;j++){
   
                    if(g[i][j]>g[i][k]+g[k][j]){
   
                        g[i][j]=g[i][k]+g[k][j];
                    }
                }
            }
        }
    }
    public static void main(String[] args) {
   
        Scanner cin=new Scanner(System.in);
        n=cin.nextInt();
        m=cin.nextInt();
        for(int i=1;i<=n;i++){
   
            for(int j=1;j<=n;j++){
   
                g[i][j]=i==j?0:INF;
            }
        }
        while(m-->0){
   
            u=cin.nextInt();
            v=cin.nextInt();
            w=cin.nextInt();
            if(w<g[u][v]){
   
                g[u][v]=g[v][u]=w;
            }
        }
        floyd();
        for(int i=1;i<=n;i++){
   
            for(int j=1;j<=n;j++){
   
                if(j!=1){
   
                    System.out.print(" ");
                }
                System.out.print(g[i][j]);
            }
            System.out.println();
        }
    }
}

计算CnmC^m_nCnm当n和m足够大的时候,无法用long的范围计算,就需要取模运算,由于取模无法进行除法操作,所以可以使用扩展欧几里得计算。

扩展欧几里得

import java.util.HashMap;  
import java.util.Map;  
  
public class LargeCombinationMod {  
    private static final int MOD = 1000000007; // 取模的素数  
  
    // 计算a的阶乘对MOD取模的结果  
    private static long factorialMod(long a) {  
        long result = 1;  
        for (long i = 2; i <= a; i++) {  
            result = (result * i) % MOD;  
        }  
        return result;  
    }  
  
    // 使用扩展欧几里得算法计算逆元  
    private static long modInverse(long a, long m) {  
        long m0 = m, t, q;  
        long x0 = 0, x1 = 1;  
  
        if (m == 1) return 0;  
  
        // 应用扩展欧几里得算法  
        while (a > 1) {  
            q = a / m;  
            t = m;  
  
            m = a % m, a = t;  
            t = x0;  
  
            x0 = x1 - q * x0;  
            x1 = t;  
        }  
  
        // 使结果为正  
        if (x1 < 0) x1 += m0;  
  
        return x1;  
    }  
  
    // 计算组合数C(n, m) mod MOD  
    public static long combinationMod(long n, long m) {  
        // 如果m大于n的一半,利用对称性减少计算量  
        if (m > n - m) {  
            m = n - m;  
        }  
  
        // 计算阶乘的模值  
        long nFactorial = factorialMod(n);  
        long mFactorial = factorialMod(m);  
        long nmFactorial = factorialMod(n - m);  
  
        // 计算逆元  
        long mInv = modInverse(mFactorial, MOD);  
        long nmInv = modInverse(nmFactorial, MOD);  
  
        // 计算组合数模值  
        long result = ((nFactorial * mInv) % MOD * nmInv) % MOD;  
        return result;  
    }  
  
    public static void main(String[] args) {  
        long n = 1000000; // 示例输入,可以根据需要调整  
        long m = 500000; // 示例输入,可以根据需要调整  
        System.out.println(combinationMod(n, m));  
    }  
}

树状数组

class Solution {
    int n = 20001;
    int[] tr = new int[n+1];

    int lowbit(int x){
        return x & -x;
    }

    void add(int x, int c){
        for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
    }

    int query(int x){
        int res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
        return res;
    }

    public List<Integer> countSmaller(int[] nums) {
        LinkedList<Integer> res = new LinkedList<>();
        for(int i = nums.length-1; i >= 0; i--){
            nums[i] += 10001;
            res.addFirst(query(nums[i]-1));
            add(nums[i],1);
        }
        return res;
    }
}

分数取模运算

由费马小定理:
a^(p-1) mod p = 1 mod p
a * a^(p-2) mod p = 1 mod p
a^(p-2) mod p = a^(-1) mod p
(b/a) % p = b * a^(-1) % p = b * a^(p-2) % p

/**
 * 分数求模
 * 费马小定理(前提是模是素数)
 * a^(p-1) mod p = 1 mod p
 * a * a^(p-2) mod p = 1 mod p
 * a^(p-2) mod p = a^(-1) mod p
 * (b/a) % p = b * a^(-1) % p = b * a^(p-2) % p
 *
 * @param a 分母
 * @param b 分子
 * @return (b / a) % mod
 */
public static long fractionMod(long a, long b, long mod) {
	// 这里使用上面的模快速幂的方法
    return mul(b, quickPower(a, mod - 2, mod), mod);
}
/**
 * 模快速幂
 *
 * @param x
 * @param n
 * @return x^n % mod
 */
public static long quickPower(long x, long n, long mod) {
    if (n == 0) return 1;
    if (n == 1) return x % mod;
    long tmp = quickPower(x, n >> 1, mod);
    // mul方法即为上面的模乘
    return (n & 1) == 0 ? mul(tmp, tmp, mod) : mul(x, mul(tmp, tmp, mod), mod);
}

/**
 * 模加
 *
 * @param x
 * @param y
 * @return (x + y) % mod
 */
public static long add(long x, long y, long mod) {
    return ((x % mod) + (y % mod)) % mod;
}
/**
 * 模乘
 *
 * @param x
 * @param y
 * @return x * y % mod
 */
public static long mul(long x, long y, long mod) {
    return ((x % mod) * (y % mod)) % mod;
}

KMP

public class KMP {
    public int indexOf(String source, String pattern) {
        int i = 0, j = 0;
        char[] src = source.toCharArray();
        char[] ptn = pattern.toCharArray();
        int sLen = src.length;
        int pLen = ptn.length;
        int[] next = getNext(ptn);
        while (i < sLen && j < pLen) {
            if (j == -1 || src[i] == ptn[j]) {
                i++;j++;
            } else {
                j = next[j];
            }
        }
        if (j == pLen)
            return i - j;
        return -1;
    }
    public int[] getNext(char[] p) {
        int pLen = p.length;
        int[] next = new int[pLen];
        int k = -1;
        int j = 0;
        next[0] = -1; // next数组中next[0]为-1
        while (j < pLen - 1) {
            if (k == -1 || p[j] == p[k]) {
                k++;j++;
                next[j] = k;
            } else {
                k = next[k];
            }
        }
        return next;
    }
}

字典树(前缀树)

class Trie {
    Trie[] chlidren;
    boolean is_exist;

    public Trie() {
            chlidren = new Trie[26];
            is_exist = false;
    }
    
    public void insert(String word) {
            Trie tree = this;
            for(int i=0;i<word.length();i++){
                int now = word.charAt(i)-'a';
                if(tree.chlidren[now]==null){
                    tree.chlidren[now] = new Trie();
                }
                tree = tree.chlidren[now];
            }
            tree.is_exist = true;
    }
    
    public boolean search(String word) {
        Trie tree = this;
            for(int i=0;i<word.length();i++){
                int now = word.charAt(i)-'a';
                if(tree.chlidren[now]==null)
                return false;
                tree = tree.chlidren[now];
            }
            return tree.is_exist;
    }
    
    public boolean startsWith(String word) {
            Trie tree = this;
            for(int i=0;i<word.length();i++){
                int now = word.charAt(i)-'a';
                if(tree.chlidren[now]==null)
                return false;
                tree = tree.chlidren[now];
            }
            return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值