javascript实现数据结构: 稀疏矩阵之三元组线性表表示
稀疏矩阵(Sparse Matrix):对于稀疏矩阵,目前还没有一个确切的定义。设矩阵A是一个n*m的矩阵中有s个非零元素,设 δ=s/(n*m),称δ为稀疏因子,
如果某一矩阵的稀疏因子δ满足δ≦0.05时称为稀疏矩阵,
稀疏矩阵的压缩存储
对于稀疏矩阵,采用压缩存储方法时,只存储非0元素。必须存储非0元素的行下标值、列下标值、元素值。因此,一个三元组(i, j, aij)唯一确定稀疏矩阵的一个非零元素。
上图的稀疏矩阵A的三元组线性表为: ( (1,2,12), (1,3,9), (3,1,-3), (3,8,4), (4,3,24), (5,2,18), (6,7,-7), (7,4,-6) )
1 三元组顺序表
若以行序为主序,稀疏矩阵中所有非0元素的三元组,就可以得构成该稀疏矩阵的一个三元组顺序表。
function Triple(i, j, elem) {
// 该非零元的行下标和列下标
this.i = i || 0;
this.j = j || 0;
this.e = elem || null;
} function TSMatrix(mu, nu) {
// 非零元三元组表
this.data = [];
// 矩阵的行数,列数
this.mu = mu || 0;
this.nu = nu || 0;
}
下图所示的稀疏矩阵及其相应的转置矩阵所对应的三元组顺序表:
一个m*n的矩阵A,它的转置B是一个n*m的矩阵,且b[i][j]=a[j][i],0≦i≦n,0≦j≦m,即B的行是A的列,B的列是A的行。
设稀疏矩阵A是按行优先顺序压缩存储在三元组表a.data中,若仅仅是简单地交换a.data中i和j的内容,得到三元组表b.data,
b.data将是一个按列优先顺序存储的稀疏矩阵B,要得到按行优先顺序存储的b.data,就必须重新排列三元组表b.data中元素的顺序。
求转置矩阵的基本算法思想是:
① 将矩阵的行、列下标值交换。即将三元组表中的行、列位置值i 、j相互交换;
② 重排三元组表中元素的顺序。即交换后仍然是按行优先顺序排序的。
方法一: 算法思想:按稀疏矩阵A的三元组表a.data中的列次序依次找到相应的三元组存入b.data中。
每找转置后矩阵的一个三元组,需从头至尾扫描整个三元组表a.data 。找到之后自然就成为按行优先的转置矩阵的压缩存储表示
TSMatrix.prototype = {
constructor: TSMatrix,
addTriple: function (triple) {
if (triple instanceof Triple) {
if(triple.i >= this.mu)
this.mu = triple.i + 1;
if(triple.j >= this.nu)
this.nu = triple.j + 1; this.data.push(triple);
return true;
}
return false;
},
// 采用三元组表存储表示,求稀疏矩阵的转置矩阵t
// 按照b.data中三元组的次序依次在a.data中找到相应的三元组进行转置
transposeSMatrix: function () {
var t = new TSMatrix();
t.mu = this.nu;
t.nu = this.mu; if (this.data.length) {
var q = 0;
for (var col = 0; col < this.nu; col++) {
for (var p = 0; p < this.data.length; p++) {
if (this.data[p].j === col)
t.data[q++] = new Triple(this.data[p].j, this.data[p].i, this.data[p].e);
}
}
} return t;
}
};
算法分析:本算法主要的工作是在p和col的两个循环中完成的,故算法的时间复杂度为O(nu * data.length),即矩阵的列数和非0元素的个数的乘积成正比。
方法二(快速转置的算法) 算法思想:
直接按照稀疏矩阵A的三元组表a.data的次序依次顺序转换,并将转换后的三元组放置于三元组表b.data的恰当位置。
前提:若能预先确定原矩阵A中每一列的(即B中每一行)第一个非0元素在b.data中应有的位置,则在作转置时就可直接放在b.data中恰当的位置。
因此,应先求得A中每一列的非0元素个数。 附设两个辅助向量num[ ]和cpot[ ] 。
◆ num[col]:统计A中第col列中非0元素的个数;
◆ cpot[col] :指示A中第一个非0元素在b.data中的恰当位置。
显然有位置对应关系:
快速转置算法如下:
// 采用三元组表存储表示,求稀疏矩阵的转置矩阵t
/*
按照a.data中三元组的次序进行转置,并将转置后的三元组置入b中恰当的位置。
如果能预先确定矩阵M中每一列(即T中每一行)的第一个非零元在b.data中应有的位置,
那么在对a.data中的三元组依次做转置时,便可直接放到b.data中恰当的位置上去。
为了其额定这些位置,在转置前,应先求得M的每一列中非零元的个数,进而求得每一列的第一个非零元在b.data中应有的位置。
在此,需要设num和cpot两个变量。num[col]表示矩阵M中第col列中非零元的个数,
cpot[col]指示M中第col列的第一个非零元在b.data中的恰当位置。显然有:
cpot[0] = 1;
cpot[col] = cpot[col - 1] + num[col - 1] 2 <= col <= a.nu
*/
TSMatrix.prototype.fastTransposeSMatrix = function(){
var t = new TSMatrix();
t.mu = this.nu;
t.nu = this.mu; if(this.data.length){
var num = [];
for(var col = 0; col < this.nu; col++)
num[col] = 0;
for(var i = 0; i < this.data.length; i++)
++num[this.data[i].j]; // 求矩阵中每一列含非零元个数
// 求第col列中第一个非零元在b.data中的序号
var cpot = [0];
for(col = 1; col < this.nu; col++)
// 上一列之前的序号+上一列的非零元个数 = 该列的序号
cpot[col] = cpot[col - 1] + num[col - 1];
for(var p = 0; p < this.data.length; p++){
col = this.data[p].j;
var q = cpot[col];
t.data[q] = new Triple(this.data[p].j, this.data[p].i, this.data[p].e);
// 给该列的序号+1,用作相同列数的情况
++cpot[col];
}
} return t;
}
三元组顺序表又称有序的双下标法,它的特点是,非零元在表中按行序有序存储,因此便于进行依行顺序处理的矩阵运算。
然而,若需按行号存取某一行的非零元,则从头开始进行查找。
行逻辑链接的顺序表
为了便于随机存取任意一行的非零元,则需知道每一行的第一个非零元在三元组表中的位置。
为此可将快速转置矩阵的算法中创建的,指示“行”信息的辅助数组cpot固定在稀疏矩阵的存储结构中。
称这种“带行链接信息”的三元组表为行逻辑链接的顺序表
设有两个稀疏矩阵A=(aij)m*n ,B=(bij)n*p ,其存储结构采用行逻辑链接的三元组顺序表。求稀疏矩阵的乘法:
function RLSMatrix(mu, nu){
TSMatrix.apply(this, arguments);
this.rpos = [0];
}
RLSMatrix.MAXSIZE = 100;
RLSMatrix.prototype = {
constructor: RLSMatrix,
__proto__: TSMatrix.prototype,
// todo
/**
* 求矩阵乘积Q = M * N,采用行逻辑链接存储表示
* @param nMatrix
* @returns {RLSMatrix}
*/
multSMatrix: function(nMatrix){
if(this.nu !== nMatrix.mu) throw Error('nu is not equivalent to mu'); // 初始化Q
var qMatrix = new RLSMatrix(this.mu, nMatrix.nu);
// Q是非零矩阵
if(this.data.length * nMatrix.data.length !== 0){
// 处理M的每一行
for(var arow = 0; arow < this.mu; arow++){
// 当前行各元素累加器清零
var ctemp = [];
qMatrix.rpos[arow] = qMatrix.data.length + 1;
var tp, ccol; if(arow < this.mu)
tp = this.rpos[arow + 1];
else
tp = this.data.length + 1; //对当前行中每一个非零元找到对应元在N中的行号
for(var p = this.rpos[arow]; p < tp; p++){
var brow = this.data[p].j;
var t;
if(brow < nMatrix.mu)
t = nMatrix.rpos[brow + 1];
else
t = nMatrix.data.length + 1; for(var q = nMatrix.rpos[brow]; q < t; q++){
// 乘积元素在Q中的序号
ccol = nMatrix.data[q].j;
ctemp[ccol] = (ctemp[ccol] || 0) + this.data[p].e * nMatrix.data[q].e;
}
} // 压缩存储该行非零元
for(ccol = 1; ccol < qMatrix.nu; ccol++){
if(ctemp[ccol]){
if(++qMatrix.data.length > RLSMatrix.MAXSIZE) throw Error('overflow');
qMatrix.data[qMatrix.data.length - 1] = new Triple(arow, ccol, ctemp[ccol]);
}
}
}
} return qMatrix;
},
_calcPos: function clcPos(){
var num = [];
for(var col = 0; col < this.nu; col++)
num[col] = 0;
for(var i = 0; i < this.data.length; i++)
++num[this.data[i].j]; // 求矩阵中每一列含非零元个数
// 求第col列中第一个非零元在b.data中的序号
for(col = 1; col < this.nu; col++)
// 上一列之前的序号+上一列的非零元个数 = 该列的序号
this.rpos[col] = this.rpos[col - 1] + num[col - 1];
}
};
所有代码:
/**
* 系数矩阵的三元组顺序表存储表示
*/ function Triple(i, j, elem) {
// 该非零元的行下标和列下标
this.i = i || 0;
this.j = j || 0;
this.e = elem || null;
} function TSMatrix(mu, nu) {
// 非零元三元组表
this.data = [];
// 矩阵的行数,列数
this.mu = mu || 0;
this.nu = nu || 0;
}
TSMatrix.prototype = {
constructor: TSMatrix,
addTriple: function (triple) {
if (triple instanceof Triple) {
if(triple.i >= this.mu)
this.mu = triple.i + 1;
if(triple.j >= this.nu)
this.nu = triple.j + 1; this.data.push(triple);
return true;
}
return false;
},
// 采用三元组表存储表示,求稀疏矩阵的转置矩阵t
// 按照b.data中三元组的次序依次在a.data中找到相应的三元组进行转置
transposeSMatrix: function () {
var t = new TSMatrix();
t.mu = this.nu;
t.nu = this.mu; if (this.data.length) {
var q = 0;
for (var col = 0; col < this.nu; col++) {
for (var p = 0; p < this.data.length; p++) {
if (this.data[p].j === col)
t.data[q++] = new Triple(this.data[p].j, this.data[p].i, this.data[p].e);
}
}
} return t;
},
// 采用三元组表存储表示,求稀疏矩阵的转置矩阵t
/*
按照a.data中三元组的次序进行转置,并将转置后的三元组置入b中恰当的位置。
如果能预先确定矩阵M中每一列(即T中每一行)的第一个非零元在b.data中应有的位置,
那么在对a.data中的三元组依次做转置时,便可直接放到b.data中恰当的位置上去。
为了其额定这些位置,在转置前,应先求得M的每一列中非零元的个数,进而求得每一列的第一个非零元在b.data中应有的位置。
在此,需要设num和cpot两个变量。num[col]表示矩阵M中第col列中非零元的个数,
cpot[col]指示M中第col列的第一个非零元在b.data中的恰当位置。显然有:
cpot[0] = 1;
cpot[col] = cpot[col - 1] + num[col - 1] 2 <= col <= a.nu
*/
fastTransposeSMatrix: function(){
var t = new TSMatrix();
t.mu = this.nu;
t.nu = this.mu; if(this.data.length){
var num = [];
for(var col = 0; col < this.nu; col++)
num[col] = 0;
for(var i = 0; i < this.data.length; i++)
++num[this.data[i].j]; // 求矩阵中每一列含非零元个数
// 求第col列中第一个非零元在b.data中的序号
var cpot = [0];
for(col = 1; col < this.nu; col++)
// 上一列之前的序号+上一列的非零元个数 = 该列的序号
cpot[col] = cpot[col - 1] + num[col - 1];
for(var p = 0; p < this.data.length; p++){
col = this.data[p].j;
var q = cpot[col];
t.data[q] = new Triple(this.data[p].j, this.data[p].i, this.data[p].e);
// 给该列的序号+1,用作相同列数的情况
++cpot[col];
}
} return t;
}
}; var a1 = new Triple(1, 2, 12);
var a2 = new Triple(1, 3, 9);
var a3 = new Triple(3, 1, -3);
var a4 = new Triple(3, 6, 14);
var a5 = new Triple(4, 3, 24);
var a6 = new Triple(5, 2, 18);
var a7 = new Triple(6, 1, 15);
var a8 = new Triple(6, 4, -7); var matrix = new TSMatrix();
matrix.addTriple(a1);
matrix.addTriple(a2);
matrix.addTriple(a3);
matrix.addTriple(a4);
matrix.addTriple(a5);
matrix.addTriple(a6);
matrix.addTriple(a7);
matrix.addTriple(a8); console.log(matrix.transposeSMatrix());
console.log(matrix.fastTransposeSMatrix()); /*
三元组顺序表又称有序的双下标法,它的特点是,非零元在表中按行序有序存储,因此便于进行依行顺序处理的矩阵运算。
然而,若需按行号存取某一行的非零元,则从头开始进行查找。
*/ /**
* 行逻辑链接的顺序表
*
* 为了便于随机存取任意一行的非零元,则需知道每一行的第一个非零元在三元组表中的位置。
* 为此可将快速转置矩阵的算法中创建的,指示“行”信息的辅助数组cpot固定在稀疏矩阵的存储结构中。
* 称这种“带行链接信息”的三元组表为行逻辑链接的顺序表
*/ function RLSMatrix(mu, nu){
TSMatrix.apply(this, arguments);
this.rpos = [0];
}
RLSMatrix.MAXSIZE = 100;
RLSMatrix.prototype = {
constructor: RLSMatrix,
__proto__: TSMatrix.prototype,
// todo
/**
* 求矩阵乘积Q = M * N,采用行逻辑链接存储表示
* @param nMatrix
* @returns {RLSMatrix}
*/
multSMatrix: function(nMatrix){
if(this.nu !== nMatrix.mu) throw Error('nu is not equivalent to mu'); // 初始化Q
var qMatrix = new RLSMatrix(this.mu, nMatrix.nu);
// Q是非零矩阵
if(this.data.length * nMatrix.data.length !== 0){
// 处理M的每一行
for(var arow = 0; arow < this.mu; arow++){
// 当前行各元素累加器清零
var ctemp = [];
qMatrix.rpos[arow] = qMatrix.data.length + 1;
var tp, ccol; if(arow < this.mu)
tp = this.rpos[arow + 1];
else
tp = this.data.length + 1; //对当前行中每一个非零元找到对应元在N中的行号
for(var p = this.rpos[arow]; p < tp; p++){
var brow = this.data[p].j;
var t;
if(brow < nMatrix.mu)
t = nMatrix.rpos[brow + 1];
else
t = nMatrix.data.length + 1; for(var q = nMatrix.rpos[brow]; q < t; q++){
// 乘积元素在Q中的序号
ccol = nMatrix.data[q].j;
ctemp[ccol] = (ctemp[ccol] || 0) + this.data[p].e * nMatrix.data[q].e;
}
} // 压缩存储该行非零元
for(ccol = 1; ccol < qMatrix.nu; ccol++){
if(ctemp[ccol]){
if(++qMatrix.data.length > RLSMatrix.MAXSIZE) throw Error('overflow');
qMatrix.data[qMatrix.data.length - 1] = new Triple(arow, ccol, ctemp[ccol]);
}
}
}
} return qMatrix;
},
_calcPos: function clcPos(){
var num = [];
for(var col = 0; col < this.nu; col++)
num[col] = 0;
for(var i = 0; i < this.data.length; i++)
++num[this.data[i].j]; // 求矩阵中每一列含非零元个数
// 求第col列中第一个非零元在b.data中的序号
for(col = 1; col < this.nu; col++)
// 上一列之前的序号+上一列的非零元个数 = 该列的序号
this.rpos[col] = this.rpos[col - 1] + num[col - 1];
}
}; var b1 = new Triple(1, 1, 3);
var b2 = new Triple(1, 3, 5);
var b3 = new Triple(2, 2, -1);
var b4 = new Triple(3, 1, 2); var t1 = new RLSMatrix();
t1.addTriple(b1);
t1.addTriple(b2);
t1.addTriple(b3);
t1.addTriple(b4);
t1._calcPos(); var c1 = new Triple(1, 2, 2);
var c2 = new Triple(2, 1, 1);
var c3 = new Triple(3, 1, -2);
var c4 = new Triple(3, 2, 4); var t2 = new RLSMatrix();
t2.addTriple(c1);
t2.addTriple(c2);
t2.addTriple(c3);
t2.addTriple(c4);
t2._calcPos(); t1.multSMatrix(t2);
javascript实现数据结构: 稀疏矩阵之三元组线性表表示的更多相关文章
- 数据结构(Java描述)之线性表
基础概念 数据结构:是相互之间存在一种或多种关系的数据元素的集合. 逻辑结构和物理结构 关于数据结构,我们可以从逻辑结构和物理结构这两个维度去描述 逻辑结构是数据对象中数据元素之间的关系,是从逻辑意义 ...
- 数据结构与算法分析java——线性表1
说到线性结构的话,我们可以根据其实现方式分为三类: 1)顺序结构的线性表 2)链式结构的线性表 3)栈和队列的线性表 应用程序后在那个的数据大致有四种基本的逻辑结构: 集合:数据元素之间只有&qu ...
- [数据结构 - 第3章] 线性表之双向链表(C语言实现)
一.什么是双向链表? 双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域.所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前 ...
- 数据结构代码整理(线性表,栈,队列,串,二叉树,图的建立和遍历stl,最小生成树prim算法)。。持续更新中。。。
//归并排序递归方法实现 #include <iostream> #include <cstdio> using namespace std; #define maxn 100 ...
- 《数据结构》C++代码 线性表
线性表,分数组和链表两种(官方名称记得是叫顺序存储和链式存储).代码里天天用,简单写写. 首先是数组,分静态.动态两种,没什么可说的,注意动态的要手动释放内存就好了. 其次是链表,依旧分静态.动态.课 ...
- c语言实现基本的数据结构(一) 线性表
#include <stdio.h> #include <tchar.h> #include <stdlib.h> #define LIST_INIT_SIZE 1 ...
- [数据结构 - 第3章] 线性表之单链表(C++实现)
一.类定义 单链表类的定义如下: #ifndef SIGNALLIST_H #define SIGNALLIST_H typedef int ElemType; /* "ElemType类型 ...
- Java数据结构与算法(1):线性表
线性表是一种简单的数据类型,它是具有相同类型的n个数据元素组成的有限序列.形如如A0,A1,...,An-1.大小为0的表为空表,称Ai后继Ai-1,并称Ai-1前驱Ai. printList打印出表 ...
- 数据结构学习笔记 <1> 线性表
一.线性表的抽象数据类型描述 类型名:线性表(List) 数据对象集:线性表示n(>=0)个元素构成的有序序列(a1,a2,……,an) 操作集:线性表L∈List, 整数i表示位置,元素X∈ ...
随机推荐
- C# 委托的三种调用示例(同步调用、异步调用、异步回调)
首先,通过代码定义一个委托和下面三个示例将要调用的方法: 代码如下: public delegate int AddHandler(int a,int b); public class 加法类 { p ...
- Qt 学习之路 2(42):QListWidget、QTreeWidget 和 QTableWidget
Qt 学习之路 2(42):QListWidget.QTreeWidget 和 QTableWidget 豆子 2013年2月5日 Qt 学习之路 2 38条评论 上一章我们了解了 model/vie ...
- Vue 使用 axios post请求后台数据时 404
今天遇到Vue 使用 axios post请求后台数据时 404 使用postman 就能获取到 网上找了大半天 终于找到了解决方法,传送门:https://www.jianshu.com/p/b10 ...
- 理解Javascript_02_执行上下文02
上一篇我们讲到在全局环境下的代码段中,执行上下文环境中如何处理数据: 变量.函数表达式——变量声明,默认赋值为undefined: this——赋值: 函数声明——赋值: 这篇文章讲关于函数执行上下文 ...
- flask-restful基础
flask-restful基本使用 基本使用 from flask_restful import Api,Resource,reqparse,inputs from flask import Flas ...
- CF1012B Chemical table(构造)
[Luogu-CF1012B] 还有重题 P5089[eJOI2018]元素周期表 题解原话 : 可以发现这个过程是不改变二分图中的连通分量的个数的 答案就是 连通分量数-1 证明 : 设一行或一列为 ...
- 【前缀和】【two-pointer】【贪心】洛谷 P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题解
解法众多的一道毒瘤题? 题目描述 奶牛Bessie很喜欢闪亮亮的东西(Baling~Baling~),所以她喜欢在她的空余时间开采钻石!她现在已经收集了\(N\)颗不同大小的钻石,现在她想在谷 ...
- 将Eclipse的Java Project转换为Dynamic Web Project
在用Eclipse做JavaEE开发时经常遇到Web工程被识别为Java工程的问题,导致很多功能无法使用. 只需做以下操作便可解决该问题. 1.右击Java工程选择Properties 2.选择左边目 ...
- PIE SDK打开网络地图数据
1. 数据介绍 网络地图数据是在线地图服务发布出来的数据,其支持数据的网络查看和传输,极大的促进了GIS的发展. 目前PIE SDK支持百度地图.谷歌地图.高德地图.天地图.Bing地图.ArcGIS ...
- 日志收集之nxlog
一,软件介绍 nxlog 是用 C 语言写的一个开源日志收集处理软件,它是一个模块化.多线程.高性能的日志管理解决方案,支持多平台.可以处理来自许多不同来源的大量事件日志.支持的日志处理类型包括重写, ...