資料結構基礎(一)陣列,矩陣
資料結構基礎(一)
有一個等式,資料結構+演算法=程式,說明了資料結構對於計算機程式設計的重要性。資料結構是指資料元素的集合(或資料物件)及元素間的相互關係和構造方法。資料物件中元素之間的相互關係稱為資料的邏輯結構,資料元素及元素之間關係的儲存形式稱為儲存結構(或物理結構)。
資料結構按照邏輯關係的不同,分為線性結構和非線性結構兩種,線性結構又可分為樹結構和圖結構。
1.1 陣列
1.陣列的定義和基本運算
陣列是程式中最常用的資料結構,陣列的本質是記憶體中一段大小固定,地址連續的儲存單元。
一維陣列是一個長度固定,下標有序的線性序列。二位陣列則是一個矩陣結構,本質上是以陣列作為陣列元素的陣列,即“陣列的陣列”。以二維陣列A[m,n]為例,其結構如圖2-1所示:
A[m,n]可以看做一個行向量形式的線性序列:
Am,n =[[a11,a12…a1n],[ a21,a22…a2n],…,[ am1,am2…amn]];
也可以看做一個列向量形式的線性序列
Am,n =[[a11,a21…am1],[ a12,a22…am2],…,[ a1n,a2n…amn]];
二維陣列的本質是一維陣列的陣列
陣列的結構特點:
陣列元素數目固定,一旦定義不可改變。
陣列中的元素具有相同的型別。
陣列下標具有上下界的約束且有序。
陣列的兩個基本運算:
給定一組下標,存取相應的資料元素。
給定一組下標,更改相應元素的值。
在程式設計語言中,把陣列看做是具有共同名字的相同型別的多個變數的集合。
2. 陣列元素的儲存
陣列適合採用順序儲存結構,對於陣列一旦確定了其維數和各維的長度,便可分配儲存空間。所以,只要給出一組下標便可求出相應陣列元素的儲存位置,在陣列的順序儲存結構中,陣列元素的位置和其下標呈線性關係。
二維陣列的儲存結構可分為以行為主儲存和以列為主儲存兩種方式
設每個陣列元素佔用L個單元,m,n為陣列的行數和列數,Loc(a11)表示元素a11的地址,
以行為主:
Loc(aij)=Loc(a11)+(i-1) ×n+(j-1) ×L
以列為主:
Loc(aij)=Loc(a11)+(j-1) ×m+(i-1) ×L
推廣至多維陣列,按下標順序(以行為主)儲存時,先排最右的下標,從右至左到最左下標,而逆下標順序正好相反。
3.矩陣
在數學中,矩陣(Matrix)是一個按照長方陣列排列的複數或實數集合 [1] ,最早來自於方程組的係數及常數所構成的方陣。這一概念由19世紀英國數學家凱利首先提出。在資料結構中,主要討論如何在節省儲存空間的前提下,正確高效的運算矩陣。
在實際應用中,經常出現一些階數很高的矩陣,同時在矩陣中有很多值相同的元素並且它們的分佈有一定的規律——稱為特殊矩陣(special matrix),對稱矩陣就是其中一種。對稱矩陣的特點是:在一個n階方陣中,有aij=aji(1≤i,j≤n)。可以對這類矩陣進行壓縮儲存,從而節省儲存空間,並使矩陣的各種運算能有效進行。
(1) 對稱矩陣
對稱矩陣關於主對角線對稱,因此只需儲存下三角部分(包括主對角線)即可。這樣,原來需要儲存n×n個儲存單元,現在只需要n×(n+1)/2個儲存單元,節約了大約一半的儲存單元。當n較大時,這是比較可觀的一部分儲存單元。
如何只儲存下三角部分的元素呢?由於下三角中共有n×(n+1)/2個元素,可將這些元素按行儲存到一個數組SA[n(n+1)/2]中。這樣,下三角中的元素aij(i≥j)儲存到SA[k]中,在陣列SA中的下標k和i、j的關係為:k=i×(i-1)/2+j-1,定址的計算方法如圖所示。
Java程式碼實現對稱矩陣儲存壓縮:
package top.yxy.xs;
/**
* 稀疏矩陣的壓縮儲存
*
* 稀疏矩陣三元組順序表
*
* 三元組順序儲存的稀疏矩陣類
*
* @author clarck
*
*/
public class SeqSparseMatrix {
// 矩陣行數、列數
private int rows, columns;
// 稀疏矩陣三元組順序表
private SeqList<Triple> list;
/**
* 構造rows行,colums列零矩陣
*
* @param rows
* @param columns
*/
public SeqSparseMatrix(int rows, int columns) {
if (rows <= 0 || columns <= 0)
throw new IllegalArgumentException("矩陣行數或列數為非正數");
this.rows = rows;
this.columns = columns;
// 構造空順序表,執行SeqList()構造方法
this.list = new SeqList<Triple>();
}
public SeqSparseMatrix(int rows, int columns, Triple[] elems) {
this(rows, columns);
// 按行主序插入一個元素的三元組
for (int i = 0; i < elems.length; i++)
this.set(elems[i]);
}
/**
* 返回矩陣第i行第j列元素,排序順序表的順序查詢演算法,O(n)
*
* @param i
* @param j
* @return
*/
public int get(int i, int j) {
if (i < 0 || i >= rows || j < 0 || j >= columns)
throw new IndexOutOfBoundsException("矩陣元素的行或列序號越界");
Triple item = new Triple(i, j, 0);
int k = 0;
Triple elem = this.list.get(k);
// 在排序順序表list中順序查詢item物件
while (k < ((CharSequence) this.list).length() && item.compareTo(elem) >= 0) {
// 只比較三元組元素位置,即elem.row == i && elem.column == j
if (item.compareTo(elem) == 0)
return elem.value;
// 查詢到(i, j), 返回矩陣元素
k++;
elem = ((Object) this.list).get(k);
}
return 0;
}
/**
* 以三元組設定矩陣元素
*
* @param elem
*/
public void set(Triple elem) {
this.set(elem.row, elem.colum, elem.value);
}
/**
* 設定矩陣第row行第column列的元素值為value,按行主序在排序順序表list中更改或插入一個元素的三元組, O(n)
*
* @param row
* @param column
* @param value
*/
public void set(int row, int column, int value) {
// 不儲存值為0元素
if (value == 0)
return;
if (row >= this.rows || column >= this.columns)
throw new IllegalArgumentException("三元組的行或列序號越界");
Triple elem = new Triple(row, column, value);
int i = 0;
// 在排序的三元組順序表中查詢elem物件,或更改或插入
while (i < this.list.length()) {
Triple item = this.list.get(i);
// 若elem存在,則更改改位置矩陣元素
if (elem.compareTo(item) == 0) {
// 設定順序表第i個元素為elem
this.list.set(i, elem);
return;
}
// elem 較大時向後走
if (elem.compareTo(item) >= 0)
i++;
else
break;
}
this.list.insert(i, elem);
}
@Override
public String toString() {
String str = "三元組順序表:" + this.list.toString() + "\n";
str += "稀疏矩陣" + this.getClass().getSimpleName() + "(" + rows + " * "
+ columns + "): \n";
int k = 0;
// 返回第k個元素,若k指定序號無效則返回null
Triple elem = this.list.get(k++);
for (int i = 0; i < this.rows; i++) {
for (int j = 0; j < this.columns; j++)
if (elem != null && i == elem.row && j == elem.colum) {
str += String.format("%4d", elem.value);
elem = this.list.get(k++);
} else {
str += String.format("%4d", 0);
}
str += "\n";
}
return str;
}
/**
* 返回當前矩陣與smat相加的矩陣, smatc=this+smat,不改變當前矩陣,演算法同兩個多項式相加
*
* @param smat
* @return
*/
public SeqSparseMatrix plus(SeqSparseMatrix smat) {
if (this.rows != smat.rows || this.columns != smat.columns)
throw new IllegalArgumentException("兩個矩陣階數不同,不能相加");
// 構造rows*columns零矩陣
SeqSparseMatrix smatc = new SeqSparseMatrix(this.rows, this.columns);
int i = 0, j = 0;
// 分別遍歷兩個矩陣的順序表
while (i < this.list.length() && j < smat.list.length()) {
Triple elema = this.list.get(i);
Triple elemb = smat.list.get(j);
// 若兩個三元組表示相同位置的矩陣元素,則對應元素值相加
if (elema.compareTo(elemb) == 0) {
// 相加結果不為零,則新建元素
if (elema.value + elemb.value != 0)
smatc.list.append(new Triple(elema.row, elema.colum,
elema.value + elemb.value));
i++;
j++;
} else if (elema.compareTo(elemb) < 0) { // 將較小三元組複製新增到smatc順序表最後
// 複製elema元素執行Triple拷貝構造方法
smatc.list.append(new Triple(elema));
i++;
} else {
smatc.list.append(new Triple(elemb));
j++;
}
}
// 將當前矩陣順序表的剩餘三元組複製新增到smatc順序表最後
while (i < this.list.length())
smatc.list.append(new Triple(this.list.get(i++)));
// 將smat中剩餘三元組複製新增到smatc順序表最後
while (j < smatc.list.length()) {
Triple elem = smat.list.get(j++);
if (elem != null) {
smatc.list.append(new Triple(elem));
}
}
return smatc;
}
/**
* 當前矩陣與smat矩陣相加,this+=smat, 改變當前矩陣,演算法同兩個多項式相加
*
* @param smat
*/
public void add(SeqSparseMatrix smat) {
if (this.rows != smat.rows || this.columns != smat.columns)
throw new IllegalArgumentException("兩個矩陣階數不同,不能相加");
int i = 0, j = 0;
// 將mat的各三元組依次插入(或相加)到當前矩陣三元組順序表中
while (i < this.list.length() && j < smat.list.length()) {
Triple elema = this.list.get(i);
Triple elemb = smat.list.get(j);
// 若兩個三元組表示相同位置的矩陣元素,則對應元素值相加
if (elema.compareTo(elemb) == 0) {
// 相加結果不為0,則新建元素
if (elema.value + elemb.value != 0)
this.list.set(i++, new Triple(elema.row, elema.colum,
elema.value + elemb.value));
else
this.list.remove(i);
j++;
} else if (elema.compareTo(elemb) < 0) { // 繼續向後尋找elemb元素的插入元素
i++;
} else {
// 複製elemb元素插入作為this.list的第i個元素
this.list.insert(i++, new Triple(elemb));
j++;
}
}
// 將mat中剩餘三元組依次複製插入當前矩陣三元組順序表中
while (j < smat.list.length()) {
this.list.append(new Triple(smat.list.get(j++)));
}
}
// 深拷貝
public SeqSparseMatrix(SeqSparseMatrix smat) {
this(smat.rows, smat.columns);
// 建立空順序表,預設容量
this.list = new SeqList<Triple>();
// 複製smat中所有三元組物件
for (int i = 0; i < smat.list.length(); i++)
this.list.append(new Triple(smat.list.get(i)));
}
/**
* 比較兩個矩陣是否相等
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof SeqSparseMatrix))
return false;
SeqSparseMatrix smat = (SeqSparseMatrix) obj;
return this.rows == smat.rows && this.columns == smat.columns
&& this.list.equals(smat.list);
}
/**
* 返回轉置矩陣
* @return
*/
public SeqSparseMatrix transpose() {
//構造零矩陣,指定行數和列數
SeqSparseMatrix trans = new SeqSparseMatrix(columns, rows);
for (int i = 0; i < this.list.length(); i++) {
//插入矩陣對稱位置元素的三元組
trans.set(this.list.get(i).toSymmetry());
}
return trans;
}
}
(2)稀疏矩陣
在矩陣中,若數值為0的元素數目遠遠多於非0元素的數目,並且非0元素分佈沒有規律時,則稱該矩陣為稀疏矩陣。稀疏矩陣常使用三元組儲存法,三元組表示法就是在儲存非零元的同時,儲存該元素所對應的行下標和列下標。稀疏矩陣中的每一個非零元素由一個三元組(i,j,aij)唯一確定。矩陣中所有非零元素存放在由三元組組成的陣列中。
檢視更多文章歡迎關注我的微信公眾號:AlbertYang