I am relatively new to C++ and I had this task to solve, where you are given a number and you have to create an nxn array of integers, where you would have to continually switch directions (right, down, up, left) until you fill it.
Example:
1 2 3 4
12 11 10 5
13 14 9 6
16 15 8 7
I believe I solved it correctly using recursion, but I am not sure if that's the most optimal approach. I know it's not a very difficult task, but if you find it interesting have a look. Any feedback on what I've done is welcome, and possibly how to improve it?
#include <iostream>
using namespace std;
int **createDynamicNumArray(int size) {
int **numArr = new int *[size];
for (int i = 0; i < size; ++i) {
numArr[i] = new int[size];
}
return numArr;
}
void delArr(int **numArr, int size) {
for (int i = 0; i < size; ++i) {
delete[] numArr[i];
}
delete[] numArr;
}
void stuffArr(int **&numArr, int size) {
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
numArr[i][j] = 0;
}
}
}
void printArr(int **arr, int size) {
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
if (arr[i][j] < 10) cout << arr[i][j] << " ";
else if (arr[i][j] < 100) cout << arr[i][j] << " ";
else if (arr[i][j] < 1000) cout << arr[i][j] << " ";
else if (arr[i][j] < 10000) cout << arr[i][j] << " ";
else if (arr[i][j] < 100000) cout << arr[i][j] << " ";
else cout << arr[i][j] << " ";
}
cout << endl;
}
}
bool inBounds(int rowIndex, int colIndex, int size) {
if (rowIndex >= size || rowIndex < 0 || colIndex >= size || colIndex < 0) return false;
return true;
}
bool isFree(int **numArr, int rowIndex, int colIndex) {
if (numArr[rowIndex][colIndex] == 0) return true;
return false;
}
void
solveRecursion(bool &right, bool &down, bool &up, bool &left, const int size, int **&arr, int &currNum, int &rowIndex,
int &colIndex, const int endGoal) {
if (currNum > endGoal) return; // end-case
if (right) {
if (inBounds(rowIndex, colIndex, size) && isFree(arr, rowIndex, colIndex)) {
arr[rowIndex][colIndex] = currNum; // insert
currNum++;
colIndex++;
solveRecursion(right, down, up, left, size, arr, currNum, rowIndex, colIndex, endGoal);
} else {
colIndex--;
rowIndex = size - colIndex;
right = false;
down = true;
}
} else if (down) {
if (inBounds(rowIndex, colIndex, size) && isFree(arr, rowIndex, colIndex)) {
arr[rowIndex][colIndex] = currNum; // insert
currNum++;
rowIndex++;
solveRecursion(right, down, up, left, size, arr, currNum, rowIndex, colIndex, endGoal);
} else {
rowIndex--;
colIndex--;
down = false;
up = true;
}
} else if (up) {
if (inBounds(rowIndex, colIndex, size) && isFree(arr, rowIndex, colIndex)) {
arr[rowIndex][colIndex] = currNum; // insert
currNum++;
rowIndex--;
solveRecursion(right, down, up, left, size, arr, currNum, rowIndex, colIndex, endGoal);
} else {
rowIndex++;
colIndex--;
up = false;
left = true;
}
} else if (left) {
if (inBounds(rowIndex, colIndex, size) && isFree(arr, rowIndex, colIndex)) {
arr[rowIndex][colIndex] = currNum; // insert
currNum++;
colIndex--;
solveRecursion(right, down, up, left, size, arr, currNum, rowIndex, colIndex, endGoal);
} else {
rowIndex++;
colIndex++;
left = false;
right = true;
}
}
solveRecursion(right, down, up, left, size, arr, currNum, rowIndex, colIndex, endGoal);
}
int main() {
int size, currNum = 1, rowIndex = 0, colIndex = 0;
bool right = true, down = false, up = false, left = false;
cout << "Enter N: " << endl;
cin >> size;
const int endGoal = size * size;
int **numArr = createDynamicNumArray(size);
stuffArr(numArr, size);
solveRecursion(right, down, up, left, size, numArr, currNum, rowIndex, colIndex, endGoal);
printArr(numArr, size);
delArr(numArr, size);
return 0;
}
Thank you.
-
\$\begingroup\$ Can you be more specific on the rule you're following here? Using your example, why is there a turn at position 8, when all the other turns only occur when continuing in the same direction isn't an option? \$\endgroup\$Toby Speight– Toby Speight2022年03月08日 13:49:46 +00:00Commented Mar 8, 2022 at 13:49
-
\$\begingroup\$ @TobySpeight Yes, the rule would be to follow the pattern: left-to-right, up-to-down, down-to-up, right-to-left and repeat (in like an encircling way) as it gets smaller and smaller till the board is full. \$\endgroup\$Nik– Nik2022年03月08日 14:09:46 +00:00Commented Mar 8, 2022 at 14:09
1 Answer 1
Don't bring the whole of std
into the global namespace.
Prefer to use standard containers (such as std::vector
) rather than raw-pointers to arrays. And think about allocation failures - if we create half of our matrix, then we're unable to release that memory if one of the later allocations fails and we catch the std::bad_alloc
.
Instead of padding using that long else if
chain, use the std::setw
manipulator to specify a field width.
Don't use std::endl
when there's no need to flush output stream. Ordinary \n
is much less overhead.
if (condition) return true; else return false;
is simply return condition;
, so inBounds()
and isFree()
can be simplified.
SolveRecursion()
could be much simpler. Instead of passing a set of bool
parameters, a first simplification would be to use an enumeration value. But we should consider passing a pair of "step" values (dx
, dy
) that can be -1, 0 or 1, and using them to add onto our row and column indices directly. That would reduce the duplication by a lot.
In main()
we stream from std::cin
, but never look to see whether that was successful. It's an error to use size
if >>
failed to assign to it.
No need to return 0;
at the end - main()
is a magic function that will automatically return 0 if you run off the end of it.
-
\$\begingroup\$ Thank you for the information. Using array of pointers because that's what the assignment requires. Took note about including the whole namespace, I will go about it with importing only what I'm gonna use(cin, cout.. etc) or using typedef. Good point about bool functions as well and all the rest! I want to ask though, how would you go about using enum in this situation? I'm not really sure I follow \$\endgroup\$Nik– Nik2022年03月08日 13:15:45 +00:00Commented Mar 8, 2022 at 13:15
-
\$\begingroup\$ My observation is that that function is called with only one of the four booleans set, so we could replace them with a single value from
enum class Direction { North, East, South, West };
and theif
/else
chain with aswitch
. It's only a small improvement, and doesn't do anything to reduce all that duplication of code, but it is a step towards a simpler function. \$\endgroup\$Toby Speight– Toby Speight2022年03月08日 13:47:27 +00:00Commented Mar 8, 2022 at 13:47