【问题标题】:What is the space complexity of my recursive solution for Count and Say?我的 Count and Say 递归解决方案的空间复杂度是多少?
【发布时间】:2017-09-23 07:16:40
【问题描述】:

给定一个数字序列:1, 11, 21, 1211, 111221, ... 序列中数字的生成规则如下: 1 是“一个 1”,所以 11。 11 是“两个 1”,所以 21。 21 是“一个 2 后跟一个 1”,所以 1211。 在这个序列中找到第 n 个数字。 假设: n从1开始,第一个数字是“1”,第二个数字是“11”

我的解决方案:

public String countAndSay(int n) {
List<Integer> result = helper(n);
StringBuilder sb = new StringBuilder();
for(Integer num : result) {
      sb.append(num);
}
return sb.toString();
}

private List<Integer> helper(int n) {
List<Integer> result = new ArrayList<Integer>();
//base case
if (n == 1) {
     result.add(1);
     return result; 
}
List<Integer> smaller = helper(n - 1);
int count = 1;
for (int i = 0; i < smaller.size(); i++) {
    if (i + 1 > smaller.size() - 1 ||
              !smaller.get(i + 1).equals(smaller.get(i))) {
         result.add(count);
         result.add(smaller.get(i));
         count = 1;
    } else {
         count++;
    }
}
    return result;
}

我对大 O 符号空间复杂度的理解是,当方法运行时,最大可能的额外空间不等待垃圾收集。 所以我对这个解决方案的空间复杂度的想法是,由于递归调用是由“List small = helper(n - 1);”行完成的,因此较低级别的递归调用的额外空间已经在等待垃圾收集。所以低级别的时间复杂度不应该计入当前级别。该解决方案的空间复杂度应为 O(n)。我说的对吗?

【问题讨论】:

    标签: java algorithm recursion space-complexity


    【解决方案1】:

    是的,从 java 的角度来看,你是对的。大 O 符号表示空间/时间随着输入大小的增加而增长的顺序。您的代码占用的空间量将随着n 输入的增加而线性增加。所以这个方案的空间复杂度确实是O(n)

    对于递归,空间复杂度涉及在最坏情况下递归/函数堆栈所占用的空间。因为当前在递归堆栈中的函数调用不符合当前的垃圾回收调用。当helper(1) 被调用时,该程序将占用的最大空间,因为此时helper(n), helper(n - 1) .... helper(2) 所有使用本地资源的调用都在递归堆栈中,不符合垃圾回收条件。

    但是,您可以在没有递归的情况下在常量空间中执行此操作。

    public String countAndSay(int n) {
            StringBuilder curr = new StringBuilder("1");
            StringBuilder prev;
            int count;
            char say;
            for (int i = 1; i < n; i++) {
                prev = curr;
                curr = new StringBuilder();       
                count = 1;
                say = prev.charAt(0);
    
                for (int j = 1, len = prev.length(); j < len; j++) {
                    if (prev.charAt(j) != say){
                        curr.append(count).append(say);
                        count = 1;
                        say = prev.charAt(j);
                    }
                    else count++;
                }
                curr.append(count).append(say);
            }                   
            return curr.toString();
    
    }
    

    希望对你有帮助!

    编辑

    我仍然对细节有些困惑。当我们调用 help(1) 时, 所有 help(2) 到 help(n) 都在递归堆栈中,但当时, 每个堆栈的空间复杂度是 O(1),因为我们只创建了一个空的 列表,最大空间复杂度为 (n-1) * O(1) + O(n)(这是助手(1)的列表)= O(n)。我要确保的是 不管怎样,这个分析是正确的。

    不,这是递归堆栈的图片。您可以设置断点并调试以查看实际发生的情况。

    call           result size(when entered)    result size(when function returns)
    helper(n)        0                               x(n)
    helper(n - 1)    0                               x(n - 1)
    helper(n - 2)    0                               x(n - 2)
    .......
    ......
    helper(3)        0                               x2
    helper(2)        0                               x1
    helper(1)        0                               1
    

    这里x(i)是执行此块后的结果大小:

    int count = 1;
    for (int i = 0; i < smaller.size(); i++) {
        if (i + 1 > smaller.size() - 1 ||
                  !smaller.get(i + 1).equals(smaller.get(i))) {
             result.add(count);
             result.add(smaller.get(i));
             count = 1;
        } else {
             count++;
        }
    }
    

    您可以使用笔和纸或调试来查看递归引擎下真正发生的事情。

    【讨论】:

    • 非常感谢您的回答并给了我更好的解决方案!我对细节仍然有些困惑。当我们调用 help(1) 时,所有 help(2) 到 help(n) 都在递归堆栈中,但那时每个堆栈的空间复杂度都是 O(1),因为我们只创建了一个空 List,并且最大空间复杂度将是 (n-1) * O(1) + O(n)(这是 helper(1) 的列表)= O(n)。我想确保这个分析是正确的。
    • 另外一个让我困惑的地方是输入的大小是恒定的,但是空间和时间的复杂度随着输入值的增加而增加。可以说空间复杂度为 O(1) 吗?
    • 再次感谢您的回答!这是一个非常详细的答案。我想我现在明白你的意思了!
    猜你喜欢
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-13
    • 1970-01-01
    • 2017-01-25
    • 1970-01-01
    相关资源
    最近更新 更多