【算法】算法模板

news2024/12/22 22:38:12

算法模板


文章目录

  • 算法模板
    • 简介
    • 数组
    • 字符串
    • 列表
    • 数学
    • 动态规划


简介

博主在LeetCode网站中学习算法的过程中使用到并总结的算法模板,在算法方面算是刚过初学者阶段,竞赛分数仅2000。

为了节省读者的宝贵时间,部分基础的算法与模板未列出。当然也并非全面。

文章及代码存在不正不明之处还望指正。

数组

  • 生成数组测试数据
  • 区间合并
  • 前缀和 二维前缀和 差分数组
  • 二分查找(各种开闭区间组合)
  • 回溯 子集型(选或不选/选哪个) 组合型 排列
    class ArrTemplates {


        class Generator {
            public void generateArr(int n, int max) {
                Random r = new Random();
                int[] arr = new int[n];
                for (int i = 0; i < n; i++) {
                    arr[i] = r.nextInt(max);
                }
                System.out.println(Arrays.toString(arr));
            }

            public void generateArr(int m, int n, int max) {
                Random r = new Random();
                int[][] arr = new int[m][n];
                for (int i = 0; i < m; i++) {
                    for (int j = 0; j < n; j++) {
                        arr[i][j] = r.nextInt(max);
                    }
                }
                for (int[] ints : arr) {
                    System.out.println(Arrays.toString(ints));
                }
            }
        }

        /**
         * 区间
         */
        class Interval {
            /**
             * 区间合并
             */
            List<int[]> intervalMerge(int[][] ranges) {
                int n = ranges.length;
                Arrays.sort(ranges, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);
                List<int[]> rList = new ArrayList<>(n);
                int[] r1 = ranges[0];
                for (int i = 1; i < n; i++) {
                    int[] r2 = ranges[i];
                    if (r2[0] <= r1[1]) {
                        r1[1] = Math.max(r1[1], r2[1]);
                    } else {
                        rList.add(r1);
                        r1 = r2;
                    }
                }
                rList.add(r1);
                return rList;
            }

        }


        class PrefixSum {
            /**
             * 前缀和
             */
            public int[] getPreFix(int[] arr) {
                int n = arr.length;
                int[] pre = new int[n + 1];
                for (int i = 0; i < n; i++) {
                    pre[i + 1] = pre[i] + arr[i];
                }
                return pre;
            }

            /**
             * 二维前缀和
             */
            public int[][] getPrefix(int[][] matrix) {
                int m = matrix.length, n = matrix[0].length;
                int[][] pre = new int[m + 1][n + 1];
                for (int i = 0; i < m; i++) {
                    for (int j = 0; j < n; j++) {
                        pre[i + 1][j + 1] = pre[i + 1][j] + pre[i][j + 1] - pre[i][j] + matrix[i][j];
                    }
                }
                return pre;
            }

            /**
             * 返回左上角在 (r1,c1) 右下角在 (r2,c2) 的子矩阵元素和
             */
            public int query(int[][] pre, int r1, int c1, int r2, int c2) {
                return pre[r2 + 1][c2 + 1] - pre[r2 + 1][c1] - pre[r1][c2 + 1] + pre[r1][c1];
            }

            //差分数组
            public int[] getDiff(int n, int[] arr) {
                int[] diff = new int[n];
                diff[0] = arr[0];
                for (int i = 1; i < n; i++) {
                    diff[i] = arr[i] - arr[i - 1];
                }
//            for (int i = 1; i < n; i++) {
//                diff[i] += diff[i - 1]; // 直接在差分数组上复原数组 a
//            }
                return diff;
            }

            int[] getDiff(int n, int[][] arr) {
                int[] diff = new int[n]; // 差分数组
                for (int[] q : arr) {
                    int l = q[0], r = q[1], x = q[2];
                    diff[l] += x;
                    if (r + 1 < n) {
                        diff[r + 1] -= x;
                    }
                }
                for (int i = 1; i < n; i++) {
                    diff[i] += diff[i - 1]; // 直接在差分数组上复原数组 a
                }
                return diff;
            }


        }

        class BinarySearch {
            /**
             * 二分查找
             * 闭区间
             * 循环不变量:左侧小于等于目标值,右侧大于目标值
             * r-1<=target,l+1>target
             */
            int binarySearch(int[] nums, int target) {
                int l = 0, r = nums.length - 1;
                while (l <= r) {
                    int m = l + (r - l) / 2;
                    if (nums[m] < target) {
                        l = m + 1;
                    } else {
                        r = m - 1;
                    }
                }
                return l;
            }

            /**
             * 左闭右开区间[l,r)
             * [l,mid) [mid+1,r)
             */
            int binarySearch2(int[] nums, int target) {
                int l = 0, r = nums.length;
                while (l < r) {
                    int m = l + (r - l) / 2;
                    if (nums[m] < target) {
                        l = m + 1;
                    } else {
                        r = m;
                    }
                }
                return l;
            }

            /**
             * 左开右闭区间(l,r]
             * (l,mid-1] (mid,r]
             */
            int binarySearch3(int[] nums, int target) {
                int l = -1, r = nums.length - 1;
                while (l < r) {
                    int m = l + (r - l) / 2;
                    if (nums[m] < target) {
                        l = m;
                    } else {
                        r = m - 1;
                    }
                }
                return l;
            }

            /**
             * 开区间(l,r)
             * (l,mid)(mid,r)
             */
            int binarySearch4(int[] nums, int target) {
                int l = -1, r = nums.length;
                while (l + 1 < r) {
                    int m = l + (r - l) / 2;
                    if (nums[m] < target) {
                        l = m;
                    } else {
                        r = m;
                    }
                }
                return r;
            }

        }

        class Backtrack {

            List<List<Integer>> res = new ArrayList<>();
            List<Integer> path = new ArrayList<>();
            int n;
            boolean[] visited = new boolean[n];

            /**
             * 子集型
             * 选或不选 n*2^n
             */
            void backtrack(int[] nums, int idx) {
                //解
                if (idx == n) {
                    res.add(new ArrayList<>(path));
                }
                //尝试 选或不选
                //不选
                backtrack(nums, idx + 1);
                //选
                path.add(nums[idx]);
                backtrack(nums, idx + 1);
                //回溯
                path.remove(path.size() - 1);
            }

            /**
             * 子集型
             * 每次选一个
             */
            void backtrack2(int[] nums, int idx) {
                //解
                res.add(new ArrayList<>(path));
                if (idx == n) {
                    return;
                }
                for (int i = idx; i < n; i++) {
                    path.add(nums[i]);
                    backtrack2(nums, i + 1);
                    //回溯
                    path.remove(path.size() - 1);
                }
            }

            /**
             * 组合型
             * 逆序排列
             */
            void backtrack(int[] nums, int idx, int k) {
                //剩余不足达到k个
                if (idx < k - path.size()) {
                    return;
                }
                if (path.size() == k) {
                    res.add(new ArrayList<>(path));
                    return;
                }
                for (int i = idx; i >= 0; i--) {
                    path.add(nums[i]);
                    backtrack(nums, i - 1, k);
                    //回溯
                    path.remove(path.size() - 1);
                }
            }

            /**
             * 组合型
             * 正序排列
             */
            void backtrack2(int[] nums, int idx, int k) {
                if (n - idx < k - path.size()) {
                    return;
                }
                if (path.size() == k) {
                    res.add(new ArrayList<>(path));
                    return;
                }
                for (int i = idx; i < n; i++) {
                    path.add(nums[i]);
                    backtrack2(nums, i + 1, k);
                    //回溯
                    path.remove(path.size() - 1);
                }
            }

            /**
             * 排列
             * Tn*n!
             */
            void backtrack3(int[] nums, int idx) {
                if (idx == n) {
                    res.add(new ArrayList<>(path));
                    return;
                }
                for (int i = 0; i < n; i++) {
                    if (!visited[i]) {
                        visited[i] = true;
                        path.add(nums[i]);
                        backtrack3(nums, idx + 1);
                        visited[i] = false;
                        path.remove(path.size() - 1);
                    }
                }
            }


        }
    }

字符串

  • kmp字符串匹配
  • 子串
  • 回文串
class StringTemplates {
        /**
         * kmp
         */
        public int kmpSearchIndex(String source, String target) {
            int n = source.length(), m = target.length();
            if (m == 0) {
                return 0;
            }
            // 创建部分匹配表
            int[] kmp = new int[m];
            for (int i = 1, j = 0; i < m; i++) {
                // 字符不匹配时,j回溯到上一个匹配的字符,继续匹配
                while (j > 0 && target.charAt(i) != target.charAt(j)) {
                    j = kmp[j - 1];
                }
                // 当haystack和needle的字符匹配时,将j的值加一
                if (target.charAt(i) == target.charAt(j)) {
                    j++;
                }
                // 更新部分匹配表
                kmp[i] = j;
            }

            // 在haystack中查找needle
            for (int i = 0, j = 0; i < n; i++) {
                // 字符不匹 j回溯到上一个匹配的字符
                while (j > 0 && source.charAt(i) != target.charAt(j)) {
                    j = kmp[j - 1];
                }
                // 字符匹配时,已匹配字符数+1
                if (source.charAt(i) == target.charAt(j)) {
                    j++;
                }
                //找到needle
                if (j == m) {
                    return i - m + 1;
                }
            }

            // 没有找到needle,则返回-1
            return -1;
        }

        /**
         * 获取长度为len的所有子串
         */
        private String[] getSubstrings(String s, int len) {
            int n = s.length();
            String[] substrings = new String[n - len + 1];
            for (int j = 0; j <= n - len; j++) {
                substrings[j] = s.substring(j, j + len);
            }
            return substrings;
        }

        /**
         * 是否回文串
         */
        boolean isPalindrome(String str) {
            int left = 0, right = str.length() - 1;
            while (left < right) {
                if (str.charAt(left) != str.charAt(right)) {
                    return false;
                }
                left++;
                right--;
            }
            return true;
        }

        /**
         * 是否回文串
         */
        private boolean isPalindrome(char[] ss, int l, int r) {
            if (l == r) {
                return true;
            }
            while (l < r) {
                if (ss[l++] != ss[r--]) {
                    return false;
                }
            }
            return true;
        }


        /**
         * 字符串任意子串是否回文串
         */
        boolean[][] palindrome(char[] cs) {
            int n = cs.length;

            boolean[][] p = new boolean[n][n];
            //1
//            for (int i = 0; i < n; i++) {
//                Arrays.fill(p[i], true);
//            }
//            for (int i = n - 1; i >= 0; i--) {
//                for (int j = i + 1; j < n; ++j) {
//                    p[i][j] = cs[i] == cs[j] && p[i + 1][j - 1];
//                }
//            }
            //2
            for (int len = 1; len <= n; len++) {
                //从i开始,统计长度为len的子串是否为回文串
                for (int i = 0; i <= n - len; i++) {
                    int j = i + len - 1;
                    if (len == 1) {
                        p[i][j] = true;
                    } else if (len == 2) {
                        p[i][j] = cs[i] == cs[j];
                    } else {
                        // 大于两个字符时,判断首尾字符是否相等,并且去除首尾字符后的子串是否是回文串
                        p[i][j] = cs[i] == cs[j] && p[i + 1][j - 1];
                    }
                }
            }
            return p;
        }

    }

列表

  • 翻转
  • 快慢指针
    class ListTemplates {

        /**
         * 翻转链表
         */
        public void reverse(ListNode head, ListNode tail) {
            ListNode last = null;
            ListNode curr = head;
            ListNode end = tail.next;
            while (curr != end) {
                //下一个节点
                ListNode next = curr.next;
                //将当前节点的指针指向前一个节点
                curr.next = last;
                //将当前节点置位下一个节点的前置
                last = curr;
                //循环控制
                curr = next;
            }
        }

        /**
         * 快慢指针
         */
        boolean fast_slowPoints(ListNode head) {
            ListNode slow = head;
            ListNode fast = head;
            while (slow != null && fast != null && fast.next != null) {
                slow = slow.next;
                fast = fast.next.next;
                if (slow == fast) {
                    return true;
                }
            }
            return false;
        }
    }

数学

  • 最大/小值
  • lcm
  • gcd
  • 快速幂
  • lowbit
  • 质数/素数(埃氏筛)
  • 回文数
  • 组合数
    class MathTemplates {

        /**
         * min
         */
        public int min(int a, int... b) {
            for (int i : b) {
                a = Math.min(a, i);
            }
            return a;
        }

        /**
         * max
         */
        public int max(int a, int... b) {
            for (int i : b) {
                a = Math.max(a, i);
            }
            return a;
        }

        /**
         * 最小公倍数 Lowest Common Multiple
         */
        private long lcm(long a, long b) {
            return a * b / gcd(a, b);
        }

        /**
         * 最大公约数 Greatest common divisor
         */
        public long gcd(long x, long y) {
            return y == 0 ? x : gcd(y, x % y);
        }

        /**
         * 快速幂
         */
        public double fastPow(double x, long n) {
            double res = 1.0;
            while (n > 0) {
                if (n % 2 == 1) {
                    res *= x;
                }
                x *= x;
                n /= 2;
            }
            return res;
        }

        /**
         * 快速幂
         */
        public long fastPow(long x, long n) {
            long res = 1;
            while (n > 0) {
                if (n % 2 == 1) {
                    res *= x;
                }
                x *= x;
                n /= 2;
            }
            return res;
        }

        public int lowbit(int i) {
            //x & (~x + 1);
            return i & -i;
        }

        /**
         * 二进制1个数
         * Integer.bitCount(i)
         */
        public int bitCount(int i) {
            i = i - ((i >>> 1) & 0x55555555);
            i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
            i = (i + (i >>> 4)) & 0x0f0f0f0f;
            i = i + (i >>> 8);
            i = i + (i >>> 16);
            return i & 0x3f;
        }

        /**
         * 是否质数
         */
        private boolean isPrime(int n) {
            for (int i = 2; i * i <= n; i++) {
                if (n % i == 0) {
                    return false;
                }
            }
            return true;
        }

        private boolean isPrime2(int n) {
            // 小于等于1的整数不是素数
            if (n <= 1) {
                return false;
            }
            // 2和3是素数
            if (n <= 3) {
                return true;
            }
            // 如果整数能被2或3整除,不是素数
            if (n % 2 == 0 || n % 3 == 0) {
                return false;
            }
            // 除了2和3,素数都可以表示成6的倍数加1或减1的形式(在6的倍数两侧的数不是素数)
            for (int i = 5; i * i <= n; i += 6) {
                if (n % i == 0 || n % (i + 2) == 0) {
                    return false;
                }
            }

            return true;
        }

        boolean[] isPrime;

        /**
         * 质数表-埃氏筛
         * Tnlog(logn) Sn
         */
        private void getPrimes(int n) {
            isPrime = new boolean[n];
            Arrays.fill(isPrime, true);
            isPrime[1] = false;
            for (int i = 2; i <= Math.sqrt(n); i++) {
                if (isPrime[i]) {
                    // 将 i 的所有倍数标记为非质数(合数)
                    for (int j = i * i; j < n; j += i) {
                        isPrime[j] = false;
                    }
                }
            }
        }

        /**
         * 回文数
         */
        public boolean isPalindrome(int x) {
            if (x < 0 || x % 10 == 0 && x != 0) {
                return false;
            }
            int reverse = 0;
            while (x > reverse) {
                reverse = reverse * 10 + x % 10;
                x /= 10;
            }
            return x == reverse || x == reverse / 10;
        }


        private final int N = 31;
        private final int[][] c = new int[N][N];

        {
            for (int i = 0; i < N; i++) {
                c[i][0] = c[i][i] = 1;
                for (int j = 1; j < i; j++) {
                    //Cn,k = Cn-1,k-1 + Cn-1,k
                    c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
                }
            }
        }


        /**
         * 计算n个数里拿k个数的组合数
         */
        public long comb(int n, int k) {
            long ans = 1;
            for (int i = 1; i <= k; i++) {
                ans *= (n - k + i);
                ans /= i;
            }
            return ans;
        }


    }

  • 树状数组
    class TreeTemplates {


        /**
         * 树状数组
         */
        class BinaryIndexedTrees {
            private int[] tree;
            private int[] nums;

            /**
             * TOnlogn
             */
            public BinaryIndexedTrees(int[] nums) {
                //lowbit(0)计算为0;舍弃0下标;
                this.tree = new int[nums.length + 1];
                this.nums = nums;
                for (int i = 0; i < nums.length; i++) {
                    add(i + 1, nums[i]);
                }
            }

            /**
             * TOlogn
             */
            public void update(int index, int val) {
                add(index + 1, val - nums[index]);
                nums[index] = val;
            }

            /**
             * TOlogn
             */
            public int sumRange(int left, int right) {
                return prefixSum(right + 1) - prefixSum(left);
            }

            /**
             * 用于计算树状数组的 x节点的父节点
             * x的父节点索引= x+=lowbit(x)
             * lowbit(x) 未 非负整数x在二进制下的最低为1及其后面的0构成的数(x的除最后一位1外,其他位置0)
             */
            private int lowBit(int x) {
                //return x & (~x + 1);
                return x & -x;
            }

            private void add(int index, int val) {
                while (index < tree.length) {
                    tree[index] += val;
                    index += lowBit(index);
                }
            }

            private int prefixSum(int index) {
                int sum = 0;
                while (index > 0) {
                    sum += tree[index];
                    index -= lowBit(index);
                }
                return sum;
            }
        }
    }

  • 无向图/带权图 领接矩阵/表
  • 并查集
  • Dijkstra
  • Floyd
    class GraphTemplates {


        /**
         * 无向图
         */
        public List<Integer>[] edgesToGraph(int n, int[][] edges) {
            List<Integer>[] graph = new List[n];
            Arrays.setAll(graph, i -> new ArrayList<>());
            for (int[] edge : edges) {
                int x = edge[0];
                int y = edge[1];
                graph[x].add(y);
                graph[y].add(x);
            }
            return graph;
        }

        /**
         * 带权图
         */
        public List<int[]>[] edgesToWGraph(int n, int[][] edges) {
            List<int[]>[] wgraph = new List[n];
            Arrays.setAll(wgraph, i -> new ArrayList<>());
            for (int[] edge : edges) {
                int x = edge[0];
                int y = edge[1];
                int w = edge[2];
                wgraph[x].add(new int[]{y, w});
                wgraph[y].add(new int[]{x, w});
            }
            return wgraph;
        }

        /**
         * 带权领接矩阵
         */
        public int[][] edgesToWGraph2(int n, int[][] edges) {
            int INF = Integer.MAX_VALUE / 2;
            int[][] graph = new int[n][n];
            for (int i = 0; i < n; i++) {
                Arrays.fill(graph[i], INF);
            }
            for (int[] edge : edges) {
                graph[edge[0]][edge[1]] = edge[2];
            }
            return graph;
        }


        /**
         * 并查集
         */
        class UnionFind {
            int[] fa;

            public UnionFind(int n) {
                fa = new int[n];
                for (int i = 0; i < n; i++) {
                    fa[i] = i;
                }
            }

            public int find(int x) {
                if (fa[x] == x) {
                    return x;
                }
                return fa[x] = find(fa[x]);
            }


            boolean union(int x, int y) {
                int fx = find(x), fy = find(y);
                if (fx == fy) {
                    return false;
                }
                if (fx > fy) {
                    fa[fy] = fx;
                } else {
                    fa[fx] = fy;
                }
                return true;
            }

        }

        /**
         * 长度统计 联通分量统计 高度压缩 并查集
         */
        class UnionFind2 {
            //父节点
            int[] fa;
            //高度
            int[] rk;
            //子集长度
            int[] sz;
            //连通分量数
            int count;

            public UnionFind2(int n) {
                fa = new int[n];
                rk = new int[n];
                sz = new int[n];
                for (int i = 0; i < n; i++) {
                    fa[i] = i;
                    sz[i] = 1;
                }
                count = n;
            }

            public int find(int x) {
                if (fa[x] == x) {
                    return x;
                }
                return fa[x] = find(fa[x]);
            }


            boolean union(int x, int y) {
                int fx = find(x), fy = find(y);
                if (fx == fy) {
                    return false;
                }
                //如果 x的高度大于 y,则令 y的上级为 x
                if (rk[fx] > rk[fy]) {
                    fa[fy] = fx;
                    sz[fx] += sz[fy];
                } else {
                    //如果 x的高度和 y的高度相同,则令 y的高度加1
                    if (rk[fx] == rk[fy]) {
                        rk[fy]++;
                    }
                    fa[fx] = fy;
                    sz[fy] += sz[fx];
                }
                count--;
                return true;
            }

            public boolean isSame(int x, int y) {
                return find(x) == find(y);
            }

            public int size(int x) {
                return sz[find(x)];
            }

            public int count() {
                return count;
            }
        }

        class Dijkstra {
            /**
             * dijkstra 统计单源最短路径长度
             */
            public long dijkstraCalculateLenOfShortestPaths(int n, int[][] edges, int start, int end) {
                List<int[]>[] wgraph = edgesToWGraph(n, edges);
                //最短路径长度,最短路径次数
                long[] dist = new long[n];
                Arrays.fill(dist, Long.MAX_VALUE);
                dist[start] = 0;

                PriorityQueue<long[]> pq = new PriorityQueue<>((a, b) -> Long.compare(a[1], b[1]));
                //节点,权重
                pq.offer(new long[]{start, 0});

                while (!pq.isEmpty()) {
                    long[] node = pq.poll();
                    int u = (int) node[0];
                    //s到达u的最短路径权重
                    long suw = node[1];
                    //此路径不是到达u的最短路径
                    if (suw > dist[u]) {
                        continue;
                    }
                    for (int[] edge : wgraph[u]) {
                        int v = edge[0], uvw = edge[1];
                        //s到达v的最短路径权重
                        long svw = dist[v];
                        if (suw + uvw < svw) {
                            dist[v] = suw + uvw;
                            pq.offer(new long[]{v, dist[v]});
                        }
                    }
                }
                return dist[end];
            }


            /**
             * dijkstra 统计单源最短路径数量
             */
            public int dijkstraCalculateCount0fShortestPaths(int n, int[][] edges, int start, int end) {
                List<int[]>[] wgraph = edgesToWGraph(n, edges);
                //最短路径长度,最短路径次数
                long[] dist = new long[n];
                Arrays.fill(dist, Long.MAX_VALUE);
                dist[start] = 0;

                int[] counts = new int[n];
                counts[start] = 1;

                PriorityQueue<long[]> pq = new PriorityQueue<>((a, b) -> Long.compare(a[1], b[1]));
                //节点,权重
                pq.offer(new long[]{start, 0});

                while (!pq.isEmpty()) {
                    long[] node = pq.poll();
                    int u = (int) node[0];
                    //s到达u的最短路径权重
                    long suw = node[1];
                    //此路径不是到达u的最短路径
                    if (suw > dist[u]) {
                        continue;
                    }
                    for (int[] edge : wgraph[u]) {
                        int v = edge[0], uvw = edge[1];
                        //s到达v的最短路径权重
                        long svw = dist[v];
                        //s到达u的最短路径数量
                        int suc = counts[u];
                        if (suw + uvw < svw) {
                            dist[v] = suw + uvw;
                            counts[v] = suc;
                            pq.offer(new long[]{v, dist[v]});
                        } else if (suw + uvw == svw) {
                            counts[v] = (counts[v] + suc) % 1000000007;
                        }
                    }
                }

                return counts[end];
            }

            /**
             * dijkstra 单元 统计最短路径长度与数量
             */
            public long[][] dijkstraCalculateLenAndCount0fShortestPaths(int n, int[][] edges) {
                List<int[]>[] wgraph = edgesToWGraph(n, edges);
                //最短路径长度,最短路径次数
                long[][] dist = new long[n][2];
                Arrays.setAll(dist, i -> new long[]{Long.MAX_VALUE, 0});
                dist[0] = new long[]{0, 1};

                PriorityQueue<long[]> pq = new PriorityQueue<>((a, b) -> Long.compare(a[1], b[1]));
                //节点,权重
                pq.offer(new long[]{0, 0});

                while (!pq.isEmpty()) {
                    long[] node = pq.poll();
                    int u = (int) node[0];
                    //s到达u的最短路径权重
                    long suw = node[1];
                    //此路径不是到达u的最短路径
                    if (suw > dist[u][0]) {
                        continue;
                    }
                    for (int[] edge : wgraph[u]) {
                        int v = edge[0], uvw = edge[1];
                        //s到达v的最短路径权重
                        long svw = dist[v][0];
                        //s到达u的最短路径数量
                        long suc = dist[u][1];
                        if (suw + uvw < svw) {
                            dist[v][0] = suw + uvw;
                            dist[v][1] = suc;
                            pq.offer(new long[]{v, dist[v][0]});
                        } else if (suw + uvw == svw) {
                            dist[v][1] = (dist[v][1] + suc) % 1000000007;
                        }
                    }
                }

                return dist;
            }

            /**
             * 稠密图 邻接矩阵
             */
            public int[] dijkstra(int n, int[][] edges, int start) {
                int INF = Integer.MAX_VALUE / 2;
                int[][] graph = new int[n][n];
                int[] dist = new int[n];
                for (int i = 0; i < n; i++) {
                    Arrays.fill(graph[i], INF);
                    dist[i] = INF;
                }
                for (int[] edge : edges) {
                    graph[edge[0]][edge[1]] = edge[2];
                }

                boolean[] vis = new boolean[n];
                dist[start] = 0;

                for (int i = 0; i < n; i++) {
                    // 找到当前距离最小的未访问节点
                    int x = -1;
                    for (int y = 0; y < n; ++y) {
                        if (!vis[y] && (x == -1 || dist[y] < dist[x])) {
                            x = y;
                        }
                    }
                    // 访问标记
                    vis[x] = true;
                    for (int y = 0; y < n; ++y) {
                        // 更新最短路长度
                        dist[y] = Math.min(dist[y], dist[x] + graph[x][y]);
                    }
                }

                return dist;
            }

        }

        //多源最短路径
        class Floyd {
            public int[][] floyd(int n, int[][] edges) {
                int INF = Integer.MAX_VALUE;
                int[][] dist = new int[n][n];
                for (int i = 0; i < n; ++i) {
                    Arrays.fill(dist[i], INF);
                    dist[i][i] = 0;
                }
                for (int[] e : edges) {
                    dist[e[0]][e[1]] = e[2];
                }
                for (int k = 0; k < n; k++) {
                    for (int i = 0; i < n; i++) {
                        for (int j = 0; j < n; j++) {
                            if (dist[i][k] == INF || dist[k][j] == INF) {
                                continue;
                            }
                            dist[i][j] = Math.max(dist[i][j], dist[i][k] + dist[k][j]);
                        }
                    }
                }
                return dist;
            }

        }


        class Prim {
            public void primMST(int n, int[][] edges) {

            }
        }
    }

动态规划

  • 爬楼梯
  • 打家劫舍
  • 子数组
  • 子序列
  • 背白 01背包 完全背包
  • 划分
  • 区间
    class DpTemplates {

        /**
         * 入门
         * 爬楼梯 打家劫舍
         */
        class Base {

            /**
             * 爬楼梯-每次相同方式二选一
             */
            public int climbingStairs(int n) {
                int[] dp = new int[n + 1];
                dp[0] = 1;
                dp[1] = 1;
                for (int i = 2; i <= n; i++) {
                    dp[i] = dp[i - 1] + dp[i - 2];
                }
                return dp[n];

            /*int p1 = 0, p2 = 1;
            for (int i = 1; i <= n; i++) {
                p2 += p1;
                p1 = p2 - p1;
            }
            return p2;*/
            }

            /**
             * 花费代价爬楼梯 每次相同方式二选一,并选择较少消费
             */
            public int climbingStairsByMinCost(int[] cost) {
                int len = cost.length;
                for (int i = 2; i < len; i++) {
                    cost[i] = Math.min(cost[i - 1], cost[i - 2]) + cost[i];
                }
                return Math.min(cost[len - 1], cost[len - 2]);
            }


            /**
             * 打家劫舍 根据要求选或不选
             */
            public int rob(int[] nums) {
                int[] dp = new int[2];
                for (int num : nums) {
                    //选 上一个只能是不选
                    int doit = dp[1] + num;
                    //不选 上一个选或不选
                    int not = Math.max(dp[0], dp[1]);
                    dp[0] = doit;
                    dp[1] = not;
                }
                return Math.max(dp[0], dp[1]);
            }

        }

        /**
         * 子数组dp
         */
        class Subarray {

            /**
             * 子数组最大值
             */
            public int maxSubArrayDp(int[] nums) {
                int[] dp = new int[nums.length];
                dp[0] = nums[0];
                int res = nums[0];
                for (int i = 1; i < nums.length; i++) {
                    dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
                    res = Math.max(res, dp[i]);
                }
                return res;
            }

            /**
             * 子数组最大值
             */
            public int maxSubArray(int[] nums) {
                int max = nums[0];
                int pre = max;
                for (int i = 1; i < nums.length; i++) {
                    pre = Math.max(pre + nums[i], nums[i]);
                    max = Math.max(max, pre);
                }
                return max;
            }


            /**
             * 单调递增/减 最长子数组
             */
            public int longestMonotonicSubarray(int[] nums) {
                int res = 1;
                int i = 0, n = nums.length;
                while (i < n - 1) {
                    //相等直接跳过
                    if (nums[i + 1] == nums[i]) {
                        i++;
                        continue;
                    }
                    // 记录开始位置
                    int start = i;
                    //定下基调:递增/递减
                    boolean inc = nums[i + 1] > nums[i];
                    // i 和 i+1 已经满足要求,从 i+2 开始判断
                    i += 2;
                    while (i < n && nums[i] != nums[i - 1] && (nums[i] > nums[i - 1]) == inc) {
                        i++;
                    }
                    // 从 start 到 i-1 是满足题目要求的(并且无法再延长的)子数组
                    res = Math.max(res, i - start);
                    i--;
                }
                return res;
            }
        }

        /**
         * 子序列dp
         */
        class Subsequence {

            /**
             * 最长公共子序列LCS
             */
            public int longestCommonSubsequence(int[] arr1, int[] arr2) {
                int m = arr1.length, n = arr2.length;
                int[][] dp = new int[m + 1][n + 1];

                for (int i = 0; i < m; i++) {
                    for (int j = 0; j < n; j++) {
                        dp[i + 1][j + 1] = arr1[i] == arr2[j] ? dp[i][j] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
                    }
                }

                return dp[m][n];
            }

            /**
             * 子序列数量
             */
            public int diffSubsequenceCount(int[] arr1, int[] arr2) {
                int MOD = 1_000_000_007;
                int m = arr1.length;
                int n = arr2.length;
                int[][] dp = new int[m + 1][n + 1];
                //init
                for (int i = 0; i <= m; i++) {
                    dp[i][0] = 1;
                }
                for (int i = 1; i <= m; i++) {
                    for (int j = 1; j <= i && j <= n; j++) {
                        dp[i][j] = dp[i - 1][j];
                        if (arr1[i - 1] == arr2[j - 1]) {
                            dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % MOD;
                        }
                    }
                }
                return dp[m][n];
            }

            /**
             * 最长递增子序列 LIS
             */
            public int longestIncreasingSubsequence(int[] nums) {
                int n = nums.length;
                int[] dp = new int[n];
                int res = 0;
                for (int i = 0; i < n; i++) {
                    for (int j = 0; j < i; j++) {
                        if (nums[j] < nums[i]) {
                            dp[i] = Math.max(dp[i], dp[j] + 1);
                        }
                    }
                    res = Math.max(res, dp[i]);
                }
                return res + 1;
            }


        }


        /**
         * 背包dp
         */
        class Knapsack {

            /**
             * 01背包
             */
            public int zeroOneKnapsack(int[] ws, int[] vs, int c) {
                int n = ws.length;
                int[][] dp = new int[n + 1][c + 1];
                dp[0][0] = 1;
                // 动态规划求解
                for (int i = 1; i <= n; i++) {
                    for (int j = 1; j <= c; j++) {
                        if (ws[i - 1] > j) {
                            dp[i][j] = dp[i - 1][j];
                        } else {
                            dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - ws[i - 1]] + vs[i - 1]);
                        }
                    }
                }

                return dp[n][c];
            }

            /**
             * 完全背包
             */
            public int completeKnapsack(int[] ws, int[] vs, int c) {
                int n = ws.length;
                int[][] dp = new int[n + 1][c + 1];
                dp[0][0] = 1;
                // 动态规划求解
                for (int i = 1; i <= n; i++) {
                    for (int j = 1; j <= c; j++) {
                        if (ws[i - 1] > j) {
                            // 物品 i 不被选入背包
                            dp[i][j] = dp[i - 1][j];
                        } else {
                            // 物品 i 被选入背包
                            // 可以重复选取,所以是 dp[i][j - weights[i - 1]] + values[i - 1]
                            dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - ws[i - 1]] + vs[i - 1]);
                        }
                    }
                }
                return dp[n][c];
            }
        }


        /**
         * 划分dp
         */
        class Partition {

            /**
             * 能否划分
             * f[i] 表示 a[:i]能否划分;
             * 枚举最右侧划分左端l,判断a[l,i]是否符合条件
             * f[i] = min/max(f[i],f[l-1]+1)
             */
            boolean canPartition(int[] nums) {
                int n = nums.length;
                boolean[] dp = new boolean[n + 1];
                dp[0] = true;
                for (int i = 1; i <= n; i++) {
                    for (int l = 0; l <= i; l++) {
                        if (dp[l] && check(nums, l, i)) {//num[l:i] 是否符合条件
                            dp[i] = true;
                            break;
                        }
                    }
                }
                return dp[n];
            }


            /**
             * 划分个数  f[i] 表示a[:i]在约束下 能划分的最大/小个数
             * 枚举右侧划分左端l,判断a[l,i]是否符合条件
             * f[i] = min/max(f[i],f[l-1]+1)
             */
            int partitionNum(int[] nums) {
                int n = nums.length;
                int[] dp = new int[n + 1];
                for (int i = 0; i < n; i++) {
                    dp[i + 1] = dp[i] + 1;
                    for (int l = 0; l <= i; l++) {
                        //符合划分条件
                        if (check(nums, l, i)) {
                            //min / max
                            dp[i + 1] = Math.min(dp[i + 1], dp[l] + 1);
                        }
                    }
                }
                return dp[n];
            }

            /**
             * 约束划分数量  划分为k个,计算最优解;
             * f[i][j]: 将a[:i]划分为j个部分的最优解;
             * 枚举右侧左端l,从f[l][j-1]转移到f[i][j],考虑最最优解的影响
             */
            public int partitionCnt(int[] nums, int k) {
                int n = nums.length;
                int[] pre = new int[n + 1];
                for (int i = 0; i < n; i++) {
                    pre[i + 1] = pre[i] + nums[i];
                }

                int[][] dp = new int[n + 1][k + 1];
                for (int i = 0; i <= n; i++) {
                    Arrays.fill(dp[i], Integer.MAX_VALUE);
                }

                dp[0][0] = 0;
                for (int i = 1; i <= n; i++) {
                    for (int j = 1; j <= Math.min(i, k); j++) {
                        for (int l = j - 1; l < i; l++) {
                            //最小化 划分子数组的和
                            dp[i][j] = Math.min(dp[i][j], Math.max(dp[l][j - 1], pre[i] - pre[l]));
                        }
                    }
                }
                return dp[n][k];
            }

            /**
             * 约束划分长度 子数组长度<=k时,最大和
             * f[i] 表示nums[:i] 划分后,子数组最大值
             * 枚举最后子数组左端点l
             * f[i] = f[r]+val
             */
            public int partitionLen(int[] nums, int k) {
                int n = nums.length;
                int[] dp = new int[n + 1];
                for (int i = 1; i <= n; i++) {
                    //逆序维护区间最大值
                    int max = 0;
                    for (int l = i - 1; l >= i - k && l >= 0; l--) {
                        max = Math.max(max, nums[l]);
                        dp[i] = Math.max(dp[i], dp[l] + max * (i - l));
                    }
                }
                return dp[n];
            }


            private boolean check(int[] nums, int l, int r) {
                return true;
            }

            /**
             * 区间不相交划分 限定区间范围(1~n)或 [is[0],is[1]]区间范围较小
             */
            public long intervalPartition(int n, int[][] is) {
                //按区间结束排序
                List<int[]>[] list = new List[n + 1];
                for (int[] interval : is) {
                    if (list[interval[1]] == null) {
                        list[interval[1]] = new ArrayList<>();
                    }
                    list[interval[1]].add(new int[]{interval[0], interval[1] - interval[0] + interval[2]});
                }

                //dpi 表示 到达第i个点时,能获得的最大价值
                long[] dp = new long[n + 1];
                for (int i = 1; i <= n; i++) {
                    dp[i] = dp[i - 1];
                    if (list[i] == null) {
                        continue;
                    }
                    for (int[] r : list[i]) {
                        dp[i] = Math.max(dp[i], dp[r[0]] + r[1]);
                    }
                }
                return dp[n];
            }

            /**
             * 区间不相交划分  [is[0],is[1]]区间范围较大时
             */
            public int intervalPartition(int[][] is) {
                int n = is.length;
                //按区间排序
                Arrays.sort(is, (a, b) -> a[1] == b[1] ? a[0] - b[0] : a[1] - b[1]);

                //dpi: 在i项中 能获得的最大价值
                int[] dp = new int[n + 1];
                for (int i = 0; i < n; i++) {
                    int s = is[i][0], p = is[i][2];
                    //二分找到上一个区间
                    int l = 0, r = i - 1;
                    while (l <= r) {
                        int m = l + (r - l) / 2;
                        //可无缝衔接<=,否则<
                        if (is[m][1] <= s) {
                            l = m + 1;
                        } else {
                            r = m - 1;
                        }
                    }

                    dp[i + 1] = Math.max(dp[i], dp[r + 1] + p);
                }

                return dp[n];
            }


        }


    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1937873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IMU提升相机清晰度

近期&#xff0c;一项来自北京理工大学和北京师范大学的团队公布了一项创新性的研究成果&#xff0c;他们将惯性测量单元&#xff08;IMU&#xff09;和图像处理算法相结合&#xff0c;显著提升了非均匀相机抖动下图像去模糊的准确性。 研究团队利用IMU捕捉相机的运动数据&…

用程序画出三角形图案

创建各类三角形图案 直角三角形&#xff08;左下角&#xff09; #include <iostream> using namespace std;int main() {int rows;cout << "输入行数: ";cin >> rows;for(int i 1; i < rows; i){for(int j 1; j < i; j){cout << &…

阿里巴巴1688商品详情API返回值全面解析-商品基本信息

阿里巴巴1688商品详情API的返回值是一个包含了商品详细信息的JSON对象&#xff0c;这些信息对于开发者在电商平台上展示商品、进行数据分析等场景非常重要。以下是对阿里巴巴1688商品详情API返回值的全面解析&#xff1a; 一、商品基本信息 商品ID&#xff1a;商品的唯一标识…

gds-linkstack:泛型链式栈

类似于C的stack的泛型容器&#xff0c;初始化、销毁、清空、入栈、出栈、取栈顶、栈空。

java项目-刷题项目实现细节及思路

设计数据表&#xff1a;&#xff1a; 分类表 id主键 分类类型 分类名称 父级id 图标链接 题目标签表 主键 标签名称 分类id&#xff08;标签会和分类进行连接 直接将分类表写进来 减少另一个关联表&#xff09; 排序 题目的信息表 id name 难度 出题人姓名 题目的类别&#…

pyspark使用 graphframes创建和查询图的方法

1、安装graphframes的步骤 1.1 查看 spark 和 scala版本 在终端输入&#xff1a; spark-shell --version 查看spark 和scala版本 1.2 在maven库中下载对应版本的graphframes https://mvnrepository.com/artifact/graphframes/graphframes 我这里需要的是spark 2.4 scala 2.…

CTF-Web习题:[BJDCTF2020]Mark Loves cat

题目链接&#xff1a;Mark Loves cat 解题思路 访问靶机网站后得到如下页面&#xff1a; 先浏览网页&#xff0c;发现最下面有一个"dog"字样&#xff0c;此时翻看源码并没有什么发现 那就例行进行目录扫描&#xff0c;源码泄露扫描&#xff0c;用dirsearch目录扫…

git commit报错: pre-commit hook failed (add --no-verify to bypass)

原因&#xff1a; 在提交前做代码风格检查&#xff0c;若检查不通过&#xff0c;则提交失败 解决方案&#xff1a;进入项目的.git>hooks目录&#xff0c;找到pre-commit文件&#xff0c;删除即可

C# 中的 String.Join 与循环

一.介绍 使用 C# 中的字符串集合时&#xff0c;将多个字符串用指定的分隔符连接成一个字符串是一项常见任务。完成此任务有两种主要方法&#xff1a;使用循环手动连接字符串或使用内置的 String.Join 方法。本文将比较这两种方法&#xff0c;并向您展示为什么在大多数情况下 S…

《JavaSE》---16.<抽象类接口Object类>

目录 前言 一、抽象类 1.1什么是抽象类 1.2抽象类代码实现 1.3 抽象类特点 1.4抽象类的作用 二、接口 2.1什么是接口 2.2接口的代码书写 2.3 接口使用 2.4 接口特点 2.5 实现多个接口 快捷键&#xff08;ctrl i &#xff09;&#xff1a; 2.6接口的好处 2.7 接…

Linux systemd 服务启动失败Main process exited, code=exited, status=203/EXEC

文章目录 问题描述解决思路及方法排除方向一&#xff1a;nohup排除方向二&#xff1a;使用绝对路径而不是相对路径 问题描述 命令&#xff1a; java -Xms1024m -Xmx1024m -jar /usr/local/java/bin/mashang/app.jar --server.port8532 ----spring.profiles.activetest是可以直…

SpringAI简单使用(本地模型+自定义知识库)

Ollama 简介 Ollama是一个开源的大型语言模型服务工具&#xff0c;它允许用户在本地机器上构建和运行语言模型&#xff0c;提供了一个简单易用的API来创建、运行和管理模型&#xff0c;同时还提供了丰富的预构建模型库&#xff0c;这些模型可以轻松地应用在多种应用场景中。O…

使用shedlock实现分布式互斥执行

前言 前序章节&#xff1a;springboot基础(82):分布式定时任务解决方案shedlock 如果你不清楚shedlock&#xff0c;建议先阅读前序章节&#xff0c;再来查看本文。 如果我们不在spring环境下&#xff0c;如何使用shedlock实现分布式互斥执行&#xff1f; 我们可以使用shedl…

【Java数据结构】线性表之栈和队列

栈&#xff08;Stack&#xff09; 简单描述 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&…

DETR算法解读——Transformer在目标检测任务的首次应用

论文&#xff1a;End-to-End Object Detection with Transformers 作者&#xff1a;Nicolas Carion, Francisco Massa, Gabriel Synnaeve, Nicolas Usunier, Alexander Kirillov, Sergey Zagoruyko 机构&#xff1a;Facebook AI 链接&#xff1a;https://arxiv.org/abs/2005.12…

【网络】网络基础(框架)

网络基础 序言&#xff1a;什么是网络&#xff1f;一、协议的概念二、网络位置三、网络协议1、协议分层2、OSI七层模型与TCP/IP五层(或四层)模型 四、网络传输基本流程1、网络传输流程图2、以太网通信&#xff08;1&#xff09;一个小故事&#xff08;2&#xff09;原理i、问题…

解答word图标变白

把WPS卸载了之后就变成白色了&#xff0c;然后在注册表中把word的地址改成office word的地址之后图标变成这样了&#xff0c;怎么办 1.winR打开命令提示符 2.输入regedit&#xff0c;打开注册表编辑器 3.找到下面这个路径 计算机\HKEY_CLASSES_ROOT\Word.Document.8\Defaul…

从零开始学量化~Ptrade使用教程(七)——期权相关操作

期权交易 可点击证券代码右侧的选&#xff0c;进入期权选择菜单。通过选择标的商品&#xff0c;认购期权和认沽期权中间的选项&#xff08;包括代码、成交价、幅度%、隐波%、内在价值、时间价值等&#xff09;&#xff0c;以及认购期权或认沽期权&#xff0c;选择所需的期权标的…

2024年第二季度 DDoS 威胁趋势报告

2024 年上半年&#xff0c;Cloudflare 缓解了 850 万次 DDoS 攻击&#xff1a;第一季度 450 万次&#xff0c;第二季度 400 万次。总体而言&#xff0c;第二季度 DDoS 攻击数量环比下降了 11%&#xff0c;但同比增长了 20%。 DDoS 攻击分布&#xff08;按类型和手段&#xff09…

pycharm中运行.sh文件

最近在跑一个项目代码&#xff0c;里面要运行.sh文件。于是配置了下如何在pycharm中正常运行.sh文件。 首先安装好git&#xff0c;然后 File—>Settings—>Tools—>Terminal—>Shell path&#xff0c;将cmd.exe改成刚刚下载的git的路径&#xff0c;注意选择的是s…