数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵

文章目录

    • 7.1 数组
      • 7.1.1 抽象数据类型
      • 7.1.2 C++数组的索引
      • 7.1.3 行主映射和列主映射
      • 7.1.4 用数组的数组来描述
      • 7.1.5 行主描述和列主描述
    • 7.2 矩阵
      • 7.2.1 定义和操作
      • 7.2.2 类matrix
        • matrix类声明
          • matrix类的构造函数
          • matrix类的复制构造函数
          • matrix类对`()`的重载
          • matrix类对`=`的重载
          • matrix类对`+`的重载
          • matrix类对`*`的重载
    • 7.3 特殊矩阵
      • 7.3.1 定义和应用
      • 7.3.2 对角矩阵
        • 类diagonalmatrix声明
          • diagonalMatrix::get
          • diagonalMatrix:: set
      • 7.3.3 三对角矩阵
          • tridiagonalMatrix::get
      • 7.3.4 三角矩阵
      • 7.3.5 对称矩阵
    • 7.4 稀疏矩阵
      • 7.4.1 基本概念
      • 7.4.2 用单个线性表描述
        • 类sparseMatrix声明
          • 类sparseMatrix中的矩阵转置
          • 类sparseMatrix中的两个稀疏矩阵相加
      • 7.4.3 用多个线性表描述

7.1 数组

7.1.1 抽象数据类型

抽象数据类型 Array 
{
实例
	形如(index,value)的数对集合,其中任意两个数对的index值都各不相同. 操作
	get(index):返回索引为index的数对中的值
	set(index,value):加入一个新数对(index,value)
    	(如果索引index值相同的数对已存在,则用新数对覆盖)
}

7.1.2 C++数组的索引

  • K维数组的索引(或下标)
    • [ i 1 ] [ i 2 ] [ i 3 ] . . . [ i k ] [i_1][i_2][i_3]...[i_k] [i1][i2][i3]...[ik]
  • k维数组创建:
    • int score [ u 1 ] [ u 2 ] [ u 3 ] . . . [ u k ] [u_1][u_2][u_3]...[u_k] [u1][u2][u3]...[uk] u i u_i ui—正的常量或有常量表示的表达式)
  • 数组元素的个数:
    • n = u 1 u 2 u 3 . . . u k n=u_1u_2u_3...u_k n=u1u2u3...uk
  • 内存空间:
    • n x sizeof(int)字节.
  • c++ 编译器为数组预留空间:
    • start -----start + sizeof(score)-1

7.1.3 行主映射和列主映射

行主映射(按行的顺序对元素一一映射)和列主映射(按列的顺序对元素一一映射)
数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第1张图片

以二维数组举例

int s[ u 1 u_1 u1] [ u 2 u_2 u2] (令s[2] [3])

行主映射下: 列主映射下:
map( i 1 i_1 i1, i 2 i_2 i2) = u 2 u_2 u2 * i 1 i_1 i1 + i 2 i_2 i2 map( i 1 i_1 i1, i 2 i_2 i2) = u 1 u_1 u1 * i 2 i_2 i2 + i 1 i_1 i1
s[0] [0] s[0] [0]
s[0] [1] s[1] [0]
s[0] [2] s[0] [1]
s[1] [0] s[1] [1]
s[1] [1] s[0] [2]
s[1] [2] s[1] [2]

思考

  • 在C++使用的是行主映射

  • M 行 M_行 M= M 列 T M_列^T MT

  • k维数组行主映射:

    m a p ( i 1 , i 2 , . . . , i k ) = ( i 1 ∗ u 2 ∗ u 3 . . . u k ) + ( i 2 ∗ u 3 . . . u k ) + . . . + ( i k − 1 ∗ u k ) + ( i k ) map(i_1, i_2, ..., i_k) = (i_1*u_2*u_3 ... u_k)+(i_2*u_3 ... u_k)+...+(i_{k-1}*u_k)+(i_k) map(i1,i2,...,ik)=(i1u2u3...uk)+(i2u3...uk)+...+(ik1uk)+(ik)

  • k维数组列主映射:

    m a p ( j 1 , j 2 , . . . j k ) = ( j 1 ∗ u 2 ∗ u 3 . . . u k ) + ( j 2 ∗ u 3 . . . u k ) + . . . + ( j k − 1 ∗ u k ) + ( j k ) map(j_1,j_2,...j_k)= (j_1*u_2*u_3 ... u_k)+(j_2*u_3 ... u_k)+...+(j_{k-1}*u_k)+(j_k) map(j1,j2,...jk)=(j1u2u3...uk)+(j2u3...uk)+...+(jk1uk)+(jk)
    注意,超过两维就没有列的概念了,因此高维的行、列主映射公式通用。

7.1.4 用数组的数组来描述

多维数组可以用数组的数组描述数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第2张图片

  • 占用空间:4×3×1 + 4×5×3

  • C++定位x[i] [j]的过程:

    • 利用一维数组的映射函数找到指针x[i]

    • 利用一维数组的映射函数找到指针x[i]所指的第i行中索引为j的元素

7.1.5 行主描述和列主描述

数组y 以下就是int s[3] [5]的行主描述 以下就是int s[3] [5]的列主描述
y[0] s[0] [0] s[0] [0]
y[1] s[0] [1] s[1] [0]
y[2] s[0] [2] s[2] [0]
y[3] s[0] [3] s[0] [1]
y[4] s[0] [4] s[1] [1]
y[5] s[1] [0] s[2] [1]
y[6] s[1] [1] s[0] [2]
y[7] s[1] [2] s[1] [2]
y[8] s[1] [3] s[2] [2]
y[9] s[1] [4] s[0] [3]
y[10] s[2] [0] s[1] [3]
y[11] s[2] [1] s[2] [3]
y[12] s[2] [2] s[0] [4]
y[13] s[2] [3] s[1] [4]
y[14] s[2] [4] s[2] [4]
  • 占用空间:3×5×4
  • 定位x[i] [j]的过程(以行主列为例):
    • 利用二维数组的映射函数(map( i 1 i_1 i1, i 2 i_2 i2) = u 2 u_2 u2 * i 1 i_1 i1 + i 2 i_2 i2)计算x[i] [j]在一维数组y中的位置u
    • 利用一维数组的映射函数(map( i 1 i_1 i1)= i 1 i_1 i1)访问元素y[u]

7.2 矩阵

7.2.1 定义和操作

m×n矩阵:m行和n列的表

M(i,j):矩阵M中第i行、第j列的元素

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第3张图片

矩阵相乘直观表示(* ^ ▽ ^ *)

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第4张图片

7.2.2 类matrix

数据描述:用于一个一维数组element,按行主次序存储

M:m×n matrix

  • 矩阵元素M(i,j)在数组element中的位置
  • map(i,j) = (i-1)*n+j-1
matrix类声明

我们要重载函数操作符(),使得在程序中对矩阵索引的用法和在数学中的一样。我们还要重载算术操作符,使它们能够用于矩阵对象。

template<class T>
class matrix 
{
	friend ostream& operator<<(ostream&,const matrix<T>&);
    public:
		matrix(int theRows=0,int theColumns=0);
    	matrix(const matrix<T>&);//复制构造函数
        ~matrix(){delete[]element;}
    	int rows()const {return theRows;}
    	int columns()const {return theColumns;}
    	T& operator()(int i, int j) const;
		matrix<T>& operator=(const matrix<T>&);
    	matrix<T> operator+()const//一元加法
        matrix<T> operator+(const matrix<T>&)const;
    	matrix<T> operator-()const//一元减法
		matrix<T> operator-(const matrix<T>&)const;
		matrix<T> operator*(constmatrix<T>&)const;
    	matrix<T>&operator+=(const T&x);
    private:
		int theRows,theColumns;//矩阵行数和列数
		T*element;//元素数组
};
matrix类的构造函数
template<class T>
matrix<T>::matrix(int theRows,int theColumns)
{
	//检验行数和列数的有效性
	if(theRows <0 || theColumns <0)
        throw illegalParameterValue("行列数必须大于等于0");
	if(theRows ==0 || theColumns == 0 && (theRows !=0|| theColumns !=0))
		throw illegalParameterValue("要么都大于0,要么都等于0");
	//创建矩阵
	this-> theRows = theRows;
    this-> theColumns = theColumns;
    element=new T [theRows* theColumns];
}
matrix类的复制构造函数
template<class T>
matrix<T>::matrix(const matrix<T>& m)
{
    //创建矩阵
    theRows = m.rows;
	theColumns = m.theColumns;
	element= new T [theRows* theColumns];
    //复制m的每一个元素
    copy (m.element,m.element + theRows* theColumns, element);
}
matrix类对()的重载

矩阵元素M(i,j)在数组element中的位置map(i,j) = (i-1)*n+j-1

template<class T>
T& matrix<T>::operator()(int i,int j)const 
{
    //返回一个指向元素(i,j)的引用
    if(i<1||i>theRows||j<1|j>theColumns)
		throw matrixIndexOutOfBounds();
	return element[(i-1)*theColumns +j-1];
}
matrix类对=的重载
template<class T>
matrix<T>& matrix<T>::operator=(const matrix<T>& m)
{
    //赋值,*this=m 
    if(this != &m)
    {//不能自己赋值自己
        //释放原空间
		delete [] element;
        theRows = m.theRows;
		theColumns = m.theColumns;
		element = new T [theRows* theColumns];
        //复制m的每一个元素
        copy (m.element,m.element + theRows* theColumns, element);
    }
    return *this;
}
matrix类对+的重载
template<class T>
matrix<T>matrix<T>::operator + (const matrix<T>& m)const 
{
    //返回矩阵w=(*this)+m
    if(theRows != m.theRows || theColumns != m.theColumns)
        throw matrixSizeMismatch();
	//生成结果矩阵w
	matrix<T> w(theRows, theColumns);
    for(int i = 0;i < theRows * theColumns;i++)
		w.element[i] = element[i] + m.element[i];
    return w;
}
matrix类对*的重载
template<class T>
matrix<T>matrix<T>::operator * (const matrix<T>& m)const 
{
    //返回矩阵w=(*this)*m
    if(theColumns != m.theRows)
        throw matrixSizeMismatch();
	//生成结果矩阵w
	matrix<T> w(theRows, m.theColumns);
    //为*this,m和w定义游标,并设定初始位置为(1,1)
    int ct = 0,cm = 0,cw = 0;
    
    /*最外层的循环对应不同行和不同列的计算,一行的所有列都好了就换一行*/
    for (int i = 1;i <= theRows;i++)
    {
        /*第二层的循环对应一行和不同列的计算,一列完了换一列*/
		for (int j = 1;j <= m.theColumns; j++)
		{
			T sum = element[ct] * m.element[cm];
            /*最内层的循环实现一行对应一列的计算*/
			for(int k=2;k<=theColumns;k++)
			{
                //累加其余项
                ct++//指向*this第i行的下一项
                /*在行主次序中同一列的两个相同元素在位置上相差m.theColumns*/
				cm += m.theColumns;//指向m的第j列的下一项
                sum += element[ct] * m.element[cm];
            }
            w.element[cw++] = sum;//保存w(i,j)
            ct -= theColumns-1;//第i行行首
            cm = j;//第j+1列起始
        }
		ct += theColumns;//重新调整至下一行的行首
        cm = 0;//重新调整至第一列起始
    }
	return w;
}

7.3 特殊矩阵

7.3.1 定义和应用

方阵是指具有相同行数和列数的矩阵

特殊矩阵

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第5张图片

7.3.2 对角矩阵

对角矩阵:D

矩阵描述:T element[n]数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第6张图片

类diagonalmatrix声明
template<class T>
class diagonalMatrix 
{
    public:
		diagonalMatrix(int theN=10);//构造函数
        ~diagonalMatrix(){delete []element;} //析构函数
    	T get(int,int)const;
    	void set(int,int,const T&);
    private:
		int n;//矩阵维数
		T*element; //存储对角元素的一维数组
};
diagonalMatrix::get
template <class T>
T diagonalMatrix<T>::get(int i,int j)const 
{
    //返回矩阵中(i,j)位置上的元素. 
    //检验i和j是否有效
	if (i<1 || j<1 || i>n || j>n) throw matrixIndexOutOfBounds();
	if(i==j)
		return element[i-1];//对角线上的元素
	else return 0;//非对角线上的元素
}
diagonalMatrix:: set
template<class T>
void diagonalMatrix<T>:: set(int i, int j, const T& newValue)
{
    //存储矩阵中位置(i,j)上元素的新值. 
    //检验i和j是否有效
	if(i<1 || j<1 || i>n || j>n) throw matrixIndexOutOfBounds();
	if(i==j)
		element[i-1] = newValue;//存储对角元素的值
    else 
		if(newValue!= 0)
            throw illegalParameterValue("非对角线上的元素必须是0");
}

7.3.3 三对角矩阵

三对角矩阵:T

三条对角线:

  • 主对角线:i = j

  • 低对角线(主对角线之下的对角线):i = j + 1

  • 高对角线(主对角线之上的对角线):i = j - 1

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第7张图片

补充:按对角线映射

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第8张图片

tridiagonalMatrix::get
template <class T>
T tridiagonalMatrix<T>::get(int i,int j)const 
{
    //返回矩阵中(i,j)位置上的元素. 
    //检验i和j是否有效
	if(i <1 || j<1 || i>n || j>n) throw matrixIndexOutOfBounds();
    //确定要返回的元素
    switch (i-j)
    {
     	case 1://低对角线
			return elelment[i-2];
     	case 0://主对角线
            return elelment[n+i-2];
        case -1://高对角线
            return elelment[2*n+i-2];
        default: return 0;
    }
 }

7.3.4 三角矩阵

非0区域的元素总数:n(n+1)/2

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第9张图片

7.3.5 对称矩阵

一个n×n对称矩阵,可以视为下三角或上三角矩阵,用三角矩阵的表示方法,用一个大小为n(n+1)/2的一维数组来表示。未存储的元素可以由存储的元素来计算。

7.4 稀疏矩阵

7.4.1 基本概念

  • 如果一个m×n矩阵中有“许多”元素为0,则称该矩阵为稀疏矩阵(sparse)。

  • 不是稀疏的矩阵被称为稠密矩阵(dense)。

  • 稀疏矩阵和稠密矩阵之间没有一个精确的界限:

    • 我们规定若一个矩阵是稀疏矩阵,则其非0元素的数目应小于 n 2 / 3 n^2/3 n2/3,在有些情况下应小于 n 2 / 5 n^2/5 n2/5
    • 对角矩阵,三对角矩阵(n×n):稀疏矩阵
    • 三角矩阵:稠密矩阵

7.4.2 用单个线性表描述

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第10张图片

线性表terms采用数组描述(terms是arrayList的实例)数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第11张图片

类sparseMatrix声明
template <class T>
struct matrixTerm
{
	int row;
	int col;
	T value;
	operator T() const { return value;}
};
template<class T>
class sparseMatrix 
{
    public:
		void transpose(SparseMatrix<T> &b);//矩阵转置
		void add(sparseMatrix<T> &b, sparseMatrix<T> &c);//两个稀疏矩阵相加
    private:
    	int rows;//矩阵行数
		int cols;//矩阵列数
		arrayList<matrixTerm<T>>terms;//矩阵非0元素表
};
类sparseMatrix中的矩阵转置

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第12张图片

关键点

矩阵*this的转置矩阵→b

矩阵*this中的元素在b中的位置?

  • colSize[i]:*this第i列的非0元素数
  • rowNext[i]:*this第i列(b中第i行)下一个元素在b中的位置
    • 初始:b中第i行的起始位置
      • rowNext[1] = 0;
      • rowNext[i] = rowNext[i - 1]+colSize[i - 1];

算法思路

  1. 计算*this每列中的非0元素数

  2. 求出b中每一行的起始点

  3. 实施从*this到b的转置复制

    定义i为* this迭代器
    依次扫描* this各元素( *i)

template<class T>
void sparseMatrix<T>::transpose(sparseMatrix<T>&b) 
{//把*this转置结果送入b
    
    //设置转置矩阵特征
    b.cols = rows;
    b.rows = cols;
    b.terms.reSet(terms.size());
    
    //初始化以实现转置
    int* colSize = new int[cols + 1];
    int* rowNext = new int[cols + 1]; 

    //计算*this每一列的非0元素数
    for (int i = 1; i <= cols; i++) 
        colSize[i] = 0;
    for (arrayList<matrixTerm<T>>::iterator i = terms.begin();
         i!=terms.end();i++)
        colSize[(*i).col]++;
    
    //求出b中每一行的起始点
    rowNext[1] = 0;
    for (int i = 2; i <= Cols; i++)
        rowNext[i] = rowNext[i - 1] + colSize[i - 1];
    
    //实施从*this到b的转置复制
    matrixTerm<T> mTerm;
    for(arrayList<matrixTerm<T>>::iterator i = terms.begin();
       i!= terms.end();i++)
    {
        int j = rowNext[(*i).col]++;//在b中的位置
        mTerm.row = (*i).col;
        mTerm.col = (*i).row;
        mTerm.value = (*i).value; 
        b.terms.set(j,mTerm);	
    }
}
类sparseMatrix中的两个稀疏矩阵相加

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第13张图片

两个稀疏矩阵(*this和b)相加,所得结果放入c中。

实现思想:

  • 定义矩阵*this的迭代器:it,和矩阵b的的迭代器ib
  • 使用it和ib,从左至右依次扫描两个矩阵中的元素。
  • 当it和ib都未扫描完成时,循环:
    • 计算it所指的元素和ib所指的元素按行主次序的索引。
      • tIndex=*this中的it所指的元素索引
      • 元素索引——(元素的行下标-1)* 列数+元素的列下标
      • bIndex =b中ib所指的元素索引
    • 判断tIndex>bIndex 还是tIndex=bIndex,确定it所指的元素是在ib所指的元素之前,之后还是进行相加运算,并只在和不为0时才加入c。
  • 复制剩余元素
template<class T>
void sparseMatrix<T>::Add(sparseMatrix<T>&b,sparseMatrix<T>&c)
{//计算c=(*this)+b. 
    //检验相容性
	if (rows != b.rows || cols != b.cols)
		throw matrixSizeMismatch();//不能相加
	//设置结果矩阵c的特征
    c.rows = rows;
    c.cols = cols;
	c.terms.clear() = 0;//初值
    int cSize = 0;
	//定义*this和b的迭代器
	arrayList<matrixTerm<T>>::iterator it = terms.begin();
    arrayList<matrixTerm<T>>::iterator ib = b.terms.begin();
    arrayList<matrixTerm<T>>::iterator itEnd = terms.end();
    arrayList<matrixTerm<T>>::iterator ibEnd = b.terms.end();
	//遍历*this和b,把相关的元素值相加
    while(it!=itEnd && ib!= ibEnd)
	{
        //每一个元素的行主索引+列数
        int tIndex = (*it).row * cols + (*it).col;
        int bIndex = (*ib).row * cols + (*ib).col;
        if(tIndex < bIndex)//b的元素在后
		{
            //*this的下一个元素
            c.terms.insert(cSize++,*it);
            it++;
        }
        else 
        {
            if (tIndex == bIndex)//位置相同
			{
                //仅当和不为0时才添加到c中
                if((*it).value + (*ib).value != 0)
                {
                    matrixTerm<T>mTerm;
                    mTerm.row = (*it).row;
                    mTerm.col = (*it).col;
					mTerm.value =(*it).value +(*ib).value;
                    c.terms.insert(cSize++,mTerm);
                }
                //*this和b的下一个元素
                it++;
                ib++;
            }
        	else
            {//b的下一个元素
                c.terms.insert(cSize++,*ib);
                ib++;
            }
        }
    }
	//复制剩余元素
    for(;it != itEnd; it++)
        c.terms.insert(cSize++,*it);
    for(;ib != ibEnd; ib++)
        c.terms.insert(cSize++,*ib);
}

7.4.3 用多个线性表描述

数据结构 | 第七章:数组和矩阵 | 行主映射和列主映射 | 稀疏矩阵_第14张图片

你可能感兴趣的:(数据结构,算法与应用,#,数据结构笔记合集,矩阵,c++,算法,数据结构)