数据结构与算法(5)--数组
Ch5 数组
0x01 数组
基本概念
数组是n个相同数据类型的数据元素构成的占用一块地址连续的内存单元的有限序列。
数组中任意一个元素可以用该元素在数组中的位置来表示,数组元素的位置通常称作数组的下标。
数组与线性表比较:
同:都是若干相同数据类型的数据元素构成的有限序列
异:
- 数组占用连续内存空间,线性表不一定
- 线性表的元素是逻辑意义上不可再分的元素,而数组中每个元素还可以是数组
- 数组的操作主要是存取,与线性表的插入、删除操作不同。
线性结构(包括线性表、堆栈、队列、串)的顺序存储结构实际就是使用数组来存储。数组是其他数据结构实现顺序存储结构的基础,是软件设计中基础的数据结构。
数组的实现,在C++
中采用行主序的存放方法,即行优先顺序。一个m×n的二维数组可以看成是m行的一维数组。
动态数组类
静态数组在定义时就给出元素个数,动态数组是在具体申请存储单元空间时才给出数组元素的个数。
C++语言建立动态数组的运算符是new
,销毁动态数组的运算符是delete
。
类定义
template <class T> //类的定义
class Array
{
private:
T* arr; //数组
int size; //数组个数
public:
Array(int sz = 100); //构造函数
Array(const Array<T>& a); //拷贝构造函数
~Array(void); //析构函数
int Size(void)const; //取数组元素个数
void operator=(const Array<T>& a); //赋值
T& operator[](int i); //下标
void Resize(int sz); //重置数组
};
类的实现
template <class T>
Array<T>::Array(int sz) //构造函数
{
if (sz <= 0)
{
cout << "无效的数组元素个数" << endl;
exit(0);
}
arr = new T[sz]; //申请内存空间
size = sz; //置数组元素个数
}
template <class T>
Array<T>::Array(const Array<T>& a) //拷贝构造函数
{
arr = new T[a.size]; //申请内存空间
//数组元素赋值
for (int i = 0; i < a.size; i++)
arr[i] = a.arr[i];
size = a.size; //置数组元素个数
}
template <class T>
Array<T>::~Array(void) //析构函数
{
delete[]arr; //释放内存空间
}
template <class T>
int Array<T>::Size(void)const //取数组元素个数
{
return size;
}
template <class T>
void Array<T>::operator=(const Array<T>& a) //赋值运算符重载
{
delete arr; //释放原内存空间
arr = new T[a.size]; //申请新内存空间
//数组元素赋值
for (int i = 0; i < a.size; i++)
arr[i] = a.arr[i];
size = a.size; //置数组元素个数
}
template <class T>
T& Array<T>:: operator[](int i) //下标运算符重载
{
if (i < 0 || i > size - 1)
{
cout << "下标越界" << endl;
exit(0);
}
return arr[i];
}
template <class T>
void Array<T>::Resize(int sz) //重置数组
{
if (sz <= 0) { cout << "无效的数组个数" << endl; exit(0); } if (sz == size) return; T* newArray = new T[sz]; //申请新数组空间
int n = (sz <= size) ? sz : size; //原数组元素拷贝
for (int i = 0; i < n; i++) newArray[i] = arr[i]; delete[]arr; //释放原数组空间
arr = newArray; //新数组指针赋值
size = sz; //置新数组个数
}
测试程序
#include <iostream>
using namespace std;
#include"Array.h"
int main()
{
Array<int> a(10);
int n = 10;
for (int i = 0; i < n; i++) a[i] = i + 1;
cout << "a[6] = " << a[6] << endl;
cout << "Size of a = " << a.Size() << endl;
Array<int> b = a;
cout << "b[6] = " << b[6] << endl;
cout << "Size of b = " << b.Size() << endl;
a.Resize(40);
a[21] = 21;
cout << "a[21] = " << a[21] << endl;
cout << "Size of a = " << a.Size() << endl;
Array<int> c(a);
cout << "c[21] = " << c[21] << endl;
cout << "Size of c = " << c.Size() << endl;
system("pause");
return 0;
}
结果如下:
a[6] = 7
Size of a = 10
b[6] = 7
Size of b = 10
a[21] = 21
Size of a = 40
c[21] = 21
Size of c = 40
请按任意键继续. . .
0x02 特殊矩阵
特殊矩阵指有许多值相同的元素或有许多零元素、且值相同的元素或零元素的分布有一定规律的矩阵。
几种特殊矩阵的压缩存储
- n阶对称矩阵
关于主对角线对称,所以可以两个对称元素共享存储空间,节省一半存储空间。主要实现方法是通过数学映射关系:
- n阶三角矩阵
以主对角线划分,n阶三角矩阵有n阶上三角矩阵(下三角元素均为0或其他常数)和n阶下三角矩阵两种。
与n阶对称矩阵类似,可以通过数学公式映射来达到共享存储空间的目的。
0x03 稀疏矩阵
稀疏矩阵:矩阵中非零元素的个数远远小于矩阵元素个数
稠密矩阵:一个不稀疏的矩阵
稀疏矩阵压缩存储方法:只存储稀疏矩阵中的非零元素。
实现方法:每个非零元素用一个三元组(i,j,aij)来表示,每个稀疏矩阵可用一个三元组线性表来表示。(其中i和j代表行和列,aij代表对应的元素值)。
稀疏矩阵的压缩存储结构主要有三元组顺序表和三元组链表两大类型
struct DataType
{ int row; //行号
int col; //列号
ElemType value; //元素值
} ;
稀疏矩阵的三元组顺序表
一个稀疏矩阵的三元组线性表的存储结构如下图所示,其中右下角代表了矩阵的行数,列数和非零元素数。
三元组顺序表类的定义和实现与顺序表类似,只是继承了顺序表类,同时元素改为了上述结构体,增加了矩阵转置函数。
矩阵的转置运算是把矩阵中每个元素的行号值转为列号值,把列号值转为行号值。
原稀疏矩阵三元组顺序表按先行序后列序的次序存放,若要求转置后的稀疏矩阵三元组顺序表仍然按先行序后列序的次序存放。那么成员函数可以设计如下:
void SeqSpaMatrix::Transpose(SeqSpaMatrix& a)
{
if(dNum == 0) return;
else
{
int i, j, k;
i = 0; //i为a.list[]的下标值
for(k = 1; k <= cols; k++) //k为原矩阵的列下标
{
for(j = 0; j < a.dNum; j++) //j为list[]的下标值
{
if(list[j].col == k) //寻找原矩阵中列下标最小值,对于所有的元素,依次判断列下标是否是当前最小值
{
a.list[i].row = list[j].col; //列号转为行号
a.list[i].col = list[j].row; //行号转为列号
a.list[i].value = list[j].value; //数组元素复制
i++;
}
}
}
}
}
稀疏矩阵的三元组链表
(1)三元组链表
用链表存储的三元组线性表。
稀疏矩阵三元组线性表的带头结点的三元组链表结构如图所示,其中头结点的行号域存储了稀疏矩阵的行数,列号域存储了稀疏矩阵的列数。
缺点是实现矩阵运算操作算法的时间复杂度高,因为算法中若要访问某行某列中的一个元素时,必须从头指针进入后逐个结点查找。由此提出了下面的三元组链表。
(2)行指针数组结构的三元组链表
把每行非零元素三元组组织成一个单链表,再设计一个指针类型的数组存储
所有单链表的头指针。
各单链表均不带头结点。由于每个单链表中的行号域数值均相同,所以单链表中省略了三元组的行号域,而把行号统一放在了指针数组的行号域中。
但是对于从某列进行查找不方便,因此又提出了三元组十字链表结构。
(3)三元组十字链表
把非零元素三元组按行和按列组织成单链表,这样稀疏矩阵中的每个非零元素三元组结点都将既在行单链表上,又在列单链表上,形成十字形状。
具体实现略。