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)=(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];
}
重载+
维度必须相同
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用多个线性表描述
- 表示方法:
把每行非0元素串接在一起,构成一个链表,称为行链表
- 每个非阴影节点都代表稀疏矩阵的一个非0元素
- 每个节点有两个域:
element
(数据域)、next
(指针域)- element有两个子域:
col
(列号)、value
(元素的值)
- element有两个子域:
- 一行至少有一个非0元素
- 行链表的节点按列号升序链接在一起
- 所有行链表(非阴影链表)被另一个链表(头节点链表)收集在一起
- 头结点链表有两个域:
element
(数据域)、next
(指针域)- element有两个子域:
row
(行号)、rowChain
(指向行链表,rowChain.firstNode
指向行链表第一个非阴影节点)
- element有两个子域:
- 头结点链表有两个域:
- 头节点链表的节点按行号升序的顺序链接在一起
- 每个节点可视为行链表的头节点
- 空的头节点链表表示没有非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;
}