矩阵的转置

稀疏矩阵:

当一个矩阵中的很多元素都是零,而且非零元素的分布没有规律时,该矩阵称为稀疏矩阵。

稀疏矩阵的压缩存储方法:

从稀疏矩阵的概念,我们可以知道,稀疏矩阵的大多元素都是零。所以,我们只需要存储非零元素即可。这时,我们引入三元组表的概念:三元组表是一个线性表,线性表中的每一个结点对应稀疏矩阵的一个非零元素。结点包括三个域,分别为非零元素的行下标、列下标和值。并且,结点按矩阵的行优先顺序排列。

【例】存在矩阵A。那么矩阵A是怎么存储的呢?


A矩阵的三元组表:索引为0时,存储的是稀疏矩阵的行数、列数和非零元素的个数。

索引

i

j

v

0

5

6

6

1

1

1

3

2

1

6

7

3

2

3

6

4

3

1

2

5

3

2

3

6

5

5

2

表中结点类型定义如下:(JAVA

package com.linearList.matrix;
/*
 *  说明: 三元组的结点描述 
 *  @author 秦霞爱朱剑锋
 */
public class Node {
    
    int i;            // 行下标
    int j;            // 列下标
    int value;        // 值
    
    public Node() {}
    
    public Node(int i, int j, int vlaue) {
        this.i = i;
        this.j = j;
        this.value = vlaue;
    }
}

矩阵类型定义如下:

package com.linearList.matrix;
/*
 *  说明:矩阵的压缩存储描述(行优先)
 */
public class Matrix {
	
	final int MAX = 10;			// 非零元素个数的最大值
	int rows, cols, vals;				// 分别为行、列、非零元素个数
	Node data[] = new Node[MAX];
	
	public Matrix(int rows, int cols, int vals) {
		this.rows = rows;
		this.cols = cols;
		this.vals = vals;
		// 初始化data[0]:data[0]的i,j,v分别存储稀疏矩阵的行数、列数和非零元素的个数
		data[0] = new Node(rows, cols, vals);
		// 申请剩余空间
		if(vals <= MAX) {
			for (int i = 1; i <= vals; i++) {
				data[i] = new Node();
			}
		}
	}
}

矩阵的转置运算:
          
A的三元组表

索引

i

j

v

0

5

6

6

1

1

1

3

2

1

6

7

3

2

3

6

4

3

1

2

5

3

2

3

6

5

5

2


B的三元组表

索引

i

j

v

0

5

6

6

1

1

1

3

2

1

6

7

3

2

3

6

4

3

1

2

5

3

2

3

6

5

5

2

也就是说,矩阵的转置实际上是三元组表的改变。

【算法一】

算法思路:

①将两个矩阵的行数和列数相互交换;

②将每个三元组中的ij互相调换;

③重排三元组之间的次序。

代码实现:(JAVA

// 稀疏矩阵的转置
public static Matrix transpose(Matrix a) {
    
    Matrix b = new Matrix(a.cols, a.rows, a.vals);        // 转置后的矩阵b
    int bindex = 1;
    for(int col = 1; col <= a.cols; col++) {        // 按a的列序转置
        for(int aindex = 1; aindex <= a.vals; aindex++) {    // 扫描整个三元组表
            if(a.data[aindex].j == col) {
                b.data[bindex].i = a.data[aindex].j;
                b.data[bindex].j = a.data[aindex].i;
                b.data[bindex].value = a.data[aindex].value;
                bindex++;
            }
        }
    }
    return b;
}

核心代码讲解:

①我们遍历矩阵a的列序,保证转置之后的矩阵行优先的原则。

②扫描整个三元组,找到当前列的结点。(此时,浪费了一定的时间。因为对于每一个col,我们都需要遍历整个三元组,直到与其对应的为止。)

算法评价:

上述算法是在二重循环内完成的,算法的时间复杂度为O(cols*vals)。当非零元素的个数值vals=cols*rows时,时间复杂度为O()。可见,该算法虽然节省了空间,但时间复杂度提高了。所以,上述算法只适用于当vals<<rows*cols(非零元素较少)的情况。

算法二:快速转置算法】

原理:

我们已经知道,矩阵的转置实际上是改变其对应的三元组。那么,我们事先能不能知道改变之后的三元组是什么样子呢?答案是肯定的。进一步,既然我们已经知道了转置之后的三元组,那么对于索引为1的第一个非零元素,我们可不可以直接确定它转置之后再哪一个位置呢?同理,索引为2的第二个非零元素,我们能不能直接确定它转置之后的位置呢?以此类推,如果我们知道了这样一个映射关系,我们就可以做到直接定位,把结点插入到新的位置。

举个例子:

我们去图书馆找书的时候,我们不会从图书馆的第一个书架开始,一本一本的去找。我们是怎么做的呢?根据书的编号,参考图书馆存放书的信息,直接定位到相应的书架去找!那么,在这个例子中,图书馆为大家提供的馆藏信息表(记录不同的书的存放位置)就显的很重要了。这个表,实际上就是一个映射关系。

那么,如果我们也构建这样一个映射关系表,是不是也可以做到直接定位了呢?答案是肯定的。

如何构建这样一个映射关系?

第一列的元素转置之后在第一行。那么如果第一列的非零元素有两个,那么他们肯定占据的是索引12位置。同理,第二列的非零元素如果有两个,占据的应该是索引34位置。以此类推,我们便可以确定转置之后的位置了。因此,我们需要记录:每一列非零元素的个数num[col]以及该列第一个非零元素的起始索引位置cpot[col]

这里,还是以矩阵A举例。

col

1

2

3

4

5

6

num[col]

2

1

1

0

1

1

cpot[col]

1

3

4

 

5

6

从表格我们可以发现:cpot[col]=cpot[col-1]+num[col]

算法思路:

①构建映射关系。即初始化num[col]cpot[col]

②将结点放到指定的位置。

代码实现:

// 稀疏矩阵的快速转置算法
public static Matrix fastTranspose(Matrix a) {
	Matrix b = new Matrix(a.cols, a.rows, a.vals);
	int num[] = new int[a.cols + 1];
	// 初始化 num[]
	for(int i = 1; i < a.vals; i++) {
		++num[a.data[i].j];
	}
	int cpot[] = new int[a.cols + 1];
	// 初始化 cpot[]
	cpot[1] = 1;
	for(int col = 2; col < num.length; col++) {
		cpot[col] = cpot[col-1] + num[col];
	}
	for(int index = 1; index <= a.vals; index++) {
		int col = a.data[index].j;
		int newIndex = cpot[col];
		b.data[newIndex].i = a.data[index].j;
		b.data[newIndex].j = a.data[index].i;
		b.data[newIndex].value = a.data[index].value;
		cpot[col]++;
	}
	return b;
}

代码讲解:

这里,初始化数组的时候,多开辟一个空间,是因为三元数组表的第一个元素)(索引为0的位置)存储的是矩阵的行数、列数以及非零元素的个数。


(欢迎评论指导!转载时,请注明出处,谢谢。)

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值