自学内容网 自学内容网

【算法】算法模板

算法模板



简介

博主在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];
            }


        }


    }

在这里插入图片描述


原文地址:https://blog.csdn.net/qq_44765647/article/details/140547000

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!