【问题标题】:Why would non-recursion method takes more time than recursion?为什么非递归方法比递归需要更多时间?
【发布时间】:2018-05-31 09:21:06
【问题描述】:

以快速排序为例,下面列出了递归和非递归的方法。

我认为这两种方法实际上实现了相同的算法,因为stack用于模拟非递归方法中的递归过程。

但是,在 85% 的测试用例通过后,我使用递归方法获得了 AC,但 Time Limit Exceeded 使用非递归方法。

那么,我的非递归方法有什么问题,或者两种方法之间存在时间复杂度差异?

谢谢!

// non-recursion
void sortIntegers(vector<int> &A) {
    if (A.empty()) {
        return;
    }
    stack<pair<int, int>> ranges;
    ranges.push(pair<int, int>(0, A.size() - 1));
    while (!ranges.empty()) {
        pair<int, int> r = ranges.top();
        ranges.pop();
        int mid = A[r.second],
            left = r.first,
            right = r.second - 1;
        if (r.first >= r.second) {
            continue;
        }
        while (left < right) {
            while (A[left] < mid && left < right) {
                left++;
            }
            while (A[right] >= mid && left < right) {
                right--;
            }
            swap(A[left], A[right]);
        }
        if (A[left] < A[r.second]) {
            left++;
        } else {
            swap(A[left], A[r.second]);
        }
        ranges.push(pair<int, int>(0, left - 1));
        ranges.push(pair<int, int>(left + 1, r.second));
    }

// recursion
void sortIntegers(vector<int> &A) {
    quick(A, 0, A.size() - 1);
}
void quick(vector<int> & A, int start, int end) {
    if (start >= end) {
        return;
    }
    int mid = A[end],  //5
        left = start,  // 0
        right = end - 1;  //3
    while (left < right) {
        while (A[left] < mid && left < right) {
            left++;
        }
        while (A[right] >= mid && left < right) {
            right--;
        }
        swap(A[left], A[right]);
    }
    if (A[left] >= A[end]) {
        swap(A[left], A[end]);
    }else {
        left++;
    }
    quick(A, start, left - 1);
    quick(A, left + 1, end);
}

【问题讨论】:

  • 函数调用被大量使用,因此很可能被编译器优化得很好。例如,参数可能会在寄存器中传递,而不是像显式堆栈那样存储到内存中。
  • 您的sortIntegers 函数在此行访问越界项目:int mid = A[r.second]。请see this version of your function using vector::at()。因此,如果您的函数引入了未定义的行为,您将无法确定某事是快还是慢。先修复你的非递归函数,然后重新运行你的结果。

标签: c++ algorithm sorting recursion quicksort


【解决方案1】:

乍一看,循环的底部有:

    ranges.push(pair<int, int>(0, left - 1));
    ranges.push(pair<int, int>(left + 1, r.second));

对我来说应该是

    ranges.push(pair<int, int>(r.first, left - 1));
    ranges.push(pair<int, int>(left + 1, r.second));

【讨论】:

  • 那么,你的解释是什么为什么应该是这样的?
  • @Algirdas Preidžius 这是快速排序。通过快速排序,您可以迭代地处理原始数组中越来越小的区域。原样的代码意味着所有区域中有一半从零开始。错了,这不是快速排序的工作方式。使用快速排序,你有一个从 a 到 b 的区域,你取一个中点,然后你用 a 到中点和中点到 b 工作。在第二次迭代中,2 个区域中的 1 个以 0 开头。在下一次迭代中,4 个区域中的 1 个以 0 开头。在下一次迭代中,8 个区域中的 1 个以 0 开头。
  • @SeanF 你错过了我评论的重点。目前的答案看起来像“我更喜欢这种代码,而不是那个”,没有任何解释为什么一个比另一个更好。这样的答案从长远来看是没有用的。
  • @Algirdas Preidžius 你没抓住重点。快速排序是一种成熟的算法。这不是偏好问题,而是他写的代码是对还是错的问题。这不是一段代码更好的情况。这是他的代码不正确的情况。向人们解释快速排序不是我的工作,这远远超出了期望答案来解释计算机科学概念的合理性。他的问题是他想写快速排序,而他的代码有问题。我的回答是指出他的代码有什么问题,以便快速排序。
  • @SeanF 你又一次错过了重点;你应该停止把你的话放在别人的嘴里:1)我没有说这是一个偏好问题。事实上,your 语句“To me that should be”暗示它是。我已经说过了。 2)我在哪里说过你应该解释什么是快速排序?我说你应该解释为什么一个代码 sn-p 有效,而不是另一个。 IE。错误在哪里。从长远来看,纯代码的答案没有用处。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
  • 2021-01-02
  • 2015-01-13
  • 2013-11-10
  • 2018-07-13
相关资源
最近更新 更多