1. 程式人生 > >經典演算法題15-稀疏矩陣及三元組

經典演算法題15-稀疏矩陣及三元組

一. 引入

我們知道矩陣是一個非常強大的資料結構,在動態規劃以及各種圖論演算法上都有廣泛的應用。

當然矩陣有著不足的地方就是空間和時間複雜度都維持在N²上,比如1w個數字建立一個矩陣,在記憶體中會佔用1w*1w=1億的型別空間,這時就會遇到outofmemory。。。

那麼面臨的一個問題就是如何來壓縮矩陣,當然壓縮的方式有很多種,這裡就介紹一個順序表的壓縮方式:三元組

二. 介紹三元組

有時候我們的矩陣中只有零星的一些非零元素,其餘的都是零元素,那麼我們稱之為稀疏矩陣,當然沒有絕對的說有多少個零元素才算稀疏。簡單說,稀疏矩陣就是非零元素個數遠遠小於元素個數的矩陣。相對於稀疏矩陣來說,一個不稀疏的矩陣也稱作稠密矩陣。

針對上面的這個無規律的存放非零元素,三元組提出了一種方法,就是僅僅記錄矩陣中的非零元素以及**它的行,列以及值**N(x,y,v)構成的一個三元組,標識一個稀疏矩陣的話,還要記錄該矩陣的階數,這樣我們就將一個二維的變成了一個一維,極大的壓縮的儲存空間。
這裡要注意的就是,三元組的構建採用“行”是從上到下,“列”也是從左到右的方式構建的順序表。
這裡寫圖片描述

package xm.math.matrix;

/**
 * 三元組處理稀疏矩陣
 *
 * @author xuming
 */
public class Node {

    public int x;
    public int y;
    public
double value; public Node(int r, int c, double v) { this.x = r; this.y = c; this.value = v; } public Node() { this(0, 0, 0.0); } }

其實說到這裡也就差不多了,我們只要知道三元組是用來做矩陣壓縮的一個順序儲存方式即可,然後知道怎麼用三元組表來做一些常規的矩陣運算,好了,既然說已經做成線性儲存了,那就做個轉置(“行列置換”)試試。

三. 矩陣轉置

做行列置換很容易,也就是交換”非零元素”的(x,y)座標,要注意的就是,原先我們的三元組採用的是”行優先“,所以在做轉置的時候需要遵循”列優先“。
這裡寫圖片描述

 public Matrix convertMatrix(Matrix node) {
        Matrix matrix = new Matrix();
        matrix.rows = node.rows;
        matrix.cols = node.cols;
        matrix.count = node.count;
        for (int col = 0; col < node.cols; col++) {
            for (int triple = 0; triple < node.count; triple++) {
                Node t = node.nodes.get(triple);
                if (col == t.y) {
                    matrix.nodes.add(new Node(t.y, t.x, t.value));
                }
            }
        }
        return matrix;
    }

結果

這裡寫圖片描述

四.矩陣乘法

已知稀疏矩陣A(m1× n1)和B(m2× n2),求乘積C(m1× n2)。
稀疏矩陣A、B、C 及它們對應的三元組表A.data、B.data、C.data如下圖:

這裡寫圖片描述

計算方法相比大家都知道,線性代數的簡單運算,口訣是:A的第一行跟B的第一列分別對應相乘,然後相加;往後依次A的第一行與B的第二列;等等。

這裡寫圖片描述

 /**
     * 兩稀疏矩陣相乘。
     * 前提:1.矩陣元素能夠相乘
     *       2.一個矩陣的列等於另一個矩陣的行
     * 原理:假設兩矩陣M與N相乘,前提M的列M.col要等於N的行N.row(反之亦可)
     * 得到結果矩陣Q, Q.row=M.row, Q.col = N.col
     * 而且Q[i][j] += M[i][k] * N[k][j]  0<i<M.row,0<j<N.col,0<k<M.col或N.row
     */
    public Integer[][] multiMatrix(Integer[][] m, Integer[][] n){
        Integer[][] q = new Integer[m.length][n[0].length];
        for(int i=0;i<m.length;i++){
            for(int j=0;j<n[0].length;j++){
                int num = 0;
                for(int k=0;k<n.length;k++){
                    num += (m[i][k]==null?0:m[i][k]) * (n[k][j]==null?0:n[k][j]);
                }
                q[i][j] = num;
            }
        }
        //列印結果
        for(int i=0;i<q.length;i++){
            for(int j=0;j<q[0].length;j++){
                System.out.print(q[i][j]+" ");
            }
            System.out.println();
        }
        return q;
    }

結果

這裡寫圖片描述