【数据结构】|第七章_数组和矩阵

7.1 数组

7.1.1 抽象数据类型

  • 数组的每个实例都是(索引,值)的数对集合
  • 有关数组操作:
    • 取值get(index):对一个给定的索引,取对应数对的值
    • 存值set(index,value):加入一个新数对。若索引相同的数对已经存在,则用新数对覆盖

7.1.2 C++数组的索引

在这里插入图片描述

7.1.3 行主映射和列主映射

  • 目的:将数组多维下标映射到一维空间上

先将数组进行排列,第一个坐标相同的索引位于同一行,第二个坐标相同的索引位于同一列

  • 行主映射: 从第一行开始,依次对每一行的索引从左至右编号。索引对应的数为行主次序
  • 列主映射: 从第一列开始,依次对每一行的索引从上至下编号。索引对应的数为列主次序

二维数组:在这里插入图片描述

三维数组
在这里插入图片描述

行主描述和列主描述

  • 将多维数组映射到一维数组上,利用映射函数来访问多维数组的元素
  • 哪一个更快?
    • 取决于是先用一维映射函数定位指针,在用指针定位元素的方法快
    • 还是用二维映射函数定位元素的方法快

7.1.4 用数组的数组来描述

  • 一个二维数组被表示成一个一维数组,这个一维数组的每一个元素还是一个一维数组
  • 一个三维数组被表示成一个一维数组,这个一维数组的每一个元素是一个二维数组

在这里插入图片描述

int main(int argc,char *argv[])
{
	int **x;
	//创建一个二维数组
	x=new int* [3];
	for(int i=0;i<3;i++)
		x[i]=new int [5];//一维数组的每一个元素还是一个一维数组
	int count=0;
	for(int i=0;i<3;i++)
		for(int j=0;j<5;j++)
			x[i][j]=count++;
			
	for(int i=0;i<3;i++)
		for(int j=0;j<5;j++)
			cout<<"x["<<i<<"]["<<j<<"]:"<<x[i][j]<<endl;

	for(int i=0;i<3;i++)
		delete [] x[i];
	delete []x;
	return 0;
}	

7.1.6不规则的二维数组

  • 不规则数组: 当一个二维数组右两行或更多的行,它们的元素个数不等时,这时的数组成为不规则数组
  • 二维数组是否规则,取决于每一行的元素个数是否相同,而元素的访问方式是相同的。

在这里插入图片描述

一个不规则二维数组的创建和使用

#include<iostream>
using namespace std;
int main(){
    int numberOfRows=5;
    //定义每一行的长度
    int length[5]={6,3,4,2,7};
    //声明一个二维数组变量
    //且分配所需要的行数
    int **irregularArray=new int* [numberOfRows];
    //分配每一行的空间
    for(int i=0;i<numberOfRows;i++){
        irregularArray[i]=new int [length[i]];
    }
    //像使用规则数组一样使用不规则数组
    irregularArray[2][3]=5;
    irregularArray[4][6]=irregularArray[2][3]+2;
    irregularArray[1][1]=3;

    //输出选择的数组元素
    cout<<irregularArray[2][3]<<endl;
    cout<<irregularArray[4][6]<<endl;
    cout<<irregularArray[1][1]<<endl;

    return 0;
}

7.2 矩阵

7.2.1 矩阵的定义和操作


在这里插入图片描述

矩阵转置

template<class T>
void transpose(T **a,int rows){
    //原地完成矩阵a[0:rows-1][0:rows-1]的转置
    for(int i=0;i<rows;I++)
        for(int j=i+1;j<rows;j++)
            swap(a[i][j],a[j][i]);
}

矩阵加法

template<class T>
void matrixAdd(T **a,T **b,T **c,int numberOfRows,int numberOfColumns){
    //将矩阵a,b相加得到c
    for(int i=0;i<numberOfRows;i++)
        for(int j=0;j<numberOfColumns;j++)
            c[i][j]=a[i][j]+b[i][j];
}

两个n*n矩阵相乘

template<class T>
void squareMatrixMultiply(T **a,T **b,T **c,int n){
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++){
            T sum=0;
            for(int k=0;k<n;k++)
                sum+=a[i][k]*b[k][j];
            c[i][j]=sum;
        }
}

一个m* n 矩阵与一个n *p矩阵相乘

template<class T>
void matrixMultiply(T **a,T **b,T **c,int m,int n,int p){
    for(int i=0;i<m;i++)
        for(int j=0;j<p;j++){
            T sum=0;
            for(int k=0;k<n;k++)
                sum+=a[i][k]*b[k][j];
            c[i][j]=sum;
        }
}

7.2.2 类matrix

int M[rows+1][cols+1]

  • 数组的0行0列不用
  • M(i,j):M[i][j]

矩阵类matrix的声明

  • 用一维数组element存储
  • 使用行主次序
  • 重载函数操作符()
  • 重载算术操作符:使能够用于矩阵对象
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;//元素数组
};

构造函数和复制构造函数

  • 构造函数

可以生成行数和列数都>=0的矩阵 (均为0/均不为0)

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];
}

  • 复制构造函数
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);
}

重载操作符=

赋值一个矩阵, M 1 = M 2 M_1=M_2 M1=M2

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;
}

重载操作符()

  • 根据下标取对应元素
    • M ( i , j ) = v a l u e M(i,j)=value M(i,j)=value
    • cout<<M(i,j)<<endl;
  • 行主映射: m a p ( i , j ) = ( i − 1 ) ∗ n + j − 1 map(i,j) = (i-1)*n+j-1 map(i,j)=(i1)n+j1
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];
}

重载+

维度必须相同

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;
}

重载*

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);
    //为三个矩阵参与乘法的数在对应一维数组上的位置,从0开始
    int ct = 0,cm = 0,cw = 0;
    //i=1;j=1;k=2
    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;
            ct -= theColumns-1;//左矩阵复位至行开头
            cm = j;//右矩阵前进至下列开头
        }
		ct += theColumns;//左矩阵前进至下行开头
        cm = 0;//右矩阵复位至首列开头
    }
	return w;
}

在这里插入图片描述

7.3 特殊矩阵

7.3.1 定义和应用

在这里插入图片描述
在这里插入图片描述

7.3.2 对角矩阵

  • 利用一维数组element[rows]表示对角矩阵
  • element[i-1]表示 D ( i , i ) D(i,i) D(i,i)

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; //存储对角元素的一维数组
};

template<class T>
diagonalMatrix<T>::diagonalMatrix(int theN){
    //构造函数
    //检验theN的值是否有效
    if(theN<1)
        throw illegalParameterValue("Matrix size must be > 0");

    n=theN;
    element=new T [n];
}

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;//非对角线上的元素
}

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三对角矩阵

在这里插入图片描述
在这里插入图片描述

三角矩阵的方法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 三角矩阵

在这里插入图片描述
在这里插入图片描述

  • 下三角矩阵
    在这里插入图片描述

set方法

template<class T>
void lowerTriangularMatrix<T>::set(int i,int j,const T& newValue){
    //给(i,j)元素赋新值
    //检验i和j的值是否合法
    if(i<1 || j<1 ||i>n||j>n)
        throw matrixIndexOutOfBounds();
    //在下三角且i>=j
    if(i>=j)
        element[i*(i-1)/2+j-1]=newValue;
    else
        if(newValue!=0)
            throw illegalParameterValue("element not in lower triangle must be zero");
}

7.3.5对称矩阵

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

7.4 稀疏矩阵

7.4.1基本概念

  • 定义:
    • 稀疏矩阵:一个m*n矩阵,大多数元素都为0
    • 稠密矩阵:不是稀疏的
  • 规定:稀疏矩阵非0数目小于 n 2 / 3 n^2/3 n2/3,有些情况下小于 n 2 / 5 n^2/5 n2/5
  • 对角矩阵、三对角矩阵:稀疏矩阵
  • 三角矩阵:稠密矩阵

7.4.2用单个线性表描述

  • terms标签的一行是矩阵非0元素在线性表中的索引
  • 线性表不仅存储矩阵非0元素,还储存行号、列号

在这里插入图片描述

类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);//矩阵转置后置于b中
		void add(sparseMatrix<T> &b, sparseMatrix<T> &c);//两个稀疏矩阵相加,结果存放于c
    private:
    	int rows;//矩阵行数
		int cols;//矩阵列数
		arrayList<matrixTerm<T>>terms;//非0项表
};

重载输出操作符<<

使用了从左至右的顺序迭代器,按行主次序提取非0元素,一行输出一个矩阵项

template <class T>
ostream& operator<<(ostream& out,sparseMatrix<T>& x)
{//将x放入输出流
	//输出矩阵特征
	out << "rows = "<< x. rows << " columns = "
	<< x. cols << endl;
	out << "nonzero terms = "<< x. terms. size() << endl;
	
//输出矩阵项, 一行一个
for(arrayList<matrixTerm<T>>::iterator i = x. terms. begin();
i != x. terms. end(); i++)
	out<< "a(" << (*i). ro w <<',' << (*i). col<< ") ="<< (*i). value << endl;
	return out;
}

重载输入操作符>>

template<class T>
istream& operator>>(istream& in, sparseMatrix<T>& x)
{//输入一个稀疏矩阵
	//输入矩阵特征
	int numberOfTerms;
	cout << "Enter number of rows, columns, and #terms"<< endl;
	in >> x. rows >> x. cols >> numberOfTerms;
	//这里应该检验输入的合法性, 留作练习

	//设置x. terms的大小,确保足够的容量
	x. terms. reSet(numberOfTerms);

	//输入项
	matrixTerm<T> mTerm;
	for (int i = 0; i < numberOfTerms; i++).
	{
		cout << "Enter row, column, and value of term"<< (i + 1) << endl;
		in >> mTerm. ro w >> mTerm. col >> mTerm. value;
		//这里应该检验输入的合法性,留作练习
		x. terms. set(i, mTerm);
	}
	return in;
}

矩阵转置

在这里插入图片描述
在这里插入图片描述

  • 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];
  • 算法思路
    • 计算*this每列中的非0元素数
    • 求出b中每一行的起始点
    • 实施从*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);	
    }
}

两个稀疏矩阵相加

在这里插入图片描述
在这里插入图片描述

  • 定义矩阵*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用多个线性表描述

  1. 表示方法:
    把每行非0元素串接在一起,构成一个链表,称为行链表
    在这里插入图片描述
  • 每个非阴影节点都代表稀疏矩阵的一个非0元素
  • 每个节点有两个域:element(数据域)、next(指针域)
    • element有两个子域:col(列号)、value(元素的值)
  • 一行至少有一个非0元素
  • 行链表的节点按列号升序链接在一起
  • 所有行链表(非阴影链表)被另一个链表(头节点链表)收集在一起
    • 头结点链表有两个域:element(数据域)、next(指针域)
      • element有两个子域:row(行号)、rowChain(指向行链表,rowChain.firstNode指向行链表第一个非阴影节点)
  • 头节点链表的节点按行号升序的顺序链接在一起
  • 每个节点可视为行链表的头节点
  • 空的头节点链表表示没有非0元素的矩阵

一个稀疏矩阵的转置

  • 使用箱子从输入矩阵*this中收集那些在结果矩阵中位于同一行的非0元素
  • bin[i]:结果矩阵b的第i行非0元素所对应的链表

稀疏矩阵的转置

  • while循环,按行主次序,对头结点从上到下,行链表从左到右迭代
    • 使用头节点链表迭代器ih和行链表迭代器ir
    • 将*this每一个元素都加到相应的箱子链表
  • for循环收集箱子链表,生成头节点链表
template<class T>
void linkedMatrix<T>::transpose(linkedMatrix<T> &b)
{//将 *this 的转置在矩阵b中返回
	b. headerChain. clear(); //从b中删除所有节点
	
	//创建bins以收集b的行
	extendedChain<rowElement<T> > *bin;
	bin = new extendedChain<rowElement<T> > [cols + 1];
	
	//头节点迭代器
	extendedChain<headerElement<T> >::iterator
	ih = headerChain. begin(),
	ihEnd = headerChain. end();

	//把*this 的项复制到bins
	while (ih != ihEnd)
	{//检查所有行
		int r = ih->row; //行链表的行数

		//行链表迭代器
		extendedChain<rowElement<T> >::iterator
		ir = ih->rowChain. begin(),
		irEnd = ih->rowChain. end();
		
		rowElement<T> x;
		//将*this 中行r中的项复制到b中的列r
		x. col = r;
		
		while (ir != irEnd)
		{//把行链表的一项复制到bin
			x. value = ir->value;
			//x最终在转置矩阵的行ir->col中
			bin[ir->col]. push_back(x);
			ir++; //行中的下一项
		}
		ih++; //进入下一行
	}

	//设置转置矩阵的维数
	b. rows = cols;
	b. cols = rows;
	
	//收集转置矩阵的头链表
	headerElement<T> h;
	//扫描bins
	for (int i = 1; i <= cols; i++)
		if (!bin[i]. empty())
		{//转置矩阵的行i
			h. ro w = i;
			h. rowChain = bin[i];
			b. headerChain. push_back(h);
			bin[i]. zero(); //免于析构
		}
	h. rowChain. zero(); //免于析构
	delete[] bin;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值