was surfing the web the other day, looking for Visual Basic questions to answer, when I stumbled across an old FAQ that explained why Visual Basic no longer has arrays with non-zero lower bounds. I don't really buy the explanation given by Paul Vick
at (it seems like the needs of the none outweighing the needs of the many to me) but it did start me thinking about arrays.
Visual Studio provides arrays of any data type. You can make one-, two-, and higher-dimensional arrays of integers, strings, objects, structures, or whatever. But there are a couple of useful kinds of arrays that Visual Studio doesn't provide. This article explains how you can implement three of these: triangular arrays, sparse arrays, and arrays with non-zero lower bounds.
When you create a typical array, Visual Studio allocates memory for a block of items that includes one entry for every possible combination of indexes. For example, the following code allocates an array of Booleans containing 1 million entries:
Dim is_connected(1000, 1000) As Boolean
In some applications, you don't really need all these entries. For example, suppose the application tests connectivity in an electric, airline, or other network and the method is_connected(R, C)
if there is a link connecting location R
with location C
. Assuming the network is non-directional (so that a link between R
also means there is a link between C
), then this array contains a lot of duplicated information. If is_connected(R, C)
then is_connected(C, R)
is also true
You can avoid storing all of this duplicated information and save half of the array's space by using a triangular array. A triangular array
stores only the values for is_connected(R, C)
where R >= C
. It deduces the values of other entries by switching the order of R and C.
The TriangularArray class described here saves this space by mapping the entries in the array into a one-dimensional storage array. Figure 1
shows a small triangular array on top and the linear storage of the array's values on the bottom.
|Figure 1. Triangle Tricks: The TriangularArray class stores entries for a triangular array (top) in a linear storage array (bottom).|
The only tricky part is figuring out the index in the storage array that corresponds to an entry in the triangular array.
Consider an item in array position (R, C
), where "R" is the row and "C" is the column. The rows above this one contain 1 + 2 + 3 + ... + R
entries. You may recall that there is a simple formula for this kind of sum: 1 + 2 + ... + R = R * (R + 1) / 2
Now notice that the number of items to the left of this one in its row is C
, so the total number of items that come before this one is R * (R + 1) / 2 + C
For example, consider item E
in Figure 1
. This item is at position (2, 1
) in the triangular array (be sure to start counting from index 0), so its position in the storage array is 2 * (2 + 1) / 2 + 1 = 3 + 1 = 4
, which you can verify in Figure 1
The following code shows a TriangularArray class. I've removed some bounds checking code to make the code easier to read:
Public Class TriangularArray(Of T)
Private m_Width As Integer
Private m_Values() As T
' Allocate storage.
Public Sub New(ByVal width As Integer)
m_Width = width
ReDim m_Values(m_Width * (m_Width + 1) \ 2 - 1)
' Return the item at position (row, column).
Default Public Property Item( _
ByVal r As Integer, ByVal c As Integer)
Return m_Values(ItemIndex(r, c))
m_Values(ItemIndex(r, c)) = value
' Calculate the item's position in the array.
Private Function ItemIndex(ByVal r As Integer, _
ByVal c As Integer) As Integer
If r >= c Then Return r * (r + 1) \ 2 + c
Return c * (c + 1) \ 2 + r
The class's constructor takes the array's width as a parameter, and allocates enough space to hold all of the array's items.
property gets or sets an item in the array. It calls the ItemIndex
method to convert the item's row and column numbers into an index in the storage array and gets or sets the appropriate value.
keyword makes Item
the default property for the class. That means the main program can treat an object of the class as if it were an array and the "index" passed to the object is sent as a parameter to the property. (In C#, this property is the class indexer.)
method simply returns R * (R + 1) / 2 + C
as described earlier. If R < C
, it swaps the roles of R
The sample program TriangularArray
(available with the downloadable code for this article
in both Visual Basic and C#), demonstrates the class for the array shown in Figure 1
. (For extra credit, try to figure out how to modify the class slightly to handle arrays that don't need to contain items on the diagonal where R = C