col 1 | col 2 | ... | |
row 1 | ... | ||
row 2 | ... | ||
... | ... |
The simplest implementation for this is to use a two-dimensional array, e.g.
DataType MyTable[NumRows][NumColumns];
However, this allocates storage space for every cell in the table, whether it is used or not.
Often we only need to use a very small number of cells in the table, e.g. a matrix with very few non-zero entries, or a table where the rows are all possible student ids and the columns represent all classes at a university: only a small number of student ids are actually used, and each of those only takes a small fraction of all the available classes.
In such cases we can be wasting a huge amount of space when storing the data.
As an alternative, we use individual nodes to store the data we actually need, and maintain pointers that effectively create linked lists of the nodes along a row and the nodes within a column, e.g.:
row 0 row 1 row 2 row 3 row 4 row 5 | | | | | | V | V col 0 0,1<----+-------------->0,4 | ^ | | | V col 1 ---------------------+-------------->1,4 | | V col 2 2,2 col 3A typical implementation is as follows:
If the table is sparsely populated then we can still expect a reasonably rapid search for each item (along either the row or the column). Note that for a table of R*C elements the worst case search/insert time is max(R,C), and usually we can expect substantially better than that.
Sample code for such a table is given below:
#include <ctime> #include <iostream> #include <iomanip> using namespace std; const int MaxRows = 100; const int MaxCols = 100; class stables { private: struct node { node *up, *down, *left, *right; int row, col; float data; }; node *RowPtrs[MaxRows]; int RowElements[MaxRows]; node *ColPtrs[MaxCols]; int ColElements[MaxCols]; int numrows, numcols; node *search(int r, int c); public: stables(); ~stables(); bool insert(int r, int c, float d); bool search(int r, int c, float &d); bool remove(int r, int c); void print(); void printformatted(); }; stables::stables() { numrows = 0; numcols = 0; for (int r = 0; r < MaxRows; r++) { RowPtrs[r] = NULL; RowElements[r] = 0; } for (int c = 0; c < MaxCols; c++) { ColPtrs[c] = NULL; ColElements[c] = 0; } } stables::~stables() { for (int r = 0; r < numrows; r++) { node *curr = RowPtrs[r]; while (curr) { node *victim = curr; curr = curr->right; delete victim; } } } stables::node *stables::search(int r, int c) { node *curr; if ((RowPtrs[r] == NULL) || (ColPtrs[c] == NULL)) return NULL; if (RowElements[r] < ColElements[c]) { curr = RowPtrs[r]; while ((curr) && (curr->col < c)) curr = curr->right; if ((curr == NULL) || (curr->col == c)) return curr; } else { curr = ColPtrs[c]; while ((curr) && (curr->row < r)) curr = curr->down; if ((curr == NULL) || (curr->row == r)) return curr; } return NULL; } bool stables::search(int r, int c, float &d) { node *curr = search(r, c); if (curr == NULL) return false; d = curr->data; return true; } bool stables::remove(int r, int c) { node *curr = search(r, c); if (curr == NULL) return false; node *above = curr->up; node *below = curr->down; node *next = curr->right; node *prev = curr->left; RowElements[r]--; ColElements[c]--; if (above) above->down = below; else ColPtrs[c] = below; if (below) below->up = above; if (prev) prev->right = next; else RowPtrs[r] = next; if (next) next->left = prev; delete curr; return true; } void stables::printformatted() { cout << " "; for (int c = 0; c < numcols; c++) { if (ColElements[c] > 0) cout << " column" << setw(2) << c << " "; } cout << endl; for (int r = 0; r < numrows; r++) { int printedcolumn = 0; node *curr = RowPtrs[r]; if (RowPtrs[r] != NULL) { cout << "Row " << setw(2) << r << ": "; while (curr) { while (printedcolumn < curr->col) { if (ColElements[printedcolumn] > 0) { cout << " "; } printedcolumn++; } cout << "(" << setw(2) << r << ","; cout << setw(2) << curr->col << ","; cout << setw(4) << curr->data << ")"; curr = curr->right; printedcolumn++; } cout << endl; } } } void stables::print() { for (int r = 0; r < numrows; r++) { node *curr = RowPtrs[r]; while (curr) { cout << "(" << r << ","; cout << curr->col << ","; cout << curr->data << "),"; curr = curr->right; } cout << endl; } } bool stables::insert(int r, int c, float d) { if ((r < 0) || (r >= MaxRows)) { cout << "ERROR: " << r << " is an invalid row number, insert failed" << endl; return false; } if ((c < 0) || (c >= MaxCols)) { cout << "ERROR: " << c << " is an invalid column number, insert failed" << endl; return false; } node *test = search(r, c); if (test != NULL) { cout << "ERROR: that row and column is already occupied, insert failed" << endl; return false; } node *curr = new node; if (curr == NULL) { cout << "ERROR: out of memory, insert failed" << endl; return false; } curr->up = curr->down = curr->left = curr->right = NULL; curr->row = r; curr->col = c; curr->data = d; RowElements[r]++; ColElements[c]++; if (r >= numrows) numrows = r + 1; if (c >= numcols) numcols = c + 1; node *before = RowPtrs[r]; if (before == NULL) { RowPtrs[r] = curr; } else if (before->col > c) { curr->right = RowPtrs[r]; RowPtrs[r]->left = curr; RowPtrs[r] = curr; } else { while (before->right && before->right->col < c) before = before->right; node *after = before->right; before->right = curr; curr->right = after; curr->left = before; if (after) after->left = curr; } node *above = ColPtrs[c]; if (above == NULL) { ColPtrs[c] = curr; } else if (above->row > r) { curr->down = ColPtrs[c]; ColPtrs[c]->up = curr; ColPtrs[c] = curr; } else { while (above->down && above->down->row < r) above = above->down; node *below = above->down; above->down = curr; curr->up = above; curr->down = below; if (below) below->up = curr; } return true; }