Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/359.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/spring-boot/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 递归算法和非递归算法的性能,大O表示法_Java_Algorithm_Performance_Recursion_Big O - Fatal编程技术网

Java 递归算法和非递归算法的性能,大O表示法

Java 递归算法和非递归算法的性能,大O表示法,java,algorithm,performance,recursion,big-o,Java,Algorithm,Performance,Recursion,Big O,想象一下,我必须检查一个字符串的所有字母是否都在另一个字符串中。我想比较两种实现,一种是尾部递归,另一种是使用hashMap。以下是两种实现: private boolean isPossible(final String left, final String right) { boolean toReturn = false; if (left.isEmpty()) { toReturn = true; } else { char cha

想象一下,我必须检查一个字符串的所有字母是否都在另一个字符串中。我想比较两种实现,一种是尾部递归,另一种是使用hashMap。以下是两种实现:

private boolean isPossible(final String left, final String right) {
    boolean toReturn = false;
    if (left.isEmpty()) {
        toReturn = true;
    } else {
        char charAt = left.charAt(0);
        final int index = right.indexOf(charAt);
        toReturn = index != -1 ? isPossible(left.substring(1), stringWithoutIndex(right, index)) : false;
    }
    return toReturn;
}
以及hashMap解决方案:

public boolean isPossible(String left, String right) {
    HashMap<Character, Integer> occurrencesMap = createOccurrenceMapFor(left);
    HashMap<Character, Integer> withTheLettersInRightRemoved = removeLettersFoundIn(right, occurrencesMap);
    return checkThatWeCanWriteTheMessage(withTheLettersInRightRemoved);
}

private HashMap<Character, Integer> removeLettersFoundIn(final String string, final HashMap<Character, Integer> occurrencesMap) {
    HashMap<Character, Integer> lettersRemoved = new HashMap<>(occurrencesMap);
    for (char c : string.toCharArray()) {
        if (lettersRemoved.containsKey(c)) 
            lettersRemoved.put(c, lettersRemoved.get(c).intValue() - 1); 
    }
    return lettersRemoved;
}

private HashMap<Character, Integer> createOccurrenceMapFor(String string) {
    HashMap<Character, Integer> occurrencesMap = new HashMap<>();
    for (char c : string.toCharArray()) {

        if (occurrencesMap.containsKey(c)) 
            occurrencesMap.put(c, occurrencesMap.get(c).intValue() + 1); 
        else 
            occurrencesMap.put(c, 1);
    }
    return occurrencesMap;
}

private boolean checkThatWeCanWriteTheMessage(HashMap<Character, Integer> occurrencesMap) {
    for (char c : occurrencesMap.keySet()){
        if (withTheLettersInMagazineRemoved.get(c) > 0) {
            return false;
        }
    }
    return true;
}
公共布尔值是可能的(字符串左、字符串右){
HashMap OccurrenceMap=CreateCurrenceMapfor(左);
删除了TTERSINRIGHT的HashMap=删除了TTERSINRFOUND(右侧,发生率MAP);
返回我们可以写入邮件的支票(删除邮件右侧);
}
私有HashMap RemoveletterFoundin(最终字符串字符串,最终HashMap发生映射){
HashMap-lettersRemoved=新的HashMap(发生率映射);
for(char c:string.toCharArray()){
如果(已删除的信件,包括(c))
lettersRemoved.put(c,lettersRemoved.get(c).intValue()-1);
}
退回信件;
}
私有HashMap CreateCurrenceMapfor(字符串){
HashMap OccurrenceMap=新HashMap();
for(char c:string.toCharArray()){
if(发生率MAP.containsKey(c))
occurrencesMap.put(c,occurrencesMap.get(c.intValue()+1);
其他的
发生率map.put(c,1);
}
返回发生率MAP;
}
我们可以写入消息的私有布尔检查(HashMap occurrencesMap){
for(字符c:occurrencesMap.keySet()){
如果(带文本的InmagazineRemoved.get(c)>0){
返回false;
}
}
返回true;
}

我认为这两种解决方案都有O(n)性能,因为它们都没有for循环,等等。但是,一旦我比较了时间,我就会发现hashMap解决方案比递归解决方案快得多。当然,这是有道理的,但我想知道为什么,因为在理论上,两者都有O(n)。我说的对吗?

第一个解决方案会检查第一个字符串中的每个字符,即O(N),但对于每个字符,它会搜索第二个字符串中的匹配字符,这会给出另一个内部/嵌套的O(N)和O(N^2)

第二个解决方案迭代第一个字符串O(N),然后迭代第二个字符串O(N),最后迭代hashmap,hashmap只包含有限的字符范围(一些常量)。总数为O(N)+O(N)+C=O(N)
aString。indexOf(aChar)
使用源字符串中字符的循环实现;因此它是O(
aString.length
)。而
stringWithoutIndex(aString,anIndex)
不能在优于O的(
aString.length
)中实现。在最坏的情况下(当
left
中的所有字符出现在
right
中时),您将执行O(
left.length
*
right.length
)操作

您的第一个代码片段相当于:

private boolean isPossible(String left, String right) {
  // loop will repeat left.length times at most
  while (true) {
    if (left.isEmpty()) {
      return true;
    } else {
      char first = left.charAt(0);
      left = left.substring(1);

      // indexOf: O(right.length)
      int index = -1;
      for (int i=0; i<right.length; i++) {
        if (right.charAt(i) == first) { 
          index = i; 
          break; 
        }
      }

      if (index >= 0) {
        // stringWithoutIndex: concatenating strings is O(size of result)
        right = right.substring(0, index) + right.substring(index+1);
      } else {
        return false;
      }
    }
  }
}
私有布尔值是可能的(字符串左、字符串右){
//循环最多将重复左.长次
while(true){
if(left.isEmpty()){
返回true;
}否则{
char first=left.charAt(0);
左=左。子字符串(1);
//索引of:O(右。长度)
int指数=-1;
对于(int i=0;i=0){
//stringWithoutIndex:串联字符串为O(结果大小)
右=右。子字符串(0,索引)+右。子字符串(索引+1);
}否则{
返回false;
}
}
}
}
我只将递归转换为迭代,并扩展了
indexOf
stringWithoutIndex
——这使得复杂度更容易计算,因为循环很容易看到

请注意,从递归到迭代的机械转换(反之亦然)不会改变复杂度类;虽然,当代码可以编写为尾部递归时(如本例中的
isPossible
),迭代代码可以更快一些,并且不会耗尽堆栈(因为它不使用它)。因此,许多编译器在幕后将尾部递归转换为迭代

对于内联函数也可以提出类似的论点(内联通常更快,但将保持在同一个大o复杂度类中),不过还有一个额外的大小权衡:如果代码在许多地方使用,内联会使编译的程序更大。

第一个算法:o(n*m)

第二个算法:O(n)-您将看到可以从
m
的大小中提取

请参见下面的反馈


第一个节目 递归函数不是尾部递归函数。没有尾声。 但我认为可以通过分离
return
语句将其转换为尾部递归函数。例如(我缩短了您的代码,但请验证)

不确定
stringWithoutIndex(右,index)
到底是什么,但最好知道时间复杂度。我假设它只返回右字符串,而不返回给定索引处的字符,即
O(m)
m
是右字符串的长度)

我用数字表示函数中的代码行(
1、2、3、4和5
)。我假设左字符串的长度为
n
,右字符串的长度为
m

  • O(1)
  • O(1)
  • O(m)
    ,因为在最坏的情况下,您会在最后一个索引处找到字符
  • O(1)
  • O(m)
    ,删除右字符串中的一个字符
  • 把这些加在一起,你会得到一次迭代的
    O(m)
    。由于迭代次数与左字符串中的字符相同,因此该算法的时间复杂度为
    O(m*n)

    • 如果
      m
      ,则它小于
      O(n^2)
    • 如果
      m=n
      ,那么它就是
      O(n^2)
    • 如果
      m>n
      ,那么它就是
      O(n*m)
    最后,该算法的复杂度等级为
    O(
    
    private boolean isPossible(final String left, final String right) {
        if (left.isEmpty()) return true;
        char firstChar = left.charAt(0);
        int index = right.indexOf(charAt);
        if (index == -1) return false;
        return isPossible(left.substring(1), stringWithoutIndex(right, index));
    }
    
    O(n*k' + m*k+n + (n-m)*l)