說明
有的時候,為了運算方便或資料儲存的空間問題,使用一維陣列會比二維或多維陣列來得方便,例如上三角矩陣、下三角矩陣或對角矩陣,使用一維陣列會比使用二維陣列來得節省空間。
解法
以二維陣列轉一維陣列為例,索引值由0開始,在由二維陣列轉一維陣列時,我們有兩種方式:「以列(Row)為主」或「以行(Column)為主」。由於
C/C++、Java等的記憶體配置方式都是以列為主,所以您可能會比較熟悉前者(Fortran的記憶體配置方式是以行為主)。
以列為主的二維陣列要轉為一維陣列時,是將二維陣列由上往下一列一列讀入一維陣列,此時索引的對應公式如下所示,其中row與column是二維陣列索引,loc表示對應的一維陣列索引:
loc = column + row*行數
以行為主的二維陣列要轉為一維陣列時,是將二維陣列由左往右一行一行讀入一維陣列,此時索引的對應公式如下所示:
loc = row + column*列數
公式的推導您畫圖看看就知道了,如果是三維陣列,則公式如下所示,其中i(個數u1)、j(個數u2)、k(個數u3)分別表示三維陣列的三個索引:
以列為主:loc = i*u2*u3 + j*u3 + k
以行為主:loc = k*u1*u2 + j*u1 + i
更高維度的可以自行依此類推,但通常更高維度的建議使用其它資料結構(例如物件包裝)會比較具體,也不易搞錯。
在C/C++中若使用到指標時,會遇到指標運算與記憶體空間位址的處理問題,此時也是用到這邊的公式,不過必須在每一個項上乘上資料型態的記憶體大小。
#include <stdio.h> #include <stdlib.h> #define ROW 3 #define COLUMN 4
void toOneByRow(int[][], int[]); void toOneByColumn(int[][], int[]); int indexByRow(int, int); int indexByColumn(int, int); void toOne(int[][COLUMN], int[], int (*)(int, int));
int main(void) { int arr1[ROW][COLUMN] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; int arr2[ROW * COLUMN] = {0};
printf("原二維資料:\n"); int row, column; for(row = 0; row < 3; row++) { for(column = 0; column < 4; column++) { printf("%4d", arr1[row][column]); } printf("\n"); }
printf("\n以列為主:"); toOneByRow(arr1, arr2); int i; for(i = 0; i < 12; i++) printf("%d ", arr2[i]);
printf("\n以行為主:"); toOneByColumn(arr1, arr2); for(i = 0; i < 12; i++) printf("%d ", arr2[i]);
printf("\n");
return 0; }
void toOneByRow(int arr1[][COLUMN], int arr2[]) { toOne(arr1, arr2, indexByRow); }
void toOneByColumn(int arr1[][COLUMN], int arr2[]) { toOne(arr1, arr2, indexByColumn); }
int indexByRow(int row, int column) { return column + row * COLUMN; }
int indexByColumn(int row, int column) { return row + column * ROW; }
void toOne(int arr1[][COLUMN], int arr2[], int (*index)(int, int)) { int row, column; for(row = 0; row < ROW; row++) { for(column = 0; column < COLUMN; column++) { arr2[index(row, column)] = arr1[row][column]; } } }
public class Matrix { public static int[] toOneByRow(final int[][] array) { return toOne(array, new Index() { public int get(int row, int column) { return column + row * array[0].length; } }); } public static int[] toOneByColumn(final int[][] array) { return toOne(array, new Index() { public int get(int row, int column) { return row + column * array.length; } }); }
private static interface Index { int get(int row, int column); } private static int[] toOne(int[][] array, Index index) { int[] arr = new int[array.length * array[0].length]; for(int row = 0; row < array.length; row++) { for(int column = 0; column < array[0].length; column++) { arr[index.get(row, column)] = array[row][column]; } } return arr; } }
def toOneByRow(array): return toOne(array, (lambda row, column: column + row * len(array[0])))
def toOneByColumn(array): return toOne(array, (lambda row, column: row + column * len(array)))
def toOne(array, f): arr = [0] * (len(array) * len(array[0])) for row in range(len(array)): for column in range(len(array[0])): arr[f(row, column)] = array[row][column] return arr
object Matrix { def toOneByRow(array: Array[Array[Int]]) = { toOne(array, _ * array(0).length + _) } def toOneByColumn(array: Array[Array[Int]]) = { toOne(array, _ + _ * array.length) } private def toOne(array: Array[Array[Int]], f: (Int, Int) => Int) = { val arr = new Array[Int](array.length * array(0).length) for(row <- 0 until array.length; column <- 0 until array(0).length) { arr(f(row, column)) = array(row)(column) } arr } }
def toOneByRow(array) toOne(array) { |row, column| column + row * array[0].length } end
def toOneByColumn(array) toOne(array) { |row, column| row + column * array.length } end
def toOne(array) arr = Array.new(array.length * array[0].length, 0) array.length.times { |row| array[0].length.times { |column| arr[yield(row, column)] = array[row][column] } } arr end
|
|