通过递归向前和向后设置数组min

时间:2016-08-05 15:56:41

标签: c++ arrays algorithm recursion

假设我有一个数组 A = {a1,a2,...,an}

假设A被划分为大小 b

的块

我要做的是:

MinUntilHere MinStartingHere 为2个大小 n 的数组,其中:

  • MinUntilHere [i] =从我的阻止开始直到我
  • 的最小值
  • MinStartingHere [i] =从i到最小阻止
  • 的最小值

我想在数组的单个横向中填充这2个数组...我正在尝试以下伪代码:

int fill(int pos = 0){

    if(pos >= n) return INF

    if(pos is the start index of a block){ //i % b == 0
        MinUntilHere[pos] = A[pos]
        MinStartingHere[pos] = min(A[pos], fill(i + 1))
    }
    else{
        MinUntilHere[pos] = min(A[pos], A[pos - 1])
        MinStartingHere[pos] = min(A[pos], fill(i + 1))
        return MinStartingHere[pos]
    }
}

但正如您所看到的,它并不完整,因为它无法检测每个块何时结束并且缺少某些返回。我怎样才能创建一个只通过数组一次的函数并计算我的答案?

Example:

A = {2,1,3,6,5,4} b = 2 ,数组必须像这样填充:

MinUntilHere =    {2,1,3,3,5,4}

MinStartingHere = {1,1,3,6,4,4}

MinUntilHere [0] = 2从0的块开始直到索引0,最小值为2.

MinUntilHere [1] = 1从1的块开始直到索引1,最小值为min(1,2)= 1

End of Example

3 个答案:

答案 0 :(得分:1)

您可以使用以下作业完成2次传递:

template <typename InputIt, typename OutputIt>
void ComputeMinUntilHere(InputIt begin, InputIt end, OutputIt output)
{
    if (begin == end) {
        return;
    }
    auto min = *begin;
    while (begin != end) {
        min = std::min(min, *begin);
        *output = min;
        ++begin;
        ++output;
    }
}

struct MinByBlock
{
    std::vector<int> minUntilHere;
    std::vector<int> minStartingHere;
};

MinByBlock ComputeMinByBlock(const std::vector<int>&v, std::size_t blockSize)
{
    MinByBlock res;
    res.minUntilHere.resize(v.size());
    res.minStartingHere.resize(v.size());
    for (std::size_t i = 0; i < v.size(); i += blockSize) {
        const auto blockBegin = v.begin() + i;
        const auto blockEndIndex = std::min(i + blockSize, v.size());
        const auto blockEnd = v.begin() + blockEndIndex;

        ComputeMinUntilHere(blockBegin, blockEnd, res.minUntilHere.begin() + i);
        ComputeMinUntilHere(std::make_reverse_iterator(blockEnd),
                            std::make_reverse_iterator(blockBegin),
                            std::make_reverse_iterator(res.minStartingHere.begin() + blockEndIndex));
    }
    return res;
}

Demo

您可能会注意到,minUntilHereminUntilHere相似,只是依赖于您从左到右或从右到左进行迭代。

ComputeMinUntilHere完成整个范围的工作。

ComputeMinByBlock通过独立块分割矢量以按块填充结果。

答案 1 :(得分:0)

在一般情况下,您不能希望在一次通过中填充数组。

例如,如果块大小为N(整个数组),那么要填写条目MinUntilHere[i],您需要查看i之前的所有项目并填写条目MinStartingHere[i]您需要查看i之后的所有项目。

答案 2 :(得分:0)

很抱歉,但是我能够找到一个线性解决方案(仅运行一次数组),代码要小得多。这是解决方案:

int fill(int pos = 0){

    if(pos >= |A|) return INF;

    int mod = pos % b;

    if(mod == 0){
        MinUntilHere[pos] = A[pos];
        MinStartingHere[pos] = min(A[pos], fill(pos + 1));
        return fill(pos + bsize);
    }
    else if(mod == bsize - 1){
        MinUntilHere[pos] = min(MinUntilHere[pos-1], A[pos]);
        return MinStartingHere[pos] = A[pos];
    }
    else{
        MinUntilHere[pos] = min(MinUntilHere[pos-1], A[pos]);
        return MinStartingHere[pos] = min(A[pos], fill(pos + 1));
    }

}

它的工作原理如下:

  • 如果我正在启动块,请将MinUntilHere设置为A [pos]元素,并将MinStartingHere [pos]设为A [pos]的最小值和fill的返回值(pos + a);
  • 如果它不是块的开始或结束,只需使MinUntilHere [pos] = min(A [pos],MinUntilHere [pos-1])并且对于MinStartingHere,将其设置为A [pos]和之间的最小值。填充(pos + 1)并返回(前一次递归)
  • 如果它是块结束,请对MinUntilHere执行相同的操作,对于MinStartingHere,只需将其设置为A [pos]并将其返回到先前的递归。

一个重要的细节是,当你启动块时,你会调用很多递归,然后使用MinStartingHere的值返回到该块的开头,并且在调用pos + b的方法之后,这意味着它将在完成当前块后开始计算新块。

感谢您的帮助:)