好的代码应该是简单直接、逻辑清晰的,bug 难以隐藏的。即时没有注释读一遍也能看懂。 而烂代码逻辑混乱,写完过段时间自己都看不懂。
这里从一些问题出发对比好代码烂代码,希望大家共同提高。
1 红黑砖迷阵问题
问题描述[1]
有一个矩形的房间里铺满正方形瓷砖。每块瓷砖涂成黑色或红色。一个人站在黑色的瓷砖上,从此出发,可以移动到四个相邻的瓷砖之一,但只能移动到黑色的瓷砖上。计算他通过重复上述移动所能经过的黑砖数。
这里把红色改成了白色,其他不变。思路原作者描述的很详细了。
/* 样例输入输出
6 9 //列数、行数
BBBBWB
BBBBBW
BBBBBB
BBBBBB
BBBBBB
BBBBBB
BBBBBB
W@BBBW
BWBBWB
45
0 0 //结束标志
*/
这里对比一下我的实现(烂代码)和原作者的实现(好代码)
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
using namespace std;
int g=0;
vector<vector<bool> > isvisit;
/*1. 由于每次输入的是字符串,考虑用vector<string>表示该二维矩阵,cin>>str输入每行。
用char a[][];cin>>a[i] 更方便*/
void reach(vector<string>&v,int x,int y,int h,int w)
{
if(isvisit[x][y] || v[x][y]=='W') return;
g++;
isvisit[x][y]=true;
//cout<<x<<" "<<y<<" "<<g<<" "<<v[x][y]<<endl;
/*2. 探索函数,顺序为左右上下
把所有边界条件写到函数入口更好更方便。*/
//left
if(y>0 ) reach(v,x,y-1,h,w);
//right
if(y<w-1 ) reach(v,x,y+1,h,w);
//up
if(x<h-1) reach(v,x+1,y,h,w);
//down
if(x>0 ) reach(v,x-1,y,h,w);
}
int main()
{
int w,h;//width height//列数、行数
while(cin>>w>>h)
{
if(w==0 && h==0) break;
vector<string> v(h,"");//1. 初始化矩阵v
// true: visited
//初始化二维vector
isvisit=vector<vector<bool> >(h,vector<bool>(w,false));
int sx=0;
int sy=0;
bool isfindat=false;
for(int i=0;i<h;i++)
{
cin>>v[i];
if(isfindat==false)
{
for(int j=0;j<v[i].size();j++)
{
if(v[i][j]=='@')
{
sx=i;
sy=j;
isfindat=true;
}
}
}
}
g=0;
reach(v,sx,sy,h,w);
cout<<g<<endl;
}
}
*/
原作者
#include <iostream>
#include<cstring>
using namespace std;
// R 红砖 B 黑砖 @人
const int MAX_ROW = 20, MAX_COLUMN = 20;
int sum, m, n; //经过的瓷砖数 行数 列数
char map[MAX_ROW][MAX_COLUMN];
bool visited[MAX_ROW][MAX_COLUMN]; //记录访问标志的数组
//递归计算从(row,col)出发经过的瓷砖数
void search(int row, int col)
{
//边界条件 坐标在地图外,不可通行,已经访问过 则回溯
// 擦作者这里col>=n写错了,坑死了,找了2个小时
if (row < 0 || row >= n || col < 0 || col >= m || map[row][col] == 'W' || visited[row][col])
return;
//cout<<row<<" "<<col<<" "<<sum<<endl;
visited[row][col] = true; //设置当前坐标的标志位
++sum; //经过的瓷砖数+1
//递归遍历当前坐标的四个相邻点
search(row , col-1);
search(row, col+1);
search(row-1, col);
search(row+1 , col);
}
int main(){
while (cin >> m >> n) //输入列数和行数
{
if(m==0 && n==0) break;
int row, col;
for (int i = 0; i < n; i++)
{
cin>> map[i]; //输入当前行的数据集
for (int j = 0; j < m; j++)
{
if (map[i][j] == '@')
{
row = i;
col = j;
}
}
}
//cout<<row<<" "<<col<<endl;
memset(visited,false,sizeof(visited));
sum = 0;
search(row, col);
cout << sum << endl;
}
return 0;
}
2 调整数组顺序使奇数位于偶数前面
题目描述[2]
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
v1 将原数组分割成两个子数组,然后再合并,时间复杂度O(n),空间复杂度较高。
void reOrderArray(vector<int> &array) {
vector<int> odd;//odd numbers
vector<int> eve;//even numbers
for(int i=0;i<array.size();i++)
{
if(array[i]%2==0)
odd.push_back(array[i]);
else
eve.push_back(array[i]);
}
array.clear();
array.insert(array.begin(),eve.begin(),eve.end());
array.insert(array.end(),odd.begin(),odd.end());
}
v2 将数组分为前半段(奇数)和后半段(混合)。用类似插入排序的思路把每次找到的奇数插入到前半段。等前半段增长结束就排好了。
用&1
判断奇数更高效
时间复杂度O(n^2),空间复杂度O(1)
void reOrderArray(vector<int> &v) {
if(v.empty()) return;
int lastodd=0;
for(int i=0;i<v.size();i++)
{
if(v[i] & 1)
{
int t=v[i];
for(int j=i-1;j>=lastodd;j--)
{
v[j+1]=v[j];
}
v[lastodd]=t;
lastodd++;
}
}
}
References
- 递归回溯法实战(一)——(Red and Black)红黑砖迷阵(POJ1979)
- http://www.nowcoder.com/practice/beb5aa231adc45b2a5dcc5b62c93f593
来源:oschina
链接:https://my.oschina.net/u/576429/blog/758999