【问题标题】:Check if a vector is ordered using divide et impera algorithm检查向量是否使用 divide et impera 算法排序
【发布时间】:2020-03-27 04:26:37
【问题描述】:

我正在尝试检查是否使用分治算法对向量进行排序,这是我到目前为止编写的代码:

#include <iostream>
#include <vector>

using namespace std;

bool isOrdered(std::vector <int> v, int left, int right)
{
int mid = (left + right)/2;

if (left == right){
    if (left == 0)
        return true;
    else
        return v[left-1] <= v[left];
}
else if (left + 1 == right)
{
    if (v[left] <= v[right])
        return true;
    else
        return false;
}
else if (left > right)
{
    if (v[left] > v[right])
        return true;
    else
        return false;
}
else
{
    return isOrdered(v, left, mid) && isOrdered(v, mid+1, right);
}
}

int main()
{
std::vector <int> v = {2, 2, 3, 2, 2};
cout << isOrdered(v, 0, v.size() - 1);
return 0;
}

它似乎不适用于某些情况,并且在调试时我不断添加特定的基本情况以使其适用于一个输入,但这不会使它适用于另一个输入,我一直这样做,直到我意识到我有算法错误。我基本上是这样想的:将向量划分为子向量,如果所有子向量都是有序的,那么整个向量都是有序的。然而,这种方法很快就失效了。如果输入长度不是 2 的幂,那么它最终会将其分解为某些长度为 1 的子向量,这些子向量始终是有序的。例如,如果输入是2 2 3 2 2 怎么办?子向量是 {2, 2}, {3} 和 {2, 2},它们都是有序的,但整个向量不是。

那么我应该如何看待这个问题呢?我试图通过添加 return v[left-1] &lt;= v[left]; 行使其适用于长度为 1 的子向量,但它仍然崩溃。

【问题讨论】:

  • 一旦有 2 个组,您需要停止,以便所有组重叠。那将是{2,2,3,2,2} 应该递归到组{2,2}, {2,3}, {3,2}, {2,2}
  • 顺便说一句,通过 const 引用而不是复制传递你的向量。
  • @NathanOliver-ReinstateMonica 我也想到了这一点.. 不确定它是否算作分而治之,但无论哪种方式,我什至如何递归地进行呢?
  • @Lastrevio2 在right - left == 1 时停止递归。此外,(left + right)/2 有可能溢出。要解决这个问题,请参阅:stackoverflow.com/questions/24317360/…
  • @NathanOliver-ReinstateMonica:仅当我们使用 signed 索引时才会出现潜在问题 ;-)(在有符号/无符号索引之间引发战争 ^_^)

标签: c++ algorithm recursion vector divide-and-conquer


【解决方案1】:

从递归开始:

如果两个子范围都是有序的,则范围是有序的并且如果“低”范围的最后一项低于“高”范围的第一项:

return isOrdered(v, left, mid - 1) && isOrdered(v, mid, right) && v[mid - 1] <= v[mid];

保持停止条件:当范围为空(不能从参数发生)或只有一个元素时。 这些是有序范围。

所以我们得到了:

bool isOrdered(const std::vector<int>& v, std::size_t left, std::size_t right)
{
    if (left == right) { // Only one element
        return true;
    } else {
        const auto mid = (left + right + 1) / 2;
        return v[mid - 1] <= v[mid]
            && isOrdered(v, left, mid - 1)
            && isOrdered(v, mid, right);
    }
}

【讨论】:

  • "如果两个子范围都被排序并且如果“低”范围的最后一项低于“高”范围:“啊,我明白了!如果两者都是有序的,那么最高的元素就是最后一个,最低的元素是第一个,所以如果低范围的最后一项低于高范围的第一项,那么整个东西都是有序的。现在开始有意义了。
  • 另外,我觉得有趣的是你写了 std::size_t。我知道它是 C++ 独有的,并且不是 C 语言,但我不知道你必须为它编写 std:: (我使用 C:B 这让你不必编写 std::没有说````使用命名空间std;```,就像我可以在不使用命名空间std的代码块上说字符串而不是std :: string),所以我不知道size_t是否也是这种情况,所以我假设在更普通的编译器/编辑器中,您必须将 std:: 放在 size_t 前面才能正常工作? (或使用命名空间标准)
  • @Lastrevio2 因为 C++ 有命名空间,所以 C++ 将 size_t 放在 std 命名空间内。要实现 100% 便携,您需要 std::size_t 或至少 using std::size_t;。很多时候你可以不使用它,因为 C 定义也被引入了,但为什么要试探命运呢?
  • @Lastrevio2: size_t(和其他一些类型/类型定义)也可以来自 C-header(没有 namespace std)。 std::string 不是其中之一。 (在某些情况下,ADL 也允许在函数调用中省略 std::)。
【解决方案2】:

你为什么还要使用这种“难”的算法?为了检查一个向量是否有序,每个成员 (v[i]) 不能大于下一个 (v[i+1])。

“分而治之”算法更有用,例如,在已经有序的向量中查找某些东西,但对于检查向量是否有序,简单的线性算法要好得多(因为可读性)。

【讨论】:

  • 这不是答案。
  • 这是一个家庭作业......当然我不会将它用于实际项目,但我正在尝试学习分而治之,如果不是我将如何学习付诸实践?
  • @Lastrevio2:作为家庭作业或学习经验,没关系。我只是说不要在现实生活中使用它(例如在工作中,在编程工作中)。
【解决方案3】:
#include <iostream>
#include <vector>

using namespace std;

bool isOrdered(const vector <int> &v, int left, int right) {
    int mid = (left + right)/2;
    if (left == right)
        return true;
    else
        return v[mid]<=v[mid+1] && isOrdered(v, left, mid) && isOrdered(v, mid+1, right);
}

int main()
{
    vector <int> v = {2, 2, 3, 2, 2};
    cout << isOrdered(v, 0, v.size() - 1);
    return 0;
}

【讨论】:

  • 虽然这段代码可以解决问题,但如果解释一下它的作用方式/原因,答案会好得多。请记住,您的答案不仅适用于提出问题的用户,还适用于所有其他找到它的人。另请注意,int mid = (left + right)/2; 是不安全的,因为它可能会溢出。
  • 这是从@Jarod42 派生的实现
  • 您仍然应该记录它的工作方式/原因。评论是“临时的”,可以随时删除。您的答案应该站稳脚跟,而不是依赖任何外部信息。
猜你喜欢
  • 2013-03-17
  • 2016-04-10
  • 2020-01-16
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-04
  • 2019-03-11
相关资源
最近更新 更多