在 for 循环时间和 space 复杂度中嵌套 Collection.stream()
Nested Collection.stream() in for loop time and space complexity
我有一个算法,我需要找到它的 space 和时间复杂度。
public static List<Integer> myList(String niceKeys, String badKeys,
List<Integer> myIds,
List<String> reviews, int k) {
List<String> niceKeyToken = Arrays.asList(niceKeys.split(" "));
List<String> badKeyToken = Arrays.asList(badKeys.split(" "));
Map<Integer, Integer> niceReview = new HashMap<>();
for (int i = 0; i < reviews.size(); i++) {
int myId = myIds.get(i);
List<String> review = Arrays.asList(reviews.get(i).split(" "));
int currentNice = (int) review.stream()
.filter(token -> niceKeyToken.contains(token))
.count();
int currentBad = (int) review.stream()
.filter(token -> badKeyToken.contains(token))
.count();
int total = currentNice * 3 + currentBad * -1;
int previous = niceReview.getOrDefault(myId, 0);
niceReview.put(myId, previous + total);
}
List<Integer> finalList = niceReview.entrySet()
.stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.limit(k)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
return finalList;
}
如果评论的大小是 M,它应该是 O(M)。
但是然后我们在 for loop
里面做 review.stream()
这应该会增加复杂性但我不确定有多少? space 复杂度是多少?
任何帮助将不胜感激。
如果评论大小为 M,假设评论大小为 N,niceKeyToken - X,badKeyToken - Y,时间复杂度应为 O(M * (N * X + N * Y)),我不太确定关于 space 的复杂性,您正在使用字符串,所以我想说这取决于字符串的大小。我认为最好用大测试数据做一些基准测试。
首先关于 niceKeyToken 和 badKeyToken - 您仅将它们用于搜索,因此 List 不是数据结构的最佳选择,您应该使用 HashSet。 ArrayList.contains() 的时间复杂度为 O(N),而 HashSet - O(1).
其次关于流 - 您正在流式传输评论列表两次,一次用于好的密钥,然后两次用于坏的密钥。你只是对流进行简单的过滤,我认为流对于这种情况来说是一种矫枉过正,你可以通过一个循环来逃脱。流有时可能很昂贵而且很慢(同样最好进行基准测试)。
第三点源于第二点——评论不需要保存在列表中,如果你只是迭代它,一个数组就足够了。
我会这样做:
public static List<Integer> myList(String niceKeys, String badKeys, List<Integer> myIds, List<String> reviews, int k) {
//HashSet contains method returns in constant time, for list, if the element, you are looking for is last
//potentially you have to traverse entire collection
Set<String> niceKeyToken = new HashSet<>(Arrays.asList(niceKeys.split(" ")));
Set<String> badKeyToken = new HashSet<>(Arrays.asList(badKeys.split(" ")));
Map<Integer, Integer> niceReview = new HashMap<>();
for (int i = 0; i < reviews.size(); i++) {
int myId = myIds.get(i);
//array would suffice here, only iterating the elements
String[] review = reviews.get(i).split(" ");
int currentNice = 0;
int currentBad = 0;
//single iteration, instead of twice with streams
for (int j = 0; j < review.length; j++) {
String token = review[j];
//quick lookup for token, because of HashSet
if (niceKeyToken.contains(token)) {
currentNice++;
}
//quick lookup for token, because of HashSet
if (badKeyToken.contains(token)) {
currentBad++;
}
}
int total = currentNice * 3 + currentBad * -1;
int previous = niceReview.getOrDefault(myId, 0);
niceReview.put(myId, previous + total);
}
return niceReview.entrySet()
.stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.limit(k)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
这应该可以将时间复杂度降低到 O(M * N)。
我有一个算法,我需要找到它的 space 和时间复杂度。
public static List<Integer> myList(String niceKeys, String badKeys,
List<Integer> myIds,
List<String> reviews, int k) {
List<String> niceKeyToken = Arrays.asList(niceKeys.split(" "));
List<String> badKeyToken = Arrays.asList(badKeys.split(" "));
Map<Integer, Integer> niceReview = new HashMap<>();
for (int i = 0; i < reviews.size(); i++) {
int myId = myIds.get(i);
List<String> review = Arrays.asList(reviews.get(i).split(" "));
int currentNice = (int) review.stream()
.filter(token -> niceKeyToken.contains(token))
.count();
int currentBad = (int) review.stream()
.filter(token -> badKeyToken.contains(token))
.count();
int total = currentNice * 3 + currentBad * -1;
int previous = niceReview.getOrDefault(myId, 0);
niceReview.put(myId, previous + total);
}
List<Integer> finalList = niceReview.entrySet()
.stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.limit(k)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
return finalList;
}
如果评论的大小是 M,它应该是 O(M)。
但是然后我们在 for loop
里面做 review.stream()
这应该会增加复杂性但我不确定有多少? space 复杂度是多少?
任何帮助将不胜感激。
如果评论大小为 M,假设评论大小为 N,niceKeyToken - X,badKeyToken - Y,时间复杂度应为 O(M * (N * X + N * Y)),我不太确定关于 space 的复杂性,您正在使用字符串,所以我想说这取决于字符串的大小。我认为最好用大测试数据做一些基准测试。
首先关于 niceKeyToken 和 badKeyToken - 您仅将它们用于搜索,因此 List 不是数据结构的最佳选择,您应该使用 HashSet。 ArrayList.contains() 的时间复杂度为 O(N),而 HashSet - O(1).
其次关于流 - 您正在流式传输评论列表两次,一次用于好的密钥,然后两次用于坏的密钥。你只是对流进行简单的过滤,我认为流对于这种情况来说是一种矫枉过正,你可以通过一个循环来逃脱。流有时可能很昂贵而且很慢(同样最好进行基准测试)。
第三点源于第二点——评论不需要保存在列表中,如果你只是迭代它,一个数组就足够了。
我会这样做:
public static List<Integer> myList(String niceKeys, String badKeys, List<Integer> myIds, List<String> reviews, int k) {
//HashSet contains method returns in constant time, for list, if the element, you are looking for is last
//potentially you have to traverse entire collection
Set<String> niceKeyToken = new HashSet<>(Arrays.asList(niceKeys.split(" ")));
Set<String> badKeyToken = new HashSet<>(Arrays.asList(badKeys.split(" ")));
Map<Integer, Integer> niceReview = new HashMap<>();
for (int i = 0; i < reviews.size(); i++) {
int myId = myIds.get(i);
//array would suffice here, only iterating the elements
String[] review = reviews.get(i).split(" ");
int currentNice = 0;
int currentBad = 0;
//single iteration, instead of twice with streams
for (int j = 0; j < review.length; j++) {
String token = review[j];
//quick lookup for token, because of HashSet
if (niceKeyToken.contains(token)) {
currentNice++;
}
//quick lookup for token, because of HashSet
if (badKeyToken.contains(token)) {
currentBad++;
}
}
int total = currentNice * 3 + currentBad * -1;
int previous = niceReview.getOrDefault(myId, 0);
niceReview.put(myId, previous + total);
}
return niceReview.entrySet()
.stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.limit(k)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
这应该可以将时间复杂度降低到 O(M * N)。