【问题标题】:Why is my 2d array not rotating?为什么我的二维数组不旋转?
【发布时间】:2018-03-20 22:37:15
【问题描述】:

我有一个平方二维数组,我想顺时针和逆时针旋转。

我在这里按照这个答案向右旋转:

Rotating a 2D pixel array by 90 degrees

我开发的代码:

void rotateRight()
{  
    for (int i = 0; i < m_width; i += 1) {
        for (int j = i + 1; j < m_height; j += 1) {
            std::swap(get(i, j), get(j, i));  
        }
    }
}

但是,阵列没有旋转。我有一个带有 5 的 10x10 数组,左上角是一个 7。我希望 7 在旋转后会转到右上角,但它仍然在左上角。

成员函数get()是我自己的,它只是返回一个单元格的引用。

T& get(const int x, const int y)
{ 
    return m_array[y * m_width + x];
} 

我怎样才能让它旋转?是否必须制作一个新数组?将不胜感激。谢谢!

更新:

最新尝试。好的,所以'7'最终从左上角向右旋转到右上角。但是当再次调用 rotateRight() 时,它会失败。我在右上角的 7 不见了,再也找不到了。调查一下。

for (int i = 0; i < m_width; i += 1) {
    for (int j = i + 1; j < m_height; j += 1) {
        get(j, i) = get(i, j);
    }
}
for (int i = 0; i < m_height; i++) {
    for (int j = 0, k = m_height - 1; j<k; j++, k--) {
        std::swap(get(j, i), get(k, i));
    }
}

输出:

Original:
700
000
000

Rotation #1:
J: 1, I: 0
J: 2, I: 0
J: 2, I: 1
J: 0, I: 0, K: 2
Value: 7 J: 0 K: 2
J: 0, I: 1, K: 2
Value: 0 J: 0 K: 2
J: 0, I: 2, K: 2
Value: 0 J: 0 K: 2
007
000
000

Rotation #2:
J: 1, I: 0
J: 2, I: 0
J: 2, I: 1
J: 0, I: 0, K: 2
Value: 0 J: 0 K: 2
J: 0, I: 1, K: 2
Value: 0 J: 0 K: 2
J: 0, I: 2, K: 2
Value: 0 J: 0 K: 2
000
000
000

最终代码

// Rotates a squared array clockwise.
void rotateRight()
{         
    T temp; 
    int halfwidth_floor = m_width / 2;
    int halfwidth_ceil = (m_width + 1) / 2; 

    for (int j = 0; j < halfwidth_floor; j += 1) {
        for (int i = 0; i < halfwidth_ceil; i += 1) {
            std::swap(temp, get(i, j)); 
            std::swap(get(i, j), get(j, m_width - i - 1));
            std::swap(get(j, m_width - i - 1), get(m_width - i - 1, m_width - j - 1));
            std::swap(get(m_width - i - 1, m_width - j - 1), get(m_width - j - 1, i));
            std::swap(get(m_width - j - 1, i), temp);
        }
    } 
}   

// Rotates a squared array counter-clockwise.
void rotateLeft()
{
    T temp; 
    int n = m_width;  

    for (int i = 0;  i < n / 2; i++) {
        for (int j = i; j < n - i - 1; j++) {
            std::swap(temp, get(i, j));  
            std::swap(get(i, j), get(n - j - 1, i));
            std::swap(get(n - j - 1, i), get(n - i - 1, n - j - 1));
            std::swap(get(n - i - 1, n - j - 1), get(j, n - i - 1));
            std::swap(get(j, n - i - 1), temp);
        }
    }
} 

【问题讨论】:

  • 一个简短的说明,说正确地做一个 2D 数组做得很好,具有 1D 存储和索引包装器!
  • 你在做什么(交换ij)相当于反射数组围绕它的前导对角线,而不是旋转它。另外,如果widthheight 不相等,您期望的行为是什么? (必须“缩小”阵列或将其部分切除)
  • 附带说明:有一个std::rotate 函数。
  • @Ron 我不认为该功能意义上的旋转是 OP 想要的
  • 我只对平方旋转感兴趣。

标签: c++


【解决方案1】:

内部循环的每次迭代都将 (i, j) 与 (j, i) 交换。

如果 (i == j),那么它什么也不做。

如果 (i != j),则该交换将进行两次 - 例如,当 i == 3 和 j == 4 完成的交换将在 i == 4 和 j == 3 时再次完成。进行两次交换当然不会导致总体上没有任何事情发生。

这就是为什么您的代码将以与其开始时完全相同的数组结束。

您可能想要做的是写入与您开始使用的数组不同的 数组。

编辑:没关系,我错过了问题的int j = i + 1 部分,并认为两个循环都从 0 迭代到宽度或高度。 (不过,对于任何想要做同样事情的人来说,以上内容可能仍然是一个有用的见解。)

您的代码将转置数组,而不是旋转数组。 (转置意味着从左上角到右下角沿对角线反射。)要旋转,您需要将每个元素 (i, j) 移动到 (j, w-i-1) 或 (h-j-1, i)。 (当然,要旋转 90 度,您的数组必须是正方形,即 (w == h))。

编辑 2:

如果你想将一个方形数组原地旋转 90 度,std::swap 将不起作用(因为它交换了 2 个元素),原始赋值也不起作用(因为它会丢失信息)。但是,您可以做的是遍历数组的四分之一并旋转 4 个元素的序列,如下所示:

void rotateRight()
{
    int halfwidth_floor = m_width / 2;
    int halfwidth_ceil = (m_width + 1) / 2;
    for (int j = 0; j < halfwidth_floor; j += 1) {
        for (int i = 0; i < halfwidth_ceil; i += 1) {
            value_type temp = get(i, j);
            get(i, j) = get(j, m_width-i-1);
            get(j, m_width-i-1) = get(m_width-i-1, m_width-j-1);
            get(m_width-i-1, m_width-j-1) = get(m_width-j-1, i);
            get(m_width-j-1, i) = temp;
        }
    }
}

value_type 是数组元素的类型(m_width == m_height)。

在第一次迭代中,这会将左下角移动到左上角,然后将右下角移动到左下角,然后将右上角移动到右下角,然后将(临时保存的)左上角移动到右上角。随后的迭代将对其他点做同样的事情,越来越接近中心。请注意,一个 for 循环迭代到向下舍入的一半大小,而另一个向上舍入:这样在一个奇数大小(例如 5x5)的数组中,它将查看一个 2x3 的矩形及其“旋转”正方形,以避免将中间元素旋转两次(或根本不旋转)。

【讨论】:

  • m_size 是 m_width * m_height。将 size 放入 get(w,h) 会导致异常。我应该只使用宽度或高度的正方形之一吗?
  • 对不起,我的错误。您正在处理一个方形数组,所以当我写 m_size 时,我实际上是指 m_width(或等效的 m_height)。我已经编辑了我的答案。
  • 首先,这很好用,但是按位复制到 temp 对我来说很麻烦。这似乎与我上面发布的问题相似,代码逻辑相似。临时变量会发生什么情况,它会进行按位复制,这可能很昂贵。 Get() 正在返回一个引用,但 temp 没有分配给一个指针。我希望使用指针来避免繁重的按位复制。有没有办法处理温度?如果我现在尝试使用指针,它不会旋转并且输出全为 0。我知道为什么,但我正试图找到一个替代方案,这样它就可以避免按位复制。我也有 getWithPointer(x, y)。
  • 根据我在网上看到的其他示例,按位复制到 temp 似乎是标准的。但这让我朝着正确的方向前进。感谢您抽出宝贵的时间,感谢所有人!
  • 是的——完全正确。
【解决方案2】:

你在做什么 转置矩阵。

你应该做什么 旋转矩阵的算法包括 2 个步骤

  1. 转置矩阵(如果向左旋转)。如果向右旋转,则转置和反转列的顺序)
  2. 反转列

如下图所示

    1 2 3        1 4 7      3 6 9
    4 5 6    =>  2 5 8  =>  2 5 8
    7 8 9        3 6 9      1 4 7

代码 像这样,(初始化一个将保存旋转数组的辅助数组,因为形状可能不是正方形)

void rotate()
{  
    for (int i = 0; i < m_width; i += 1) {
        for (int j = i + 1; j < m_height; j += 1) {
            aux_array[j][i] = get(i, j)
        }
    }
    for (int i=0; i < m_height; i++) {
        for (int j=0,  int k=m_height-1; j<k; j++,k--) {
            swap(aux_array[j][i], aux_array[k][i]);
        }
    }
}

【讨论】:

  • 我尝试了这个并且得到了一些有趣的结果。发布在上面。
  • 您可以尝试使用较小的数组并添加一些调试打印,这些打印打印到 7 被移动的索引吗?
  • 张贴在我可以使用 3x3 数组的上方。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-02
  • 2022-01-07
相关资源
最近更新 更多