区间和的个数(树状数组、线段树)
给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中,值位于范围 [lower, upper] (包含 lower 和 upper)之内的 区间和的个数 。 区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。
示例 1: 输入:nums = [-2,5,-1], lower = -2, upper = 2 输出:3 解释:存在三个区间:[0,0]、[2,2] 和 [0,2] ,对应的区间和分别是:-2 、-1 、2 。 示例 2: 输入:nums = [0], lower = 0, upper = 0 输出:1
提示:
- 1 <= nums.length <= 105
- -231 <= nums[i] <= 231 - 1
- -105 <= lower <= upper <= 105
- 题目数据保证答案是一个 32 位 的整数
解答:
class Solution {
public int countRangeSum(int[] nums, long lower, long upper) {
long sums[] = new long[nums.length];
for (int i = 0; i < nums.length; i++) {
sums[i] = ((i - 1 >= 0) ? sums[i - 1] : 0) + nums[i];
}
int result = divideAndConquer(sums, 0, sums.length - 1, upper, lower);
return result;
}
private int divideAndConquer(long sums[], int start, int end, long upper, long lower) {
if (start > end)
return 0;
if (start == end)
return (sums[start] <= upper && sums[start] >= lower) ? 1 : 0;
int mid = (start + end) / 2;
int counts = 0;
counts += divideAndConquer(sums, start, mid, upper, lower);
counts += divideAndConquer(sums, mid + 1, end, upper, lower);
int ls = start, le = mid;
while (le >= start && sums[mid + 1] - sums[le] <= upper)
le--;
for (int r = mid + 1; r <= end; r++) {
while (ls <= mid && sums[r] - sums[ls] >= lower)
ls++;
while (le + 1 <= mid && sums[r] - sums[le + 1] > upper)
le++;
if (ls - le - 1 < 0)
continue;
counts += (ls - le - 1);
}
ls = start;
int i = 0, r = mid + 1;
long merged[] = new long[end - start + 1];
while (ls <= mid || r <= end) {
if (ls > mid || (r <= end && sums[r] < sums[ls])) {
merged[i++] = sums[r++];
} else {
merged[i++] = sums[ls++];
}
}
for (i = 0; i < merged.length; i++) {
sums[start + i] = merged[i];
}
return counts;
}
}
将数据流变为多个不相交区间(设计、二分查找)
给你一个由非负整数 a1, a2, ..., an 组成的数据流输入,请你将到目前为止看到的数字总结为不相交的区间列表。 实现 SummaryRanges 类:
- SummaryRanges() 使用一个空数据流初始化对象。
- void addNum(int val) 向数据流中加入整数 val 。
- int[][] getIntervals() 以不相交区间 [starti, endi] 的列表形式返回对数据流中整数的总结。
示例:
输入:
["SummaryRanges", "addNum", "getIntervals", "addNum", "getIntervals", "addNum", "getIntervals", "addNum", "getIntervals", "addNum", "getIntervals"] [[], [1], [], [3], [], [7], [], [2], [], [6], []]
输出:
[null, null, [[1, 1]], null, [[1, 1], [3, 3]], null, [[1, 1], [3, 3], [7, 7]], null, [[1, 3], [7, 7]], null, [[1, 3], [6, 7]]]
解释:
提示:
- 0 <= val <= 104
- 最多调用 addNum 和 getIntervals 方法 3 * 104 次
**进阶:**如果存在大量合并,并且与数据流的大小相比,不相交区间的数量很小,该怎么办?
解答:
class SummaryRanges {
private final TreeSet<Integer> tree = new TreeSet<>();
public void addNum(int val) {
tree.add(val);
}
public int[][] getIntervals() {
ArrayList<int[]> result = new ArrayList<>(1 << 2);
Iterator<Integer> iterator = tree.iterator();
int[] array = new int[tree.size()];
int i = 0;
while (iterator.hasNext())
array[i++] = iterator.next();
int length = array.length;
if (length == 0)
return result.toArray(new int[0][]);
int start = array[0];
for (i = 0; i < length; ++i) {
if (i + 1 < length && array[i + 1] - array[i] != 1) {
result.add(new int[] { start, array[i] });
start = array[i + 1];
} else if (i + 1 == length) {
result.add(new int[] { start, array[i] });
}
}
return result.toArray(new int[result.size()][]);
}
}
逆波兰表达式求值(栈、数组)
根据 逆波兰表示法,求表达式的值。 有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
- 整数除法只保留整数部分。
- 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1: 输入:tokens = ["2","1","+","3",""] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9 示例 2: 输入:tokens = ["4","13","5","/","+"] 输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6 示例 3: 输入:tokens = ["10","6","9","3","+","-11","","/","*","17","+","5","+"] 输出:22 解释: 该算式转化为常见的中缀算术表达式为: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22
提示:
- 1 <= tokens.length <= 104
- tokens[i] 要么是一个算符("+"、"-"、"*" 或 "/"),要么是一个在范围 [-200, 200] 内的整数
逆波兰表达式: 逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
- 该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
- 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。
解释:
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (String s : tokens) {
if (s.equals("+")) {
stack.push(stack.pop() + stack.pop());
} else if (s.equals("-")) {
stack.push(-stack.pop() + stack.pop());
} else if (s.equals("*")) {
stack.push(stack.pop() * stack.pop());
} else if (s.equals("/")) {
int num1 = stack.pop();
stack.push(stack.pop() / num1);
} else {
stack.push(Integer.parseInt(s));
}
}
return stack.pop();
}
}
本文内容到此结束了, 如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。 如有错误❌疑问💬欢迎各位大佬指出。 主页:共饮一杯无的博客汇总👨💻
保持热爱,奔赴下一场山海。🏃🏃🏃