您的位置:首页 > 理论基础 > 数据结构算法

数据结构,算法与应用(1)

2015-09-14 16:35 369 查看
第3章 数据描述

最常见的数据描述方法有:公式化描述、链接描述、间接寻址和模拟指针。

公式化描述借助数学公式来确定元素表中的每个元素分别存储在何处(如存储器地址)。

最简单的情形就是把所有元素依次连续存储在一片连续的存储空间中,这就是通常所说的连续线性表。

在链接描述中,元素表中的每个元素可以存储在存储器的不同区域中,每个元素都包含一个指向下一个元素的指针。同样,在间接寻址方式中,元素表中的每个元素也可以存储在存储器的不同区域中,不同的是,此时必须保存一张表,该表的第 i项指向元素表中的第 i个元素,所以这张表是一个用来存储元素地址的表。

在公式化描述中,元素地址是由数学公式来确定的;在链接描述中,元素地址分布在每一个表元素中;而在间接寻址方式下,元素地址则被收集在一张表中。

模拟指针非常类似于链接描述,区别在于它用整数代替了 C++指针,整数所扮演的角色与指针所扮演的角色完全相同。

1. 公式化描述的评价

这种描述方法的一个缺点是空间的低效利用。考察如下的情形:我们需要维持三个表,而且已经知道在任何时候这三个表所拥有的元素总数都不会超过 5000个。然而,很有可能在某个时刻一个表就需要5000个元素,而在另一时刻另一个表也需要 5000 个元素。若采用类LinearList,这三个表中的每一个表都需要有 5000个元素的容量。因此,即使我们在任何时刻都不会使用5000以上的元素,也必须为此保留总共 15000个元素的空间。

为了避免这种情形,必须把所有的线性表都放在一个数组 list中进行描述,并使用两个附加的数组first和last对这个数组进行索引。图 3-3给出了在一个数组 list中描述的三个线性表。我们采用大家很习惯的约定,即如果有 m个表,则每个表从1到m进行编号,且 first[i]为第i个表中的第一个元素。有关 first[i]的约定使我们更容易地采用公式化描述方式。 last[i]是表i的最后一个元素。注意,根据这些约定,每当第 i个表不为空时,有 last[i]>first[i],而当第i个表为空时,有last[i]=first[i]。所以在图3-3的例子中,表
2是空表。在数组中,各线性表从左至右按表的编号次序1,2,3,...,m进行排列。



公式化描述代码如下:


View
Code
为了避免第一个表和最后一个表的处理方法与其他的表不同,定义了两个边界表:表 0和表m+1,其中first[0]=last[0]=-1, first[m+1]=last[m+1]=MaxSize-1。为了在第 i 个表的第 k 个元素之后插入一个元素,首先需要为新元素创建空间。如果last[i]=first[i+1],则在第 i 个表和第 i + 1个表之间没有空间,因此不能把第 k + 1至最后一个元素向后移动一个位置。在这种情况下,通过检查关系式 last[i-1]<first[i]是否成立,可以确定是否有可能把第i
个表的1至k-1元素向前移一个位置;如果这个关系式不成立,要么需要把表 1至表i-1的元素向前移一个位置,要么把表 i+1至表m 向后移一个位置,然后为表 i创建需要增长的空间。当表中所有的元素总数少于 MaxSize时,这种移位的方法是可行的。图3-4是一个伪 C++函数,它向表 i 中插入一个新的元素,可以把该函数细化为兼容的 C++代码。

尽管在一个数组中描述几个线性表比每个表用一个数组来描述空间的利用率更高,但在最坏的情况下,插入操作将耗费更多的时间。事实上,一次插入操作可能需要移动 MaxSize-1个元素。



2. 链表

令L=(e1 , e2 , ..., en )是一个线性表。在针对该表的一个可能的链表描述中,每个元素 ei 都放在不同的节点中加以描述。每个节点都包含一个链接域,用以指向表中的下一个元素。所以节点ei 的指针将指向ei +1,其中1≤i<n。节点en 没有下一个节点,所以它的链接域为 NULL(或0)。指针变量first 指向描述中的第一个节点。图 3-5给出了表L=(e1 , e2 , ..., en )的链表描述。



由于图3 - 5中的每个链表节点都正好有一个链接域,所以该图的链表结构被称之为单向链表(singly linked list)。并且,由于第一个节点 e1 的指针指向第二个节点 e 2,e2 的指针指向 e3 ,. . .,最后一个节点链接域为 N U L L (或0 ),故这种结构也被称作链( c h a i n)。

删除操作

为了从图3-6所示的链中删除第四个元素,需进行如下操作:

1) 找到第三和第四个节点。

2) 使第三个节点指向第五个节点。

3) 释放第四个节点所占空间,以便于重用。

程序3-14中给出了删除操作的代码。有三种情形需要考虑。第一种情形是: k小于1或链表为空;第二种情形是:第一个元素将被删除且链表不为空;最后一种情形是:从一个非空的链表中删除首元素之外的其他元素。

插入操作

插入和删除的过程很相似。为了在链表的第 k个元素之后插入一个新元素,需要首先找到第k 个元素,然后在该节点的后面插入新节点。图 3-7给出了k=0 和k≠0 两种情况下链表指针的变化。插入之前的实线指针,在插入之后被“打断”。程序3-15给出了相应的 C++ 代码,它的复杂性为O (k)。



链表遍历器(迭代器)

链表遍历器(见程序3-18)有两个共享成员Initialize和Next。Initialize返回一个指针,该指针指向第一个链表节点中所包含的数据,同时把私有变量location设置为指向链表的第一个节点,该变量用来跟踪我们在链表中所处的位置。成员 Next用来调整 location,使其指向链表中的下一个节点,并返回指向该节点数据域的指针。由于 ChainIterator类访问了Chain类的私有成员first,所以应把它定义为Chain的友类。



链表描述如下:


View
Code

下面是一个扩展的链表,包括了尾指针:


View
Code
对应的测试代码如下:


View
Code

循环列表

采纳下面的一条或两条措施,使用链表的应用代码可以更简洁、更高效: 1)把线性表描述成一个单向循环链表(singly linked circular list),或简称循环链表(circular list),而不是一个单向链表;2) 在链表的前部增加一个附加的节点,称之为头节点( head node)。通过把单向链表最后一个节点的链接指针改为指向第一个节点,就可以把一个单向链表改造成循环链表,如图3-8a 所示。图 3-8b 给出了一个带有头指针的非空的循环链表,图 3-8c 给出了一个带有头指针的空的循环链表。



在使用链表时,头指针的使用非常普遍,因为利用头指针,通常可以使程序更简洁、运行速度更快。CircularList类的定义与Chain类的定义很类似。尽管链表搜索的复杂性仍然保持为O(n),但代码本身要稍微简单一些。由于与程序3-12相比,程序3-20在for循环的每次循环中执行的比较次数 较少,因此程序3-20将比程序3-12运行得更快一些,除非要查找的元素紧靠链表的左部。

与公式化描述方法的比较

采用公式化描述方法的线性表仅需要能够保存所有元素的空间以及保存表长所需要的空间,而链表和循环链表描述还需要额外的空间,用来保存链接指针(线性表中 的每个元素都需要一个相应的链接指针)。采用链表描述所实现的插入和删除操作要比采用公式化描述时执行得更快。当每个元素都很长时(字节数多),尤其如此。

还可以使用链接模式来描述很多表,这样做并不会降低空间利用率,也不会降低执行效率。对于公式化描述,为了提高空间利用率,不得不把所有的表都放在一个数 组中加以描述,并使用了另外两个数组来对这个数组进行索引,更有甚者,与一个表对应一个数组的情形相比,插入和删除操作变得更为复杂,而且存在一个很显著的最坏运行时间。

采用公式化描述,可以在O(1)的时间内访问第k 个元素。而在链表中,这种操作所需要的时间为O(k)。

双向链表

对于线性表的大多数应用来说,采用链表和 /或循环链表已经足够了。然而,对于有些应用,如果每个链表元素既有指向下一个元素的指针,又有指向前一个元素的指针,那么在设计应用代码时将更为方便。双向链表( doubly linked list)即是这样一个有序的节点序列,其中每个节点都有两个指针:left和right。left指针指向左边节点(如果有) ight指针指向右边节点,r(如果有)。图3 - 9给出了线性表 ( 1 , 2 , 3 , 4 )的双向链表表示。



小结

本节引入了以下重要概念:
• 单向链表 令x 是一个单向链表。当且仅当x.first=0时x 为空。如果x 不为空,则x.first指向链表的第一个节点。第一个节点指向第二个节点;第二个节点指向第三个节点,如此进行下去。最后一个节点的链指针为 0。
• 单向循环链表 它与单向链表的唯一区别是最后一个节点又反过来指向了第一个节点。当循环链表x为空时,x.first=0。
• 头指针 这是在链表中引入的附加节点。利用该节点通常可以使程序设计更简洁,因为这样可以避免把空表作为一种特殊情况来对待。使用头指针时,每个链表(包括空表)都至少包含一个节点(即头指针)。
• 双向链表 双向链表由从左至右按序排列的节点构成。 right 指针用于把节点从左至右链接在一起,最右边节点的 right指针为0。left指针用于把节点从右至左链接在一起,最左边节点的left指针为0。
• 双向循环链表 双向循环链表与双向链表的唯一区别在于,最左边节点的 left指针指向最右边的节点,而最右边节点的 right指针指向最左边的节点。

间接寻址

间接寻址(indirect addressing)是公式化描述和链表描述的组合。采用这种描述方法,可以保留公式化描述方法的许多优点——可以根据索引在

( 1 )的时间内访问每个元素、可采用二叉搜索方法在对数时间内对一个有序表进行搜索等等。与此同时,也可以获得链表描述方法的重要特色——在诸如插入和删除操 作期间不必对元素进行实际的移动。因此,大多数间接寻址链表操作的时间复杂性都与元素的总数无关。

在间接寻址方式中,使用一个指针表来跟踪每个元素。可采用一个公式(如公式( 3 - 1))来定位每个指针的位置,以便找到所需要的元素。

元素本身可能存储在动态分配的节点或节点数组之中。图3-10给出了一个采用间接寻址表 table 描述的5元素线性表。其中 table[i]是一个指针,它指向表中的第i+1个元素,length 是表的长度。

尽管可以使用公式(3-1)来定位指向表中第 i个元素的指针,但这个公式本身并不能直接定位第 i个元素。在对表元素的寻址模式中table提供了一级“间接”引用。

间接寻址对应的代码如下:


View
Code

模拟指针

在大多数应用中,可以利用动态分配及 C++指针来实现链表和间接寻址表。不过,有时候采用一个节点数组以及对该数组进行索引的模拟指针( simulated pointer),可以使设计更方便、更高效。

假定采用一个数组 n o d e,该数组的每个元素中都包含两个域: d a t a和l i n k。数组中的节点分别是:node[0]、 node[l]、...、node[NumberOfNodes-l]。以下用节点 i来代表node[i]。如果一个单向链表 c由节点10,5和24按序构成,将得到 c=10(指向链表c的第一个节点的指针是整数类型),n o d e [ 1 0 ] . l i n k = 5 (指向第二个节点的指针 ),n o d e [ 5 ] . l i n k = 2 4 (指向下一个节点的指针
),node[24].link=-1(表示节点24是链表中的最后一个节点 )。在绘制链表时,可以把每个链接指针画成一个箭头(如图3-12所示),与使用C++指针的时候一样。



为了实现指针的模拟,需要设计一个过程来分配和释放一个节点。当前未被使用的节点将被放入一个存储池( storage pool)之中。开始时,存储池中包含了所有节点 node[0:Number-OfNodes-l]。Allocate从存储池中取出节点,每次取出一个。 Deallocate则将节点放入存储池中,每次放入一个。因此,Allocate 和Deallocate 分别对存储池执行插入和删除操作,等价于 C++函数d e l e t e和n e w。如果存储池是一个节点链表(如图 3 - 1 3所示),这两个函数可以高效地执行。用作存储池的链表被称之为可用空间表(
available space list),其中包含了当前未使用的所有节点。first 是一个类型为 int 的变量,它指向可用空间表中的第一个节点。添加和删除操作都是在可用空间表的前部进行的。

各种描述方法 的比较

在图3-15中,分别给出了使用本章所介绍的四种数据描述方法执行各种链表操作所需要的时间复杂性。在表中,s 和n 分别表示sizeof(T)和链表长度。由于采用C++指针和采用模拟指针完成这些操作所需的时间复杂性完全相同,因此表中在把这两种情形的时间复杂性合并在同一行中进行描述。



使用间接寻址与使用链表描述所需要的空间大致相同,二者都比使用公式化描述所需要的空间更多。不管是使用链表描述还是间接寻址,执行链表的插入和删除操作 所需要的时间复杂性均与每个链表元素本身的大小无关。然而,在使用公式化描述时,插入和删除操作的复杂性与元素本身的大小成线性关系。所以,如果链表元素 的大小 s 很大,那么使用链表描述和间接寻址将更适合于需要大量插入和删除操作的应用。

在使用公式化描述和间接寻址时,确定表的长度以及访问表中第 k 个元素所需要的时间复杂性均为O (1)。而在使用链表描述时,这些操作的时间复杂性分别为O(length)和O(k),所以链表描述不适合于这两种操作占优势的应用。

基于以上的讨论可以发现,间接寻址比较适合这样的应用:表元素本身很大,较频繁地进行插入、删除操作以及确定表的长度、访问第 k 个元素。同时,如果线性表本身已经按序排列,那么使用公式化描述或间接寻址进行搜索所需要时间均为 O ( l o gn),而使用链表描述时,所需要的时间O(n)。

应用:箱子排序


View
Code

第4章 数组和矩阵

一维数组定义



一维数组代码:


View
Code
二维数组代码:


View
Code
两种数组的测试代码如下:


View
Code
利用数组来实现二维矩阵


View
Code

复杂性
当T是一个内部 C + +数据类型(如 int, float和c h a r)时,构造函数和析构函数的复杂性为

(1),而当T是一个用户自定义的类时,构造函数和析构函数的复杂性为
O(size)。之所以存在这种差别,是因为当 T是一个用户自定义类时,在用 new(delete)创建(删除)数组element的过程中,对于ele ment的每个元素都要调用一次 T的构造函数(析构函数)。下标操作符 [ ]的复杂性为

(1),其他操作符的复杂性均为
O(size)。(注意复杂性不会是

(size),因为所有操作符的代码都可以引发一个异常并提前终止)。

特殊矩阵

方阵(square matrix)是指具有相同行数和列数的矩阵。一些常用的特殊方阵如下:
• 对角矩阵(diagonal) M是一个对角矩阵当且仅当 i≠j 时有M(i, j)=0。如图4-4a 所示。
• 三对角矩阵( tridiagonal) M是一个三对角矩阵当且仅当 | i - j | > 1 时有M (i, j)=0。如图4-4b 所示。

• 下三角矩阵(lower triangular) M是一个下三角矩阵当且仅当 i < j 时有M (i, j )=0。如图4-4c 所示。
• 上三角矩阵(upper triangular) M是一个上三角矩阵当且仅当 i > j 时有M (i, j )=0。如图4-4d 所示。
• 对称矩阵(symmetric) M是一个对称矩阵当且仅当对于所有的 i 和j 有M (i, j )=M (j, i )。如图4-4e 所示。



对角矩阵实现代码:


View
Code
三对角角矩阵实现代码:


View
Code
下三角矩阵实现代码:


View
Code

堆栈折叠问题

例4-4

假定有一个堆栈,其中有 n 个纸盒,纸盒 1位于栈顶,纸盒 n 位于栈底。每个纸盒的宽度为w,深度为d。第i 个纸盒的高度为 hi 。堆栈的体积为 w*d* ∑hi 。在堆栈折叠(stack folding)问题中,选择一个折叠点 i 把堆栈分解成两个子堆栈,其中一个子堆栈包含纸盒 1至i,另一个子堆栈包含纸盒 i+1至n。重复这种折叠过程,可以得到若干个堆栈。如果创建了 s 个堆栈,则这些堆栈所需要的空间宽度为 s*w,深度为d,高度h 为最高堆栈的高度。 s 个堆栈所需要的空间容量为s*w*d*h。由于h
是第i 至第j 纸盒所构成的堆栈的高度(其中i≤j ),因此h 的可能取值可由n×n矩阵H给出,其中对于 i >j有H(i, j)=0。即有h= ∑hk ,i≤j。由于每个纸盒的高度可以认为是大于 0,所以H(i, j )=0代表一个不可能的高度。图 4-6a 给出了一个五个纸盒的堆栈。每个矩形中的数字代表纸盒的高度。图 4-6b 给出了五个纸盒堆栈折叠成三个堆栈后的情形,其中最大堆栈的高度为7。矩阵H是一个上三角矩阵,如图 4-6c 所示。



稀疏矩阵

如果一个m×n 矩阵中有“许多”元素为0,则称该矩阵为稀疏矩阵( sparse)。不是稀疏的矩阵被称为稠密矩阵(dense)。在稀疏矩阵和稠密矩阵之间并没有一个精确的界限。 n×n 的对角矩阵和三对角矩阵都是稀疏矩阵,二者都有 O(n)个非0元素和O(n*n)个0元素。一个n×n 的三角矩阵是稀疏矩阵吗?它至少有 n(n-1)/2个0元素,最多有 n(n+1)/2个非0元素。在本节中我们规定若一个矩阵是稀疏矩阵,则其非 0元素的数目应小于n*n/3,在有些情况下应小于 n*n /5,因此可将把三角矩阵视为稠密矩阵。

诸如对角矩阵和三对角矩阵这样的稀疏矩阵,其非 0区域的结构很有规律,因此可以设计一个很简单的存储结构,该存储结构的大小就等于矩阵非 0区域的大小。本节中主要考察具有不规则非0区域的稀疏矩阵。

数组描述

稀疏矩阵数组实现代码:其中包括矩阵乘法的实现


View
Code

链表描述
用一维数组来描述稀疏矩阵所存在的缺点是:当我们创建这个一维数组时,必须知道稀疏矩阵中的非 0元素总数。虽然在输入矩阵时这个数是已知的,但随着矩阵加法、减法和乘法操作的执行,非0元素的数目会发生变化,因此如果不实际计算,很难精确地知道非 0元素的数目。
正因为如此,只好先估计出每个矩阵中的非 0元素数目,然后用它作为数组的初始大小。在设计SparseMatrix类时就采用了这样的策略。在该类的代码中,当结果矩阵非 0元素的数目超出所估计的数目时将引发一个异常。不过也可以重写这些代码,在非 0元素的数目超出所估计的数目时分配一个新的、更大的数组,然后从老数组中把元素复制出来并删除老数组。这种额外工作将使算法的效率降低,并留下了新数组 到底应该取多大的问题。如果新数组不够大,还得重复上述分配和复制过程;如果新数组太大,又会浪费很多空间。一种解决办法就是采用基于指针的描述。这种方
法需要额外的指针存储空间,但可以节省对数组描述中其他一些信息的存储。最重要的是,它可以避免存储的再分配以及部分结果的复制。
1. 描述
链表描述的一种可行方案是把每行的非 0元素串接在一起,构成一个链表,如图 4 - 11所示。图中每个非阴影节点代表稀疏矩阵中的一个非 0元素,它有三个域: c o l(非 0元素所在列号) a l u e(非 0元素的值)和 l i n k(指向下一个非阴影节点的指针)、v。仅当矩阵某行中至少包含一个非 0元素才会为该行创建一个链表。在行链表中,每个节点按其 c o l值的升序进行排列。

用另外一个链表把所有的行链表(即非阴影链表)收集在一起,如图 4-11中的阴影节点所示。每个阴影节点也有三个域: row(相应行链表的行号) ink(指向下一个阴影节点的指针)、l和a(指向行链表, a.first 指向行链表中的第一个节点)。各阴影节点按其 r o w值的升序排列。每个阴影节点可以被视为一个行链表的头节点,因此阴影链表可以被视为头节点链表。空的头节点链表代表没有非0元素的矩阵。
稀疏矩阵链表实现的代码: 包括矩阵乘法的实现

方法一:利用前面实现的链表实现





#include <iostream>

#include "Chain_extended.h"

using namespace std;

template <class T>

class LinkedMatrix;

template <class T>

class HeadNode;

template <class T>

class CNode{

friend class LinkedMatrix<T>;

friend class HeadNode<T>;

public:

int operator!=(const CNode<T>& y){

return (value!=y.value);

}

void Output(ostream& out)const{

out<<"column "<<col<< " value "<<value;

}

private:

int col;

T value;

};

template<class T>

ostream& operator<<(ostream& out,const CNode<T>& x){

x.Output(out);out<<endl;

return out;

}

template<class T>

class HeadNode{

friend class LinkedMatrix<T>;

public:

int operator!=(const HeadNode<T>& y){

return (row!=y.row);

}

void Output(ostream& out)const{

out<<"row"<<row;

}

private:

int row;

Chain<CNode<T> >a;

};

template<class T>

ostream& operator<<(ostream& out,const HeadNode<T>& x){

x.Output(out);

out<<endl;

return out;

}

template<class T>

class LinkedMatrix{

public:

LinkedMatrix(){}

~LinkedMatrix(){}

void Transpose(LinkedMatrix<T> &b)const;

void Add(const LinkedMatrix<T> &b,LinkedMatrix<T>& c)const;

void Output(ostream& out)const;

void Input(istream& in);

private:

int rows,cols;

Chain<HeadNode<T> >a;

};

template<class T>

void LinkedMatrix<T>::Transpose(LinkedMatrix<T> &b)const

{

b.a.Erase();

Chain<CNode<T> >* bin;

bin=new Chain<CNode <T> >[cols+1];

ChainIterator<HeadNode<T> > p;

HeadNode<T>* h=p.Initialize(a);

while(h){

int r=h->row;

ChainIterator<CNode<T> > q;

CNode<T> *z=q.Initialize(h->a);

CNode<T> x;

x.col=r;

while(z){

x.value=z->value;

bin[z->col].Append(x);

z=q.Next();

}

h=p.Next();

}

b.rows=cols;

b.cols=rows;

HeadNode<T> H;

for(int i=1;i<=cols;i++)

if(!bin[i].IsEmpty()){

H.row=i;

H.a=bin[i];

b.a.Append(H);

bin[i].Zero();

}

H.a.Zero();

delete[] bin;

}

template<class T>

void LinkedMatrix<T>::Add(const LinkedMatrix<T> &b,LinkedMatrix<T>& c)const

{

}

template<class T>

void LinkedMatrix<T>::Output(ostream& out)const

{

ChainIterator<HeadNode<T> > p;

out<<"rows="<<rows<<" columns="<<cols<<endl;

HeadNode<T> *h=p.Initialize(a);

if(!h){

out<<"No non-zero terms"<<endl;

return ;

}

while(h){

out<<"row "<<h->row<<endl;

out<<h->a<<endl;

h=p.Next();

}

}

template<class T>

ostream& operator<<(ostream &out,const LinkedMatrix<T> & c)

{

c.Output(out);

return out;

}

template<class T>

void LinkedMatrix<T>::Input(istream& in)

{

a.Erase();

int terms;

cout<<"Enter number of rows,columns and terms"<<endl;

in>>rows>>cols>>terms;

HeadNode<T> H;

H.row=0;

for(int i=1;i<=terms;i++){

cout<<"Enter row,column and value of term"<<i<<endl;

int row,col;

T value;

in>>row>>col>>value;

if(row>H.row){

if(H.row)

a.Append(H);

H.row=row;

H.a.Zero();

}

CNode<T>* c=new CNode<T>;

c->col=col;

c->value=value;

H.a.Append(*c);

}

if(H.row)

a.Append(H);

H.a.Zero();

}

template<class T>

istream& operator>>(istream& in,LinkedMatrix<T> &c)

{

c.Input(in);

return in;

}

int main(void)

{

LinkedMatrix<int> a, b, c;

// test input and output

cin >> a;

cout << "Matrix a is" << endl << a;

cin >> b;

cout << "Matrix b is" << endl << b;

// test transpose

a.Transpose(c);

cout << "The transpose of a is" << endl << c;

return 0;

}



方法二:利用C++标准库中STL中的list容器实现:





#include <iostream>

#include <list>

#include <vector>

using namespace std;

template <class T>

class LinkedMatrix;

template <class T>

class HeadNode;

template <class T>

class CNode{

friend class LinkedMatrix<T>;

friend class HeadNode<T>;

public:

int operator!=(const CNode<T>& y){

return (value!=y.value);

}

void Output(ostream& out)const{

out<<"column "<<col<< " value "<<value;

}

private:

int col;

T value;

};

template<class T>

ostream& operator<<(ostream& out,const CNode<T>& x){

x.Output(out);out<<endl;

return out;

}

template<class T>

class HeadNode{

friend class LinkedMatrix<T>;

public:

int operator!=(const HeadNode<T>& y){

return (row!=y.row);

}

void Output(ostream& out)const{

out<<"row"<<row;

}

private:

int row;

list<CNode<T> > a;

};

template<class T>

ostream& operator<<(ostream& out,const HeadNode<T>& x){

x.Output(out);

out<<endl;

return out;

}

template<class T>

class LinkedMatrix{

public:

LinkedMatrix(){}

~LinkedMatrix(){}

void Transpose(LinkedMatrix<T> &b)const;

void Add(const LinkedMatrix<T> &b,LinkedMatrix<T>& c)const;

void Multiply(const LinkedMatrix<T> &b,LinkedMatrix<T>& c)const;

void Output(ostream& out)const;

void Input(istream& in);

private:

int rows,cols;

list<HeadNode<T> > a;

};

template<class T>

void LinkedMatrix<T>::Transpose(LinkedMatrix<T> &b)const

{

b.a.clear();

vector<list<CNode<T> > > bin(cols+1);

typename list<HeadNode<T> >::const_iterator it;

typename list<CNode<T> >::const_iterator it2;

for(it=a.begin();it!=a.end();++it){

int r=it->row;

CNode<T> x;

x.col=r;

for(it2=(*it).a.begin();it2!=(*it).a.end();++it2){

x.value=it2->value;

bin[it2->col].push_back(x);

}

}

b.rows=cols;

b.cols=rows;

HeadNode<T> H;

for(int i=1;i<=cols;i++)

if(!bin[i].empty()){

H.row=i;

H.a=bin[i];

b.a.push_back(H);

}

}

template<class T>

void LinkedMatrix<T>::Add(const LinkedMatrix<T> &b,LinkedMatrix<T>& c)const

{

if(rows!=b.rows||cols!=b.cols)

throw "Size Miss Match";

c.a.clear();

c.rows=rows;

c.cols=cols;

typename list<HeadNode<T> >::const_iterator itt;

typename list<HeadNode<T> >::const_iterator itb;

typename list<CNode<T> >::const_iterator it2t;

typename list<CNode<T> >::const_iterator it2b;

itt=a.begin();

itb=b.a.begin();

while(itt!=a.end()&&itb!=b.a.end()){

int rt=itt->row;

int rb=itb->row;

if(rt>rb){

c.a.push_back(*itb);

++itb;

}

else if(rt<rb){

c.a.push_back(*itt);

++itt;

}

else{

it2t=(*itt).a.begin();

it2b=(*itb).a.begin();

HeadNode<T> H;

H.row=rt;

while(it2t!=(*itt).a.end()&&

it2b!=(*itb).a.end()){

int ct=it2t->col;

int cb=it2b->col;

if(ct>cb){

H.a.push_back(*it2b);

++it2b;

}

else if(ct<cb){

H.a.push_back(*it2t);

++it2t;

}

else{

CNode<T> cn;

cn.col=ct;

cn.value=it2t->value+it2b->value;

H.a.push_back(cn);

++it2t;

++it2b;

}

}

while(it2t!=(*itt).a.end())

H.a.push_back(*it2t++);

while(it2b!=(*itb).a.end())

H.a.push_back(*it2b++);

c.a.push_back(H);

++itt;

++itb;

}

}

while(itt!=a.end())

c.a.push_back(*itt++);

while(itb!=b.a.end())

c.a.push_back(*itb++);

}

template<class T>

void LinkedMatrix<T>::Multiply(const LinkedMatrix<T> &b2,LinkedMatrix<T>& c)const

{

if(cols!=b2.rows)

throw "Size Miss Match";

c.a.clear();

c.rows=rows;

c.cols=b2.cols;

LinkedMatrix<T> b;

b2.Transpose(b);

typename list<HeadNode<T> >::const_iterator itt;

typename list<HeadNode<T> >::const_iterator itb;

typename list<CNode<T> >::const_iterator it2t;

typename list<CNode<T> >::const_iterator it2b;

itt=a.begin();

while(itt!=a.end()){

int rt=itt->row;

HeadNode<T> H;

H.row=rt;

itb=b.a.begin();

while(itb!=b.a.end()){

int rb=itb->row;

it2t=(*itt).a.begin();

it2b=(*itb).a.begin();

CNode<T> cn;

cn.col=rb;

T sum=0;

while(it2t!=(*itt).a.end()&&

it2b!=(*itb).a.end()){

int ct=it2t->col;

int cb=it2b->col;

if(ct>cb){

++it2b;

}else if(ct<cb){

++it2t;

}else {

sum+=it2t->value*it2b->value;

++it2t;

++it2b;

}

}

if(sum!=0){

cn.value=sum;

H.a.push_back(cn);

}

++itb;

}

c.a.push_back(H);

++itt;

}

}

template<class T>

void LinkedMatrix<T>::Output(ostream& out)const

{

out<<"rows="<<rows<<" columns="<<cols<<endl;

typename list<HeadNode<T> >::const_iterator it;

typename list<CNode<T> >::const_iterator it2;

for(it=a.begin();it!=a.end();it++){

out<<"row "<<it->row<<endl;

for(it2=(*it).a.begin();it2!=(*it).a.end();it2++){

out<<(*it2);

}

}

}

template<class T>

ostream& operator<<(ostream &out,const LinkedMatrix<T> & c)

{

c.Output(out);

return out;

}

template<class T>

void LinkedMatrix<T>::Input(istream& in)

{

a.clear();

int terms;

cout<<"Enter number of rows,columns and terms"<<endl;

in>>rows>>cols>>terms;

typename list<HeadNode<T> >::iterator it;

typename list<CNode<T> >::iterator it2;

for(int i=1;i<=terms;i++){

cout<<"Enter row,column and value of term "<<i<<endl;

int row,col;

T value;

in>>row>>col>>value;

CNode<T> c;

c.col=col;

c.value=value;

for(it=a.begin();it!=a.end()&&row>(*it).row;it++);

if(it==a.end()){

HeadNode<T> H;

H.row=row;

H.a.push_back(c);

a.push_back(H);

}

else if(row<(*it).row){

HeadNode<T> H;

H.row=row;

H.a.push_back(c);

a.insert(it,H);

}

else if((*it).row==row){

for(it2=(*it).a.begin();it2!=(*it).a.end()&&col>(*it2).col;it2++);

if(it2==(*it).a.end()){

(*it).a.push_back(c);

}

else{

(*it).a.insert(it2,c);

}

}

}

}

template<class T>

istream& operator>>(istream& in,LinkedMatrix<T> &c)

{

c.Input(in);

return in;

}

int main(void)

{

LinkedMatrix<int> a, b, c;

// test input and output

cin >> a;

cout << "Matrix a is" << endl << a;

cin >> b;

cout << "Matrix b is" << endl << b;

// test transpose

// a.Transpose(c);

// cout << "The transpose of a is" << endl << c;

// a.Add(b,c);

// cout << "The sum of a and b is" << endl << c;

a.Multiply(b,c);

cout << "The sum of a and b is" << endl << c;

return 0;

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: