AlgorithmClassfication4 区间的前缀和,可以考虑前缀数组,查询O(1),但是更新会变为O(n)
树状数组 是一种可以动态维护序列前缀和的数据结构(序列下标从 11 开始)
而线段树 ,更新和查询都是O(logn)
前缀和 前缀和,就是用一个前缀和数组,将前面的数值,都求和求起来。
前缀和 + 二分法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Solution { public int minSubArrayLen (int target, int [] nums) { int n = nums.length; int [] sums = new int [n]; sums[0 ] = nums[0 ]; for (int i=1 ; i<n; i++) { sums[i] = nums[i] + sums[i-1 ]; } int ans = Integer.MAX_VALUE; for (int i = 0 ; i < n; i++) { int right = i, left = 0 , t = sums[i] - target; if (t < 0 ) { continue ; } while (left <= right) { int mid = (left + (right - left >> 1 )); if (sums[mid] == t) { left = right = mid; break ; } else if (sums[mid] > t) { right = mid - 1 ; } else { left = mid + 1 ; } } ans = Math.min(ans, i - right); } return ans = = Integer.MAX_VALUE ? 0 : ans; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int subarraySum (int [] nums, int k) { int n = nums.length; int [] preSums = new int [n+1 ]; int ans = 0 ; Map<Integer, Integer> map = new HashMap <>(); map.put(0 , 1 ); for (int i=1 ; i<=n; i++) { preSums[i] = nums[i-1 ] + preSums[i-1 ]; ans += map.getOrDefault(preSums[i] - k, 0 ); map.put(preSums[i], map.getOrDefault(preSums[i], 0 ) + 1 ); } return ans; } }
这个自己利用前缀和的思想,得到区间串的hash,还是没太懂理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution { int N = (int )10e5 + 10 ; int [] h = new int [N], p = new int [N]; int P = 131313 ; public List<String> findRepeatedDnaSequences (String s) { int n = s.length(); List<String> ans = new ArrayList <>(); p[0 ] = 1 ; for (int i = 1 ; i <= n; i++) { h[i] = h[i-1 ] * P + s.charAt(i-1 ); p[i] = p[i-1 ] * P; } Map<Integer, Integer> map = new HashMap <>(); for (int i = 1 ; i + 10 - 1 <= n; i++) { int j = i + 10 - 1 ; int hash = h[j] - h[i-1 ] * p[j-i+1 ]; int cnt = map.getOrDefault(hash, 0 ); if (cnt == 1 ) { ans.add(s.substring(i-1 , j)); } map.put(hash, cnt + 1 ); } return ans; } }
树状数组 树状数组 java_树状数组详解
树状数组与前缀和差分数组以及二维树状数组
树状数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Solution { int N = (int )10e5 + 10 ; int [] tr = new int [N]; int lowbit (int x) { return x & (-x); } void update (int x, int v) { for (int i = x; i < N; i += lowbit(i)) { tr[i] += v; } } int query (int x) { int ans = 0 ; for (int i = x; i > 0 ; i -= lowbit(i)) { ans += tr[i]; } return ans; } public int numTeams (int [] rating) { int n = rating.length, ans = 0 ; for (int i = 0 ; i < n; i++) { for (int k = i + 1 ; k < n; k++) { int a = rating[i], c = rating[k]; if (a > c) { ans += query(a - 1 ) - query(c); } else { ans += query(c - 1 ) - query(a); } update(c, 1 ); } for (int k = i + 1 ; k < n; k++) { update(rating[k], -1 ); } } return ans; } }
双树状数组优化-枚举终点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Solution { int N = (int )10e5 + 10 ; int lowbit (int x) { return x & (-x); } void update (int [] tr, int x, int v) { for (int i = x; i < N; i += lowbit(i)) { tr[i] += v; } } int query (int [] tr, int x) { int ans = 0 ; for (int i = x; i > 0 ; i -= lowbit(i)) { ans += tr[i]; } return ans; } public int numTeams (int [] rating) { int n = rating.length, ans = 0 ; int [] trLeft = new int [N], trRight = new int [N]; for (int num : rating) { update(trRight, num, 1 ); } for (int i = 0 ; i < n; i++) { update(trRight, rating[i], -1 ); ans += query(trLeft, rating[i] - 1 ) * (query(trRight, N - 1 ) - query(trRight, rating[i])); ans += query(trRight, rating[i] - 1 ) * (query(trLeft, N - 1 ) - query(trLeft, rating[i])); update(trLeft, rating[i], 1 ); } return ans; } }
树状数组离散化 树状数组+离散化
Treeset排序 + 去重 map重新编号 做法,说白了,就是将值域的数字,去掉不存在的压缩。
首先用treeset 将值域存在的都放进去,因为排好序了,然后一个个编号(map ),这样肯定就缩小了呀。后面每一个数组就用该编号代替了。
只能说细节挺多的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 class Solution { int N = 0 ; int [] tr; public int countRangeSum (int [] nums, int lower, int upper) { int n = nums.length; long [] sums = new long [n+1 ]; for (int i=1 ; i<=n; i++) { sums[i] = sums[i-1 ] + nums[i-1 ]; } Set<Long> set = new TreeSet <>(); set.add(0l ); for (long sum: sums) { set.add(sum); set.add(sum - upper); set.add(sum - lower); } Map<Long, Integer> map = new HashMap <>(); int index = 1 ; for (long sum: set) { map.put(sum, index++); } N = index + 10 ; tr = new int [N]; int ans = 0 ; update(map.get(0l ), 1 ); for (int i=0 ; i<n; i++) { ans += preSum(map.get(sums[i+1 ] - lower)) - preSum(map.get(sums[i+1 ] - upper) - 1 ); update(map.get(sums[i+1 ]), 1 ); } return ans; } int lowbit (int x) { return x & (-x); } void update (int x, int value) { for (int i=x; i < N; i += lowbit(i)) { tr[i] += value; } } int preSum (int x) { int ans = 0 ; for (int i=x; i>0 ; i -= lowbit(i)) { ans += tr[i]; } return ans; } }
拟合:三元组ai>aj, ai=ak 【2022-08-20】网易秋招笔试四道编程题
由于a的值太大,所以考虑离散化
两个数组分别存放左边开始,右边开始的对应值的个数。如l[i]表示在0~当前下标中,i出现的个数。
然后用树状数组 维护相同数字的个数的乘积。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import java.util.*;import java.util.stream.Collectors;public class SanYuanZu { static int N = 1 ; static int [] tr; static int lowbit (int x) { return x & (-x); } static void update (int x, int value) { for (int i = x; i < N; i += lowbit(i)) { tr[i] += value; } } static int query (int x) { int ans = 0 ; for (int i = x; i > 0 ; i -= lowbit(x)) { ans += tr[i]; } return ans; } public static void main (String[] args) { Scanner scanner = new Scanner (System.in); int [] nums = Arrays.stream(scanner.nextLine().split(" " )).mapToInt(Integer::valueOf).toArray(); Set<Integer> set = Arrays.stream(nums).boxed().collect(Collectors.toCollection(() -> new TreeSet <>((a, b) -> b - a))); Map<Integer, Integer> map = new HashMap <>(); for (Integer num : set) { map.put(num, N++); } nums = Arrays.stream(nums).map(map::get).toArray(); int [] l = new int [N+10 ], r = new int [N + 10 ]; tr = new int [N + 10 ]; for (int num : nums) { r[num] += 1 ; } int ans = 0 ; for (int num : nums) { ans += query(num-1 ); update(num, -l[num] * r[num]); l[num] += 1 ; r[num] -= 1 ; update(num, l[num] * r[num]); } System.out.println(ans); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 class NumArray { int [] tr; int [] nums; int n; int lowbit (int x) { return x & (-x); } void add (int x, int value) { x += 1 ; for (int i = x; i <= n; i += lowbit(i)) { tr[i] += value; } } int query (int x) { x += 1 ; int ans = 0 ; for (int i = x; i > 0 ; i -= lowbit(i)) { ans += tr[i]; } return ans; } public NumArray (int [] nums) { this .nums = nums; n = nums.length; tr = new int [n + 10 ]; for (int i = 0 ; i < nums.length; i++) { add(i, nums[i]); } } public void update (int index, int val) { add(index, val - nums[index]); nums[index] = val; } public int sumRange (int left, int right) { return query(right) - query(left-1 ); } }
线段树 线段树(java)
线段树详解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class MyCalendarTwo { TreeMap<Integer, Integer> map; public MyCalendarTwo () { map = new TreeMap <>(); } public boolean book (int start, int end) { Integer pre = map.floorKey(start), next = map.ceilingKey(end); if ((pre == null || map.get(pre) <= start) && (next == null || map.get(next) >= end)) { return true ; } return false ; } }
本来需要开辟一个数组存储连续的数据,但是连续的数组中有些不会存储到,那么考虑使用TreeMap
区间的前缀和,可以考虑前缀数组,查询O(1),但是更新会变为O(n)
树状数组 是一种可以动态维护序列前缀和的数据结构(序列下标从 11 开始)
而线段树 ,更新和查询都是O(logn)
拓扑排序 找到没有环的节点,首先将入与出翻转,那么入度为0的,原来出度是0,肯定不会出现环,然后用拓扑思想遍历下去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Solution { public List<Integer> eventualSafeNodes (int [][] graph) { int n = graph.length; List<Integer>[] revGraph = new ArrayList [n]; int [] cnts = new int [n]; for (int i = 0 ; i < n; i++) { revGraph[i] = new ArrayList <>(); } for (int u=0 ; u < n; u++) { for (int v: graph[u]) { cnts[u] += 1 ; revGraph[v].add(u); } } Deque<Integer> queue = new ArrayDeque <>(); for (int i = 0 ; i < n; i++) { if (cnts[i] == 0 ) { queue.offerLast(i); } } while (!queue.isEmpty()) { int v = queue.pollFirst(); for (int u: revGraph[v]) { cnts[u] -= 1 ; if (cnts[u] == 0 ) { queue.offerLast(u); } } } return IntStream.range(0 , cnts.length).filter(index -> cnts[index] == 0 ).boxed().collect(Collectors.toList()); } }