【重构】人人都来写算法 之 矩阵顺时针旋转90度,空间效率O(1),时间效率O(n*n)

本文分享了作者如何通过重构代码来减少重复并提高代码可读性的过程。针对不同大小的矩阵进行了四种不同的90度旋转算法实现,并进行了验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中午抽了15分钟空闲时间对之前的一段代码进行了重构,虽然使用指针操作二维数组在可读性上没有原来的二维下标访问清晰,但是为了消除重复也只能这样了。

原文链接:

http://blog.youkuaiyun.com/walle_love_eva/article/details/9951977


之前的代码之所以存在重复代码的坏味道,主要是我希望测试奇数方阵和偶数方阵两种情况,最初函数是传入二维数组的下标引用,但是第二维下标必须明确,因此导致重复代码。

但是,我们假设,我们是在一个固定大小图像上截取一个奇数方阵或者偶数方阵来进行测试,因此也就可以解决这个问题。

此外需要特别说明的一点是,正如前一篇文章所言,两种不同的方法都可以解决,同时两种方法我又进行了细微的变通,因此四个不同的算法函数可以实现这一转换。因此我分别调用这四个函数,让一个矩阵进行4四顺时针90度的转换,又变回原图像,以此对算法进行验证。只要其中任何一个算法出现问题,4次转换后矩阵就和原来的不同。

第一次重构后的代码:

#include <iostream>
#include <iomanip>

using std::cout;
using std::endl;
using std::setw;

const int SIZE_4 = 4;
const int SIZE_7 = 7;

void printImage(int* matrix, int size)
{
    for(int i=0; i<size; ++i)
    {
        for(int j=0; j<size; ++j)
        {
            cout<<setw(4)<<matrix[i*size + j]<<" ";
        }
        cout<<endl;
    }
    cout<<"-----------------------------"<<endl;
}

void swap(int &a, int &b)
{
    if(&a == &b)
    {
        return;
    }
    int temp = a;
    a = b;
    b = temp;
}

void swap_bitOpt(int &a, int &b)
{
    if(&a == &b)
    {
        return;
    }
    a = a^b;
    b = a^b;
    a = a^b;
}

void transpose90_zero(int* matrix, int size)
{
    //Diagonal Flip
    for(int i=0; i<size; ++i)
    {
        for(int j=i+1; j< size; ++j)
        {
            swap_bitOpt(matrix[i*size + j], matrix[j*size + i]);
        }
    }

    //Flip form left to right
    for(int i=0; i<size; ++i)
    {
        for(int j=0; j<size/2; j++)
        {
            swap(matrix[i*size +j], matrix[i*size +(size-j-1)]);
        }
    } 
}

void transpose90_one(int* matrix, int size)
{
    //Flip up and down
    for(int i=0; i<size/2; ++i)
    {
        for(int j=0; j<size; j++)
        {
            swap(matrix[i*size +j], matrix[(size-i-1)*size +j]);
        }
    }
    
    //Diagonal Flip
    for(int i=0; i<size; ++i)
    {
        for(int j=i+1; j< size; ++j)
        {
            swap(matrix[i*size + j], matrix[j*size + i]);
        }
    }
}

void transpose90_two(int* matrix, int size)
{
    for(int layer=0; layer<size/2; ++layer)
    {
        int first = layer;
        int last = size-1-layer;

        for(int i=first; i<last; ++i)
        {
            int offset = i - first;
            int top = matrix[first*size + i];
            matrix[first*size + i] = matrix[(last-offset)*size + first];
            matrix[(last-offset)*size + first] = matrix[last*size +(last-offset)];
            matrix[last*size + (last-offset)] = matrix[i*size + last];
            matrix[i*size + last] = top;
        }
    }
}

void transpose90_three(int* matrix, int size)
{
    for(int layer=0; layer<size/2; ++layer)
    {
        int last = size-layer -1;
        int iter = 0;
        for(int i=layer; i<last; ++i)
        {
            int reverse_i = last - iter;
            int top = matrix[layer*size + i];
            matrix[layer*size + i] = matrix[reverse_i*size + layer];
            matrix[reverse_i*size + layer] = matrix[last*size + reverse_i];
            matrix[last*size + reverse_i] = matrix[i*size + last];
            matrix[i*size + last] = top;
            iter++;
        }
    }
}


int main()
{
    int image[SIZE_7][SIZE_7] = {
        {1,2,3,4,5,6,7}, 
        {8,9,10,11,12,13,14}, 
        {15,16,17,18,19,20,21}, 
        {22,23,24,25,26,27,28},
        {29,30,31,32,33,34,35},
        {36,37,38,39,40,41,42},
        {43,44,45,46,47,48,49}};

    //Test even matrix
    printImage((int*)image, 4);
    transpose90_zero((int*)image, 4);
    printImage((int*)image, 4);
    transpose90_one((int*)image, 4);
    printImage((int*)image, 4);
    transpose90_two((int*)image, 4);
    printImage((int*)image, 4);
    transpose90_three((int*)image, 4);
    printImage((int*)image, 4);
    
    //Test odd matrix
    printImage((int*)image, 7);
    transpose90_zero((int*)image, 7);
    printImage((int*)image, 7);
    transpose90_one((int*)image, 7);
    printImage((int*)image, 7);
    transpose90_two((int*)image, 7);
    printImage((int*)image, 7);
    transpose90_three((int*)image, 7);
    printImage((int*)image, 7);

    getchar();
}

现在再看这段代码,可读性比最初确实大打折扣,二维数组下标访问变成了一维数组下标访问。我觉得还是需要继续重构,或者改善代码的可读性。

在继续重构前,我再次明确下最初的想法:我希望的是旋转一副位图图像,假设位图图像大小是一定的,我仅仅需要在main函数中测试偶数矩阵和奇数矩阵的转换,那么我怎么传入更加合适呢?

第二次重构的代码如下:

#include <iostream>
#include <iomanip>

using std::cout;
using std::endl;
using std::setw;

const int SIZE_MATRIX = 100;

void printImage(int matrix[][SIZE_MATRIX], int size)
{
    for(int i=0; i<size; ++i)
    {
        for(int j=0; j<size; ++j)
        {
            cout<<setw(4)<<matrix[i][j]<<" ";
        }
        cout<<endl;
    }
    cout<<"-----------------------------"<<endl;
}

void swap(int &a, int &b)
{
    if(&a == &b)
    {
        return;
    }
    int temp = a;
    a = b;
    b = temp;
}

void transpose90_zero(int matrix[][SIZE_MATRIX], int size)
{
    //Diagonal Flip
    for(int i=0; i<size; ++i)
    {
        for(int j=i+1; j< size; ++j)
        {
            swap(matrix[i][j], matrix[j][i]);
        }
    }

    //Flip form left to right
    for(int i=0; i<size; ++i)
    {
        for(int j=0; j<size/2; j++)
        {
            swap(matrix[i][j], matrix[i][size-j-1]);
        }
    } 
}

void transpose90_one(int matrix[][SIZE_MATRIX], int size)
{
    //Flip up and down
    for(int i=0; i<size/2; ++i)
    {
        for(int j=0; j<size; j++)
        {
            swap(matrix[i][j], matrix[size-i-1][j]);
        }
    }
    
    //Diagonal Flip
    for(int i=0; i<size; ++i)
    {
        for(int j=i+1; j< size; ++j)
        {
            swap(matrix[i][j], matrix[j][i]);
        }
    }
}

void transpose90_two(int matrix[][SIZE_MATRIX], int size)
{
    for(int layer=0; layer<size/2; ++layer)
    {
        int first = layer;
        int last = size-1-layer;

        for(int i=first; i<last; ++i)
        {
            int offset = i - first;
            int top = matrix[first][i];
            matrix[first][i] = matrix[last-offset][first];
            matrix[last-offset][first] = matrix[last][last-offset];
            matrix[last][last-offset] = matrix[i][last];
            matrix[i][last] = top;
        }
    }
}

void transpose90_three(int matrix[][SIZE_MATRIX], int size)
{
    for(int layer=0; layer<size/2; ++layer)
    {
        int last = size-layer -1;
        int iter = 0;
        for(int i=layer; i<last; ++i)
        {
            int reverse_i = last - iter;
            int top = matrix[layer][i];
            matrix[layer][i] = matrix[reverse_i][layer];
            matrix[reverse_i][layer] = matrix[last][reverse_i];
            matrix[last][reverse_i] = matrix[i][last];
            matrix[i][last] = top;
            iter++;
        }
    }
}

int main()
{
    int image[SIZE_MATRIX][SIZE_MATRIX] = {
        {1,2,3,4,5,6,7}, 
        {8,9,10,11,12,13,14}, 
        {15,16,17,18,19,20,21}, 
        {22,23,24,25,26,27,28},
        {29,30,31,32,33,34,35},
        {36,37,38,39,40,41,42},
        {43,44,45,46,47,48,49}};

    //Test even matrix
    printImage(image, 4);
    transpose90_zero(image, 4);
    printImage(image, 4);
    transpose90_one(image, 4);
    printImage(image, 4);
    transpose90_two(image, 4);
    printImage(image, 4);
    transpose90_three(image, 4);
    printImage(image, 4);
    
    //Test odd matrix
    printImage(image, 7);
    transpose90_zero(image, 7);
    printImage(image, 7);
    transpose90_one(image, 7);
    printImage(image, 7);
    transpose90_two(image, 7);
    printImage(image, 7);
    transpose90_three(image, 7);
    printImage(image, 7);

    getchar();
}

新的重构中,果断删除通过位运算来进行交换的函数,因为在性能差别不大的情况下,谁会喜欢更加难懂的代码呢?

很多时候,一段代码仅仅细微的改变就可以大大改善程序的可读性,那为什么我们不多做一些工作,将代码写的更好呢?

本题要求将给定的 N 个正整数按非递增的顺序,填入“螺旋矩阵”。所谓“螺旋矩阵”,是指从左上角第 1 个格子开始,按顺时针螺旋方向填充。要求矩阵的规模为 m 行 n 列,满足条件:m×n 等于 N;m≥n;且 m−n 取所有可能值中的最小值。 输入格式: 输入在第 1 行中给出一个正整数 N,第 2 行给出 N 个待填充的正整数。所有数字不超过 10 4 ,相邻数字以空格分隔。 输出格式: 输出螺旋矩阵。每行 n 个数字,共 m 行。相邻数字以 1 个空格分隔,行末不得有多余空格。 输入样例: 12 37 76 20 98 76 42 53 95 60 81 58 93 输出样例: 98 95 93 42 37 81 53 20 76 58 60 76 根据以上的题目要求,改错下列的代码,使之能正确输出 #include <iostream> #include <vector> using namespace std; bool cmp(int a, int b){ return a > b; } int N, m, n; vector<int> num; int juZhen[105][105]; int main(void){ cin >> N; for(int i = 0; i < N; i++){ int temp; cin >> temp; num.push_back(temp); } sort(num.begin(), num.end(), cmp); int min_mn = 9999; for(int i = 1; i <= N / i; i++){ if(N % i == 0 && N / i - i < min_mn){ n = i; m = N / i; min_mn = m - n; } } // m行n列 // 坐标 (ii, jj) 按照 mov 的方向变化 // n m-1 n-1 m-2 n-2... // 1 2 3 // 12 13 4 // 11 14 5 // 10 15 6 // 9 8 7 //顺时针移动 int mov[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; //最开始的坐标 int ii = 0, jj = 0; //一开始行列要移动的次数 int mi = n, mj = m - 1; // i:储存的数字 j:移动的方向 k:已经移动的次数(什么时候转向) int i = 0, j = 0; while(i < N){ if(j % 2 == 0){ for(int k = 0; k < mi; k++){ juZhen[ii][jj] = num[i++]; jj += mov[j][1]; } mi--; }else{ for(int k = 0; k < mj; k++){ juZhen[ii][jj] = num[i++]; ii += mov[j][0]; } mj--; } j = (j + 1) % 4; } for(int i = 0; i < m; i++){ for(int j = 0; j < n;j++){ cout << juZhen[i][j]; if(j != n - 1){ cout << " "; } } if(i != m - 1){ cout << endl; } } return 0; }
最新发布
07-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值