1. 程式人生 > >資料結構基礎(一)陣列,矩陣

資料結構基礎(一)陣列,矩陣

資料結構基礎()

有一個等式,資料結構+演算法=程式,說明了資料結構對於計算機程式設計的重要性。資料結構是指資料元素的集合(或資料物件)及元素間的相互關係和構造方法。資料物件中元素之間的相互關係稱為資料的邏輯結構,資料元素及元素之間關係的儲存形式稱為儲存結構(或物理結構)。

資料結構按照邏輯關係的不同,分為線性結構和非線性結構兩種,線性結構又可分為樹結構和圖結構。

 

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