除法求值
LeetCode339题,已知一些等式,根据这些等式换算出各个字母的联系,并求出题目给出的等式值。
思路分析:问题的方程式中会出现未知的方程式解,比如没有出现过的字母,这类方程式的解通常是不存在的,另外一些方程式会存在两个字母没有明显的交叉点,但是可以通过已知方程式的解,进行代换得到答案。这时我们可以把代换的过程,看作是从A点如何到B点的一个无向图求路径的问题,比如a/b=2.0,可以看作是A点到B点的路长为2.0,所以寻找没有明面的方程式的解,其实就是找两点之间的距离。
class Edge {
String from;
String to;
double val;
public Edge(String from, String to, double val) {
this.from = from;
this.to = to;
this.val = val;
}
}
public class Solution3 {
HashMap<String, List<Edge>> mEdges = new HashMap<>();
double[] mRes;
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
for (int i = 0; i < equations.size(); i++) {
String s1 = equations.get(i).get(0);
String s2 = equations.get(i).get(1);
double v1 = values[i];
//将s1通向s2的边关联
List<Edge> edges1 = mEdges.get(s1);
if (edges1 == null) {
edges1 = new ArrayList<>();
mEdges.put(s1, edges1);
}
edges1.add(new Edge(s1, s2, v1));
//s2也需要关联s1
List<Edge> edges2 = mEdges.get(s2);
if (edges2 == null) {
edges2 = new ArrayList<>();
mEdges.put(s2, edges2);
}
edges2.add(new Edge(s2, s1, 1 / v1));
}
List<String> visited = new ArrayList<>();
for (int i = 0; i < queries.size(); i++) {
mRes[i] = dfs(queries.get(i).get(0), queries.get(i).get(1), visited);
visited.clear();
}
return mRes;
}
private double dfs(String start, String dest, List<String> visited) {
if (!mEdges.containsKey(start) || !mEdges.containsKey(dest)) {
return -1.0;
}
//将访问过的顶点加入
visited.add(start);
if (start.equals(dest)) {
return 1.0;
}
//获取start顶点的边
List<Edge> edges = mEdges.get(start);
if (edges == null || edges.isEmpty()) {
return -1.0;
}
for (Edge edge : edges) {
if (visited.contains(edge.to)) {
continue;
}
//循环遍历查找,直到找到目标点
double res = dfs(edge.to, dest, visited);
if (res != -1.0) {
return res * edge.val;
}
}
return -1.0;
}
}
和至少为 K 的最短子数组
LeetCode862题,求一个数组里的连续的子数组的和,大于或等于指定的值,返回该数组的最短长度。
思路分析:
1.让数组A不断的进行一个叠加sum,当发现sum小于1的时候,可以认为前面这段叠加是无效的,将sum置为0记录重新开始叠加的下标
2.另外这里还需对原数组做处理,将后一个小于0的数置为0,并且将该数和前一个数相加,循环判断,直到遇见一个不为0的数,该操作为下一步做铺垫
3.当sum>=K的时候,就要计算最小子数组的长度了,这里实际得出的长度并不就是最终的一个长度,需要判断sum-A[begin]是否大于K,如果条件为真,将begin向后移一位并且从sum中减去,直到条件为假,最终求出连续子数组的最小长度。
int sum = 0, begin = 0, res = -1;
for (int i = 0; i < A.length; i++) {
//不断进行一个叠加
sum += A[i];
//当sum小于1时,前面这段叠加可以认为是无效的
if (sum < 1) {
sum = 0;
//记录新开始的叠加下标的子数组
begin = i + 1;
continue;
}
//这里需要将小于0的数不断往前处理,为后续计算res做处理
for (int j = i; A[j] < 0; j--) {
A[j - 1] = A[j] + A[j - 1];
A[j] = 0;
}
if (sum >= K) {
//对begin做处理,如果A[begin]是大于0的,则后面的数也一定是大于0的
while (sum - A[begin] >= K)
sum -= A[begin++];
int length = i - begin + 1;
if (res < 0 || res > length)
res = length;
}
}
return res;
删除一次得到子数组最大和
LeetCode1186题,同样是求子数组的最大和,并且允许删除子数组中的一个元素,返回得出的最大和。
思路分析:
动态规划的思想,要保存之前数组计算的值,并且尝试性删除当前数组中最小的负数,来让子数组的和最大,当程序继续进行后,遇见了更小的值,导致了删除后的元素大于当前数组和的话,就按照这样的规则运行下去。
int len = arr.length;
int[] f = new int[len];
int[] g = new int[len];
f[0] = arr[0];
g[0] = -200001;
int res = arr[0];
for (int i = 1; i < len; i++) {
//f记录所有值的叠加情况,没有删除的问题
f[i] = Math.max(f[i - 1] + arr[i], arr[i]);//其实就是f(i-1)是否<0
//如果arr[i]是一个负数,那么前者就会比后者小,这时g[i]就需要等于f[i-1]假设此时删除的是arr[i],如果下一次遇到更小的值,就回把这个值给加上
g[i] = Math.max(g[i - 1] + arr[i], f[i - 1]);
res = Math.max(res, Math.max(f[i], g[i]));
}
return res;