经典算法题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;
    }

具体完整代码见我的githubhttps://github.com/shibing624/BlogCode/blob/master/src/main/java/xm/math/matrix/Matrix.java

结果

这里写图片描述

四.矩阵乘法

已知稀疏矩阵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;
    }

完整代码见github

给一个矩阵计算的线上计算器:https://matrixcalc.org/zh/#%7B%7B1,1%7D,%7B2,2%7D%7D%2A%7B%7B1,1%7D,%7B2,2%7D%7D

结果

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值