【问题标题】:R G B element array swapR G B 元素数组交换
【发布时间】:2018-08-18 01:47:50
【问题描述】:

我正在尝试创建这个 c++ 程序来执行下面的描述。我很确定问题出在递归中,但不确定如何解决它。我猜它只是不断迭代到无穷大和崩溃。我什至没有得到输出。我想我可以只比较前一个指针和当前指针,然后根据词典进行 3 件式临时交换。我会使用一个指针来遍历数组并在每次交换后递减它,然后以该 ptr 作为参数递归调用。没用,我在这里,请帮助我:)。如果有一个更简单的解决方案也可以,但更愿意了解我的代码哪里出错了。

#include <string>
#include <iostream>
using namespace std;

// Given an array of strictly the characters 'R', 'G', and
// 'B', segregate the values of the array so that all the
// Rs come first, the Gs come second, and the Bs come last.
// You can only swap elements of the array.

char* RGBorder(char* c_a)
{

    size_t sz = sizeof(c_a)/sizeof(*c_a);
    char* ptr_ca = c_a;
    char* prv_ptr = ptr_ca;
    ptr_ca++;
    char temp;

    while(*ptr_ca)
    {
        switch(*ptr_ca)
        {
            case 'R' :
                if( *prv_ptr < *ptr_ca ) {
                    temp = *prv_ptr; *prv_ptr = *ptr_ca; *ptr_ca = temp;
                } else if( *prv_ptr == *ptr_ca ) {
                    continue;
                } else { ptr_ca--; RGBorder(ptr_ca); }

            case 'G' :
                if( *prv_ptr < *ptr_ca ) {
                    temp = *prv_ptr; *prv_ptr = *ptr_ca; *ptr_ca = temp;
                } else if( *prv_ptr == *ptr_ca ) {
                    continue;
                } else { ptr_ca--; RGBorder(ptr_ca); }
            default:
                ptr_ca++;
                continue;
        }
        ptr_ca++;
        cout << *ptr_ca;
    }

    return c_a;
}

int main()
{
    char ca[] =  {'G', 'B', 'R', 'R', 'B', 'R', 'G'};
    char *oca =RGBorder(ca);
    char *pca = oca;
    while(*pca)
    {
        cout << *pca << endl;
        pca++;
    }
}

【问题讨论】:

  • size_t sz = sizeof(c_a)/sizeof(*c_a); -- 这并不像您认为的那样。顺便说一句,您的问题更广为人知的是Dutch National Flag 问题。
  • 如果有更简单的解决方案也可以使用, 'G' 总是在排序列表的中间某处。这个想法是将'R'移动/交换到'G'的前面和G之后的B's。网络上的大量示例展示了如何通过简单的线性搜索/交换来做到这一点.

标签: c++ arrays compare-and-swap


【解决方案1】:

你的代码有很多问题。

1) 您使用字符指针调用函数RGBorder,然后尝试使用以下方法获取字符数:

size_t sz = sizeof(c_a)/sizeof(*c_a);

这不会为您提供字符数。相反,这只会让你得到 ​​p>

sizeof(char *) / sizeof(char)

通常为 4 或 8。使用 char 数组调用函数的唯一方法是提供一个以 null 结尾的数组(因此您可以使用 strlen),或者您必须在数组作为单独的参数:

char *RGBorder(char *c_a, int size)

2) 我没有仔细阅读您的代码,但是有更简单的方法可以在数组中进行 3 路分区。一种流行的算法是基于Dutch National Flag 问题的算法。

由于您希望数组按RGB 顺序排列,因此您知道G 的系列将始终位于序列的中间(某处),R 在序列的左侧,@987654333 @ 总是在序列的右边。

所以目标是简单地将R 交换到中间的左侧,将B 交换到中间的右侧。所以基本上你需要一个循环,在需要时逐渐改变“中间”,同时在检测到 R 和 B 时将它们交换到适当的位置。

以下代码说明了这一点:

#include <algorithm>

char *RGBorder(char *c_a, int num)
{
    int middle = 0;  // assume we only want the middle element
    int low = 0;     // before the G's
    int high = num - 1;  // after the G's

    while (middle <= high)
    {
        if ( c_a[middle] == 'R' )  // if we see an 'R' in the middle, it needs to go before the middle
        {
            std::swap(c_a[middle], c_a[low]);  // swap it to a place before middle
            ++middle;  // middle has creeped up one spot
            ++low;     // so has the point where we will swap when we do this again
        }
        else
        if (c_a[middle] == 'B')  // if we see a 'B' as the middle element, it needs to go after the middle
        {
            std::swap(c_a[middle], c_a[high]); // place it as far back as you can
            --high;  // decrease the back position for next swap that comes here
        }
        else
           ++middle;  // it is a 'G', do nothing
    }
    return c_a;
}

Live Example


这是另一个使用std::partition 的解决方案。

#include <algorithm>
#include <iostream>

char *RGBorder(char *c_a, int num)
{
    auto iter = std::partition(c_a, c_a + num, [](char ch) {return ch == 'R';});
    std::partition(iter, c_a + num, [](char ch) {return ch == 'G';});
    return c_a;
}

Live Example

基本上,第一次调用std::partition 会将R 放在数组的前面。由于std::partition 将迭代器(在本例中为char *)返回到分区发生的末尾,因此我们在第二次调用std::partition 时将其用作起始位置,在此我们对G 值进行分区.

请注意,std::partition 也通过交换来实现其目标。


鉴于此解决方案,我们可以通过使用循环将其推广到 n 路分区。假设我们想按 RGBA 顺序放置东西(4 个值而不是 3 个)。

#include <algorithm>
#include <iostream>
#include <cstring>

char *RGBorder(char *c_a, int num, char *order, int num2)
{
    auto iter = c_a;
    for (int i = 0; i < num2 - 1; ++i)
        iter = std::partition(iter, c_a + num, [&](char ch) {return ch == order[i];});
    return c_a;
}


int main()
{
    char ca[] = "AGBRRBARGGARRBGAGRARAA";
    std::cout << RGBorder(ca, strlen(ca), "RGBA", 4);
}

输出:

RRRRRRRGGGGGBBBAAAAAAA

【讨论】:

  • 谢谢,我会进一步研究这个例子。
【解决方案2】:

很抱歉直言不讳,但该代码是一团糟。我不是指错误,对于初学者来说,这些是可以原谅的。我是说格式。一行中的多个语句使阅读和调试代码变得非常困难。没有直接内在含义的短变量名称使得很难理解代码应该做什么。 using namespace std; 也是非常糟糕的做法,但我可以想象你是由开设该课程的人教你这样做的。

第一个问题

您的cases 没有break,因此您为R 执行所有案例,并为G 执行Gdefault。此外,您的代码将永远不会到达循环的最后 2 行,因为您在每种情况下都在continue 之前退出。

第二个问题

你有一个无限循环。在cases 中,您有两种情况最终会陷入无限循环:

  1. else if( *prv_ptr == *ptr_ca ) 分支中,您只需continue; 而无需更改指针。

  2. else 分支中,您执行ptr_ca--;,但随后在default 中,您再次调用ptr_ca++;
    (请注意,即使使用breaks,您仍会调用ptr_ca++;在循环结束时。)

在这两种情况下,指针都不会改变,所以一旦你进入任何这些条件,你的循环就永远不会退出。

可能的第三个问题

我只能猜测,因为从名称上看不出来,但似乎prv_ptr 应该保存循环中的最后一个指针?如果是这样,那么您永远不更新该指针似乎是错误的。无论哪种方式,正确的变量名称都可以更清楚地说明该指针的确切用途。 (附带说明,const 的一致使用有助于识别此类问题。如果您有一个不是 const 但从未更新的变量,您要么忘记添加 const,要么忘记更新它。)

如何解决

格式化您的代码:

  • 不要使用using namespace std;
  • 每行一个语句。
  • 为您的变量提供正确的名称,以便轻松识别什么是什么。 (这不是 1993 年,真的,我宁愿拥有 thisIsThePointerHoldingTheCharacterThatDoesTheThing 而不是 ptr_xy。)

修复上述问题(添加breaks,确保您的循环实际退出)。

然后调试您的代码。带调试器。当它运行时。使用断点并逐行执行,在代码执行时检查指针的值。花哨的东西。

祝你好运!

【讨论】:

  • 谢谢,感谢您的反馈。 :)
【解决方案3】:

只需计算“R”、“G”和“B”字母的数量,然后从头开始填充数组。 更容易,没有递归。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-16
    • 2018-01-10
    • 2020-01-24
    • 2010-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多