数据结构基础(一)
有一个等式,数据结构+算法=程序,说明了数据结构对于计算机程序设计的重要性。数据结构是指数据元素的集合(或数据对象)及元素间的相互关系和构造方法。数据对象中元素之间的相互关系称为数据的逻辑结构,数据元素及元素之间关系的存储形式称为存储结构(或物理结构)。
数据结构按照逻辑关系的不同,分为线性结构和非线性结构两种,线性结构又可分为树结构和图结构。
1.1 数组
1.数组的定义和基本运算
数组是程序中最常用的数据结构,数组的本质是内存中一段大小固定,地址连续的存储单元。
一维数组是一个长度固定,下标有序的线性序列。二位数组则是一个矩阵结构,本质上是以数组作为数组元素的数组,即“数组的数组”。以二维数组A[m,n]为例,其结构如图2-1所示:

A[m,n]可以看做一个行向量形式的线性序列:
Am,n =[[a11,a12…a1n],[ a21,a22…a2n],…,[ am1,am2…amn]];
也可以看做一个列向量形式的线性序列
Am,n =[[a11,a21…am1],[ a12,a22…am2],…,[ a1n,a2n…amn]];

二维数组的本质是一维数组的数组
数组的结构特点:
数组元素数目固定,一旦定义不可改变。
数组中的元素具有相同的类型。
数组下标具有上下界的约束且有序。
数组的两个基本运算:
给定一组下标,存取相应的数据元素。
给定一组下标,更改相应元素的值。
在程序设计语言中,把数组看做是具有共同名字的相同类型的多个变量的集合。
2. 数组元素的存储
数组适合采用顺序存储结构,对于数组一旦确定了其维数和各维的长度,便可分配存储空间。所以,只要给出一组下标便可求出相应数组元素的存储位置,在数组的顺序存储结构中,数组元素的位置和其下标呈线性关系。

二维数组的存储结构可分为以行为主存储和以列为主存储两种方式

设每个数组元素占用L个单元,m,n为数组的行数和列数,Loc(a11)表示元素a11的地址,
以行为主:
Loc(aij)=Loc(a11)+(i-1) ×n+(j-1) ×L
以列为主:
Loc(aij)=Loc(a11)+(j-1) ×m+(i-1) ×L
推广至多维数组,按下标顺序(以行为主)存储时,先排最右的下标,从右至左到最左下标,而逆下标顺序正好相反。
3.矩阵
在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合 [1] ,最早来自于方程组的系数及常数所构成的方阵。这一概念由19世纪英国数学家凯利首先提出。在数据结构中,主要讨论如何在节省存储空间的前提下,正确高效的运算矩阵。
在实际应用中,经常出现一些阶数很高的矩阵,同时在矩阵中有很多值相同的元素并且它们的分布有一定的规律——称为特殊矩阵(special matrix),对称矩阵就是其中一种。对称矩阵的特点是:在一个n阶方阵中,有aij=aji(1≤i,j≤n)。可以对这类矩阵进行压缩存储,从而节省存储空间,并使矩阵的各种运算能有效进行。
(1) 对称矩阵
对称矩阵关于主对角线对称,因此只需存储下三角部分(包括主对角线)即可。这样,原来需要存储n×n个存储单元,现在只需要n×(n+1)/2个存储单元,节约了大约一半的存储单元。当n较大时,这是比较可观的一部分存储单元。
如何只存储下三角部分的元素呢?由于下三角中共有n×(n+1)/2个元素,可将这些元素按行存储到一个数组SA[n(n+1)/2]中。这样,下三角中的元素aij(i≥j)存储到SA[k]中,在数组SA中的下标k和i、j的关系为:k=i×(i-1)/2+j-1,寻址的计算方法如图所示。

Java代码实现对称矩阵存储压缩:
package top.yxy.xs; / * 稀疏矩阵的压缩存储 * * 稀疏矩阵三元组顺序表 * * 三元组顺序存储的稀疏矩阵类 * * @author clarck * */ public class SeqSparseMatrix { // 矩阵行数、列数 private int rows, columns; // 稀疏矩阵三元组顺序表 private SeqList<Triple> list; / * 构造rows行,colums列零矩阵 * * @param rows * @param columns */ public SeqSparseMatrix(int rows, int columns) { if (rows <= 0 || columns <= 0) throw new IllegalArgumentException("矩阵行数或列数为非正数"); this.rows = rows; this.columns = columns; // 构造空顺序表,执行SeqList()构造方法 this.list = new SeqList<Triple>(); } public SeqSparseMatrix(int rows, int columns, Triple[] elems) { this(rows, columns); // 按行主序插入一个元素的三元组 for (int i = 0; i < elems.length; i++) this.set(elems[i]); } / * 返回矩阵第i行第j列元素,排序顺序表的顺序查找算法,O(n) * * @param i * @param j * @return */ public int get(int i, int j) { if (i < 0 || i >= rows || j < 0 || j >= columns) throw new IndexOutOfBoundsException("矩阵元素的行或列序号越界"); Triple item = new Triple(i, j, 0); int k = 0; Triple elem = this.list.get(k); // 在排序顺序表list中顺序查找item对象 while (k < ((CharSequence) this.list).length() && item.compareTo(elem) >= 0) { // 只比较三元组元素位置,即elem.row == i && elem.column == j if (item.compareTo(elem) == 0) return elem.value; // 查找到(i, j), 返回矩阵元素 k++; elem = ((Object) this.list).get(k); } return 0; } / * 以三元组设置矩阵元素 * * @param elem */ public void set(Triple elem) { this.set(elem.row, elem.colum, elem.value); } / * 设置矩阵第row行第column列的元素值为value,按行主序在排序顺序表list中更改或插入一个元素的三元组, O(n) * * @param row * @param column * @param value */ public void set(int row, int column, int value) { // 不存储值为0元素 if (value == 0) return; if (row >= this.rows || column >= this.columns) throw new IllegalArgumentException("三元组的行或列序号越界"); Triple elem = new Triple(row, column, value); int i = 0; // 在排序的三元组顺序表中查找elem对象,或更改或插入 while (i < this.list.length()) { Triple item = this.list.get(i); // 若elem存在,则更改改位置矩阵元素 if (elem.compareTo(item) == 0) { // 设置顺序表第i个元素为elem this.list.set(i, elem); return; } // elem 较大时向后走 if (elem.compareTo(item) >= 0) i++; else break; } this.list.insert(i, elem); } @Override public String toString() { String str = "三元组顺序表:" + this.list.toString() + "\n"; str += "稀疏矩阵" + this.getClass().getSimpleName() + "(" + rows + " * " + columns + "): \n"; int k = 0; // 返回第k个元素,若k指定序号无效则返回null Triple elem = this.list.get(k++); for (int i = 0; i < this.rows; i++) { for (int j = 0; j < this.columns; j++) if (elem != null && i == elem.row && j == elem.colum) { str += String.format("%4d", elem.value); elem = this.list.get(k++); } else { str += String.format("%4d", 0); } str += "\n"; } return str; } / * 返回当前矩阵与smat相加的矩阵, smatc=this+smat,不改变当前矩阵,算法同两个多项式相加 * * @param smat * @return */ public SeqSparseMatrix plus(SeqSparseMatrix smat) { if (this.rows != smat.rows || this.columns != smat.columns) throw new IllegalArgumentException("两个矩阵阶数不同,不能相加"); // 构造rows*columns零矩阵 SeqSparseMatrix smatc = new SeqSparseMatrix(this.rows, this.columns); int i = 0, j = 0; // 分别遍历两个矩阵的顺序表 while (i < this.list.length() && j < smat.list.length()) { Triple elema = this.list.get(i); Triple elemb = smat.list.get(j); // 若两个三元组表示相同位置的矩阵元素,则对应元素值相加 if (elema.compareTo(elemb) == 0) { // 相加结果不为零,则新建元素 if (elema.value + elemb.value != 0) smatc.list.append(new Triple(elema.row, elema.colum, elema.value + elemb.value)); i++; j++; } else if (elema.compareTo(elemb) < 0) { // 将较小三元组复制添加到smatc顺序表最后 // 复制elema元素执行Triple拷贝构造方法 smatc.list.append(new Triple(elema)); i++; } else { smatc.list.append(new Triple(elemb)); j++; } } // 将当前矩阵顺序表的剩余三元组复制添加到smatc顺序表最后 while (i < this.list.length()) smatc.list.append(new Triple(this.list.get(i++))); // 将smat中剩余三元组复制添加到smatc顺序表最后 while (j < smatc.list.length()) { Triple elem = smat.list.get(j++); if (elem != null) { smatc.list.append(new Triple(elem)); } } return smatc; } / * 当前矩阵与smat矩阵相加,this+=smat, 改变当前矩阵,算法同两个多项式相加 * * @param smat */ public void add(SeqSparseMatrix smat) { if (this.rows != smat.rows || this.columns != smat.columns) throw new IllegalArgumentException("两个矩阵阶数不同,不能相加"); int i = 0, j = 0; // 将mat的各三元组依次插入(或相加)到当前矩阵三元组顺序表中 while (i < this.list.length() && j < smat.list.length()) { Triple elema = this.list.get(i); Triple elemb = smat.list.get(j); // 若两个三元组表示相同位置的矩阵元素,则对应元素值相加 if (elema.compareTo(elemb) == 0) { // 相加结果不为0,则新建元素 if (elema.value + elemb.value != 0) this.list.set(i++, new Triple(elema.row, elema.colum, elema.value + elemb.value)); else this.list.remove(i); j++; } else if (elema.compareTo(elemb) < 0) { // 继续向后寻找elemb元素的插入元素 i++; } else { // 复制elemb元素插入作为this.list的第i个元素 this.list.insert(i++, new Triple(elemb)); j++; } } // 将mat中剩余三元组依次复制插入当前矩阵三元组顺序表中 while (j < smat.list.length()) { this.list.append(new Triple(smat.list.get(j++))); } } // 深拷贝 public SeqSparseMatrix(SeqSparseMatrix smat) { this(smat.rows, smat.columns); // 创建空顺序表,默认容量 this.list = new SeqList<Triple>(); // 复制smat中所有三元组对象 for (int i = 0; i < smat.list.length(); i++) this.list.append(new Triple(smat.list.get(i))); } / * 比较两个矩阵是否相等 */ public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof SeqSparseMatrix)) return false; SeqSparseMatrix smat = (SeqSparseMatrix) obj; return this.rows == smat.rows && this.columns == smat.columns && this.list.equals(smat.list); } / * 返回转置矩阵 * @return */ public SeqSparseMatrix transpose() { //构造零矩阵,指定行数和列数 SeqSparseMatrix trans = new SeqSparseMatrix(columns, rows); for (int i = 0; i < this.list.length(); i++) { //插入矩阵对称位置元素的三元组 trans.set(this.list.get(i).toSymmetry()); } return trans; } }
讯享网
(2)稀疏矩阵
在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵。稀疏矩阵常使用三元组存储法,三元组表示法就是在存储非零元的同时,存储该元素所对应的行下标和列下标。稀疏矩阵中的每一个非零元素由一个三元组(i,j,aij)唯一确定。矩阵中所有非零元素存放在由三元组组成的数组中。
查看更多文章欢迎关注我的微信公众号:AlbertYang


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/127783.html