0
\$\begingroup\$

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.

asked Mar 8, 2022 at 10:27
\$\endgroup\$
2
  • \$\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\$ Commented 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\$ Commented Mar 8, 2022 at 14:09

1 Answer 1

1
\$\begingroup\$

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.

answered Mar 8, 2022 at 10:44
\$\endgroup\$
2
  • \$\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\$ Commented 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 the if/else chain with a switch. 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\$ Commented Mar 8, 2022 at 13:47

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.