递归是否总能提高性能?

Does recursion always improves performance?

这里我有两个解决方案来判断一个srting是否是回文。第一个使用递归,第二个使用 for 循环。我有点困惑我的递归代码与没有递归的代码的执行方式。递归 运行 的代码是否在 O(n) 时间内?如果可以,怎么做?

//Solution using recursion
function isPalindrome(arr) {
    //Runs on first call only
    if (typeof arr === "string"){
        //remove whitespace
        if(arr.match(/\s/)) {
            arr = arr.replace(/\s/g, "");
        }
        //convert to array
        arr = arr.split("");
    }

    //base condition
    if(arr.length === 0 || arr.length === 1) {
        //console.log(true);
        return true;
    } else {
        if (arr[0] !== arr[arr.length - 1]) {
            //console.log(false);
            return false;
        } else {
            arr.shift(); //remove first element
            arr.pop(); //remove last element

            //recursive call
            isPalindrome(arr);
        }
    }
}

//Solution without using recursion
function palindrome(str) {
    var reverseString = [];
    //remove whitespace
    if(str.match(/\s/)) {
        str = str.replace(/\s/g, "");
    }
    //convert to array
    var arr = str.split("");

    for(var i = arr.length; i > 0; i--) {
        reverseString.push(arr.pop());
    }

    if (reverseString.join("") === str) {
        return true;
    } else {
        return false;
    }

}

console.log(palindrome("racecar"));
console.log(palindrome("si racecar is"));

正在回答您的标题问题:

不,递归不会提高性能。它很可能总是使实现变慢(与基于相同循环的对应物相比)

关于复杂度:

您的递归解决方案可能是 O(n^2),因为 arr.shift() 操作可能是线性的。

从 V8 实现开始:array.shift 操作 线性的。参见 https://github.com/v8/v8/blob/master/src/array.js#L596 and https://github.com/v8/v8/blob/master/src/array.js#L313

替代实施:

function isPalindromeZ(s) {
    for (var i = 0, len = s.length; i < len / 2; ++i) {
        if (s[i] != s[len - i - 1]) {
            return false;
        }
    }

    return true;
}

与您的实现相比,此实现的优点在于它 O(1) 额外的内存消耗。

不,递归本身不会提高性能,相反。递归调用增加了一些开销,因此递归解决方案通常比迭代解决方案慢一些,如果存在一个简单的解决方案(如示例中所示)。

递归可用于简化某些自然嵌套的任务,这些任务需要迭代解决复杂的代码。这就是递归可以提高性能的情况,通过消除如此多的复杂性,它远远超过它增加的小开销。

您问题中的示例不是如何使用递归的好示例。可以用来演示递归如何工作,但不能演示递归如何用好。

递归版本没有 O(n) 复杂度,更接近于 O(n²) 复杂度。 arr.shift() 调用将移动数组中的所有项目,这意味着每次迭代都有一个运行剩余数组长度的内部循环。

此外,递归版本中存在错误;最后一行应该是:

return isPalindrome(arr);