数据结构与算法系列研究四——数组和广义表

稀疏矩阵的十字链表实现和转置

一、数组和广义表的定义

数组的定义1:一个 N 维数组是受 N 组线性关系约束的线性表。
           二维数组的逻辑结构可形式地描述为:
           2_ARRAY(D,R)
              其中 D={aij} | i=0,1,...,b1-1; j=0,1,...,b2-1;aij∈D0}
              R={Row,Col}
              Row={<aij,ai,j+1>|0<=i<=b1-1;0<=j<=b2-2;aij,ai,j+1∈D0}
                           ai,j+1是aij在行关系中的后继元素。
              Col={<aij,ai+1,j>|0<=i<=b1-2;0<=j<=b2-1;aij,ai+1,j∈D0}
                           ai+1,j是aij在列关系中的后继元素。
            ①每一个数组元素a[i][j]都受两个关系Row和Col的约束:
              ROW(行关系):ai,j+1 是aij在行关系中的直接后继。
              COL(列关系):ai+1,j是aij在列关系中的后继元素。
           ②每个数组元素属于同一数据类型。
           ③每个数组元素由下标(i,j)唯一确定其位置。
           ④每个下标i由bi限定其范围,0≤i≤bi-1
      n维数组的逻辑结构可描述为:    
           n_ARRAY(D,R)
                D---数组的元素  
                R---定义为数组元素间的关系
           R=(R1,R2,...,Rn)
    数组的定义2 :一维数组是定长线性表; 二维数组是一个定长线性表,它的每个元素是一个一维数组;n维数组是线性表,它的每个元素是n-1维数组。
       数组是线性结构,基于两点:
          1、一个 n维数组被定义为一个线性表,它的元素是一个 n-1维数组。
          2、一个 n维数组的数据元素受n个关系的约束,且每个关系都是线性的。

其中: cn =L, ci-1= bi × ci,  1<i ≤ n ; ci 为常数
          上式称为n维数组的存储映象函数

 数组的基本操作:
    1、数组初始化:确定数组维数、长度,分配存储空间。
         initarray(&A,n,bound[ ]);  
             bound[ ]= b1,b2......bn
   2、撤消数组
         destroyarray (&A);
   3、求数组元素值
        value(A,&e,index[ ]);           
              index[ ]= i1,i2,......in
   4、为数组元素赋值
        assign(&A,e,index[ ]);
 数组的顺序表示及实现:
     用一遍地址连续的存储单元依次存放数据元素。
     1、数据类型描述
        #define MAX_ARRAY_DIM 8
       typedef struct {
          ElemType *base;   //数组元素空间
          int dim;          //数组维数
          int *bounds;      //数组维长
          int *constant;    //常数因子
      }ARRAY;
 矩阵的压缩存储:
    1、矩阵压缩存储的概念
        特殊矩阵:值相同元素或0元素在矩阵中分布有一定规律。
       ⒈对称矩阵:矩阵中的元素满足
                     aij=aji       1≤i,j≤n
       ⒉三角矩阵:上(下)三角矩阵指矩阵的下(上)三角(不包括对角线)中的元素均为常数c或0的n阶矩阵。
       ⒊对角矩阵(带状矩阵):矩阵中所有非0元素集中在以主对角线为中心的区域中。
      稀疏矩阵:非0元素很少( ≤ 5%)且分布无规律。
    2、矩阵的压缩存储
         为多个相同值的元分配一个存储单元;对零元不分配空间。
       对称矩阵的压缩存储
       存储分配策略: 每一对对称元只分配一个存储单元,即只存储下三角(包括对角线)的元, 所需空间数为:
                        n×(n+1)/2。
       存储分配方法: 用一维数组sa[n(n+1)/2]作为存储结构。
             sa[k]与aij之间的对应关系为:
      稀疏矩阵存储分配策略
          只存储稀疏矩阵的非0元素。
          矩阵中的一个元素可以用行列下标和其值来唯一表示,因此可以用一个三元组(i,j,aij) 唯一确定一个非0元素。
          逻辑上,用三元组表来表示稀疏矩阵的非0元                             
  广义表的定义
    广义表又称为列表(lists),是n≥0个元素a1,a2,...,an的有限序列,记为:
         A=( a1,a2,...,an)
     其中:
        A是广义表的表名,n是广义表的长度
        ai 是单个元素或广义表,
           若ai是单个元素,则称为广义表的单元素(或原子)。
           若是广义表,则称ai是广义表的子表。所以广义表又称为列表。
           即  ai ∈D0  或 ai ∈lists
        广义表的表头(Head):非空表A 的第一个元素 a1。
        广义表的头与a1具有相同的表示形式。
        广义表的表尾(Tail):除其头之外的其余元素( a2,...,an)组成的表。
        广义表的尾一定是一个广义表。
        特点:广义表的定义是一个递归的定义。


二、稀疏矩阵的十字链表实现

  2.1.实验内容
   编程实现稀疏矩阵的十字链表实现
    1.用txt文件录入稀疏矩阵数组,格式如下:
        m  n  t //表示行号,列号和总数
        i   j  value
        ..................
   2.读文件建立十字链表
   3.输出建立后的链表,格式为;
     行号1  列号11 值** 列号12 值***  。。。。。
     行号2  列号21 值** 列号22 值***  。。。。。
    。。。。。。。。。
   4.实现矩阵的转置
   5.输出转置后的矩阵,格式为矩阵形式。
 
 2.2.输入和输出
   输入:本程序采取文件读写形式,文件中数值格式详见实验内容
   输出:本程序有两种输出形式分别为按行输出和按矩阵形式输出

 2.3.关键数据结构与算法描述
    数据结构:建立十字链表需要知道行列号i,j和链表的right,down指针,以及节点的数值,于是数据结构呼之欲出,又因过程中读文件时需要先建立一个缓冲器存储节点的信息, 则两个结构具体如下:

/***********以下构建数据结构************/
typedef  struct OLink{
    int i,j;
    ElemType value;
    struct OLink *right,*down;
}*LinkList,OLink;
/************构建存储结构*************/
typedef struct record{
    int i;
    int j;
    int value;
}RECORD;
/**************构建完毕**************/

算法描述:
      建立十字链表,

1.首先要知道链表的头节点,因每行,每列都需要一个循环链表,则共需要m+n+1个头指针,m个行指针,n个列指针,1个总头指针。

2.建立完两个指针链表之后(注意:此处链表的每一个元素都是附加头节点),就要对矩阵的元素进行插入,当插入元素时要注意和对应行,对应列都建立联系,构成网状结构,当插入完元素之后,十字链表也就建立完毕了。

3.余下的就是输出元素了,根据头节点共有两种输出方法,一种按行输出,一种按列输出。当然也可以把非零元素放到二维数组中,通过二维数组进行输出。

4.最后,就是矩阵的转置,只需将a[i][j]与a[j][i]交换即可,再重新建立十字链表,修改初始化和i,j指向即可。其中最核心的算法就是头节点的构建和元素的插入了,具体代码如下:

/*************创建行,列头节点*****************/
void CreateHead(LinkList *head, int m, int n)
{
    //以下建立列头结点
    LinkList p = *head,q;
    //构建列头节点
    for(int i=0; i<n; i++)
    {
      q = (LinkList)malloc(sizeof(OLink));
      q->i = -1;   //注意在矩阵的外面,也可以不赋值
      q->j = i;    //代表矩阵的列号,从0开始

      q->down = q;  //构建循环链表的标志
      p->right = q; //链接
      p = q;        //继续向前推进
    }
    p->right = (*head);//循环链表的标志

    //以下建立行头结点,基本原理同上
    p = (*head);
    for(i=0; i<m; i++)
    {
      q = (LinkList)malloc(sizeof(OLink));
      q->i = i;
      q->j = -1;

      q->right = q;
      p->down = q;
      p = q;
    }
    p->down = (*head);
}
/************各个头节点构建完毕,共m+n+1个************/

/***************插入节点的算法************************/
bool InsertNode(LinkList *head, int i, int j, ElemType e)
{
    LinkList p = *head,q;
    if(i < 0||j < 0||i >= (*head)->i||j >= (*head)->j)
        return false;
    /********构建节点********/
    q = (LinkList)malloc(sizeof(OLink));
    q->i = i;
    q->j = j;
    q->value = e;
    /*******完毕***********/
    for(int k=0; k<=i; k++)
    {
        p = p->down;//注意此处等于i截至
    }
    //产生定位指针
    LinkList sr = p,s = p->right;
    /******若不满足s==p,或者插入元素大于后面元素,继续推进********/
    while(s!=p && q->j>s->j)
    {
        sr = s;
        s=s -> right;
    }
    /*******推进完毕,有可能有三种情况*************/
    q->right = s;
    sr->right = q;
    /**********行链接处理完毕*********************/
    /******以下链接列链表,方法同上**********************/
    p = *head;
       for( k=0; k<=j; k++)
    {
         p = p->right;
    }
     sr = p;
     s = p->down;
    while(s!=p && q->j>s->j)
    {
        sr = s;
        s = s->down;
    }
    q->down = s;
    sr->down = q;
    /**********列链接处理完毕*********************/
     return true;
}

 2.4.测试与理论
   1.在文件操作中输入如下文本:

2.程序运行后应产生9*5的矩阵,具体输出形式应与要求一致,如图;

3.转置后变成5*9的矩阵,具体显示如下

 2.5、所有程序

  1 #include "stdlib.h"
  2 #include "conio.h"
  3 #include "iostream"
  4 using namespace std;
  5
  6 typedef  int ElemType;//设置元素类型
  7
  8 /***********以下构建数据结构************/
  9 typedef  struct OLink{
 10     int i,j;
 11     ElemType value;
 12     struct OLink *right,*down;
 13 }*LinkList,OLink;
 14 /************构建存储结构*************/
 15 typedef struct record{
 16     int i;
 17     int j;
 18     int value;
 19 }RECORD;
 20 /**************构建完毕**************/
 21
 22 /**************进行初始化操作********************/
 23 void InitArray(LinkList *head, int m, int n)
 24 {
 25     *head = (LinkList)malloc(sizeof(OLink));
 26     (*head)->i=m;      //行长度
 27     (*head)->j=n;      //列长度
 28 }
 29 /***************初始化完毕*********************/
 30
 31 /*************创建行,列头节点*****************/
 32 void CreateHead(LinkList *head, int m, int n)
 33 {
 34     //以下建立列头结点
 35     LinkList p = *head,q;
 36     //构建列头节点
 37     for(int i=0; i<n; i++)
 38     {
 39       q = (LinkList)malloc(sizeof(OLink));
 40       q->i = -1;   //注意在矩阵的外面,也可以不赋值
 41       q->j = i;    //代表矩阵的列号,从0开始
 42
 43       q->down = q;  //构建循环链表的标志
 44       p->right = q; //链接
 45       p = q;        //继续向前推进
 46     }
 47     p->right = (*head);//循环链表的标志
 48
 49     //以下建立行头结点,基本原理同上
 50     p = (*head);
 51     for(i=0; i<m; i++)
 52     {
 53       q = (LinkList)malloc(sizeof(OLink));
 54       q->i = i;
 55       q->j = -1;
 56
 57       q->right = q;
 58       p->down = q;
 59       p = q;
 60     }
 61     p->down = (*head);
 62 }
 63 /************各个头指针构建完毕,共m+n+1个************/
 64
 65 /***************插入节点的算法************************/
 66 bool InsertNode(LinkList *head, int i, int j, ElemType e)
 67 {
 68     LinkList p = *head,q;
 69     if(i < 0||j < 0||i >= (*head)->i||j >= (*head)->j)
 70         return false;
 71
 72     /********构建节点********/
 73     q = (LinkList)malloc(sizeof(OLink));
 74     q->i = i;
 75     q->j = j;
 76     q->value = e;
 77     /*******完毕***********/
 78
 79
 80     for(int k=0; k<=i; k++)
 81     {
 82         p = p->down;//注意此处等于i截至
 83     }
 84     //产生定位指针
 85     LinkList sr = p,s = p->right;
 86     /******若不满足s==p,或者插入元素大于后面元素,继续推进********/
 87     while(s!=p && q->j>s->j)
 88     {
 89         sr = s;
 90         s=s -> right;
 91     }
 92     /*******推进完毕,有可能有三种情况*************/
 93     q->right = s;
 94     sr->right = q;
 95     /**********行链接处理完毕*********************/
 96
 97
 98     /******以下链接列链表,方法同上**********************/
 99     p = *head;
100     for( k=0; k<=j; k++)
101     {
102          p = p->right;
103     }
104      sr = p;
105      s = p->down;
106     while(s!=p && q->j>s->j)
107     {
108         sr = s;
109         s = s->down;
110     }
111     q->down = s;
112     sr->down = q;
113     /**********列链接处理完毕*********************/
114
115    return true;
116 }
117
118 /**********关于矩阵转置的算法,即a[i][j]<->a[j][i]******************/
119 LinkList   MatrixTransposition(LinkList head, int m, int n)
120 {
121     LinkList THead = head,p,q,h;
122     int temp;
123     //重新构造十字矩阵,列换行,行换列
124     InitArray(&h,n,m);
125     CreateHead(&h,n,m);
126     for(p = THead->down; p!=THead; p = p->down)
127     {
128
129       for(q = p->right; q!=p; q = q->right)
130       {
131           //j变i,i变j,值不变
132         if(!InsertNode(&h, q->j, q->i, q->value))
133             return  NULL;
134       }
135
136     }
137     return  h;
138
139 }
140 /*******销毁链表操作******************/
141 void  DestroyMatrix(LinkList  *head)
142 {
143     LinkList  p,q,r;
144     for(p = (*head)->down; p!=*head; p = p->down)
145     {
146
147       for(q = p->right; q!=p;)
148       {
149            r = q->right;
150            free(q);
151            q=r;
152       }
153
154     }
155 }
156 /*************打印矩阵算法*********************/
157 void  PrintMatrix(LinkList head,int m,int n)
158 {
159     LinkList p,q;
160
161     cout<<"此矩阵为"<<m<<"*"<<n<<"型"<<endl;
162     //向下推进,按行输出
163     for(p = head->down; p!=head; p = p->down)
164     {
165       cout<<"第"<<p->i<<"行 ";
166       for(q = p->right; q!=p; q = q->right)
167       {
168
169           cout<<"  "<<"["<<q->i<<","<<q->j<<"]  "<<q->value<<"   ";
170       }
171       cout<<endl;
172     }
173 }
174 //以矩阵形式输出
175 void   MatrixPrint(LinkList head,int m,int n)
176 {
177     int  a[100][100];
178     LinkList p,q;
179     memset(a,0,sizeof(a));//置零
180     for(p = head->down; p!=head; p = p->down)
181     {
182       for(q = p->right; q!=p; q = q->right)
183       {
184         a[q->i][q->j]=q->value;//赋值
185       }
186     }
187     for(int i=0;i<m;i++)
188     {
189         for(int j=0;j<n;j++)
190         {
191             cout<<a[i][j]<<"    ";
192         }
193         cout<<endl;
194     }
195 }
196
197 void  MainMenu()
198 {
199     LinkList head,h;
200     int m,n,total;//total为矩阵中非零数值个数
201     RECORD RecordMatrix[1000];
202     FILE *fp;
203
204     if((fp = fopen("F:Matrix.txt","r"))==NULL)
205     {
206         cout<<"cannot open the file"<<endl;
207         exit(-1);
208     }
209     //找到矩阵的基本框架
210     fscanf(fp,"%d%d%d",&m,&n,&total);
211     //缓存器record
212     for(int i=0;i<total;i++)
213     {
214         fscanf(fp,"%d%d%d",&RecordMatrix[i].i,&RecordMatrix[i].j,&RecordMatrix[i].value);
215     }
216     //构建十字链表
217     InitArray(&head,m,n);
218     CreateHead(&head,m,n);
219     for(i=0;i<total;i++)
220     {
221         if(!InsertNode(&head,RecordMatrix[i].i,RecordMatrix[i].j,RecordMatrix[i].value))
222             return ;
223     }
224      cout<<"原矩阵元素为"<<endl;
225      PrintMatrix(head,m,n);//打印链表矩阵
226      MatrixPrint(head,m,n);//以矩阵形式打印
227
228      //测试转制后的矩阵
229      h = MatrixTransposition(head,m,n);
230      cout<<endl<<"转置后矩阵元素为"<<endl;
231      PrintMatrix(h,n,m);
232      MatrixPrint(h,n,m);//以矩阵形式打印
233
234      DestroyMatrix(&head);//销毁链表
235      DestroyMatrix(&h);   //销毁链表
236         fclose(fp);           //关闭文件
237 }
238
239 int main()
240 {
241     MainMenu();//引入主函数
242        getchar();
243     return 0;
244 }

 

 

时间: 2024-10-06 01:09:58

数据结构与算法系列研究四——数组和广义表的相关文章

数据结构期末复习第五章数组和广义表

数据结构期末复习第五章 数组和广义表 二维数组A[m][n]按行优先 寻址计算方法,每个数组元素占据d 个地址单元.     设数组的基址为LOC(a11) :LOC(aij)=LOC(a11)+((i-1)*n+j-1)*d     设数组的基址为LOC(a00) :LOC(aij)=LOC(a00)+( i*n+j )*d    二维数组A[m][n]按列优先 寻址计算方法,每个数组元素占据d 个地址单元.     设数组的基址为LOC(a11) :LOC(aij)=LOC(a11)+((j

数据结构(C语言第2版)-----数组,广义表,树,图

任何一个算法的设计取决于选定的数据结构,而算法的实现依赖于采用的存储结构. 之前线性表的数据元素都是非结构的原子类型,元素的值是不可再分的.下面学习的这两个线性表是很特殊的,其中数据元素本身也可能是一种数据结构. 认识数组和广义表 数组可以看成是一种特殊的线性表,也就是线性表中的数据元素本身也是一个线性表,数组中的个元素具有统一的类型.其实说白了就是在脑海中想数组中的数据如何在内存中以什么形式的线性表来存储.在C语言中,一个二维数组可以定义为其分量类型为一维数组类型的一维数组类型. 数组一旦被建

数据结构与算法系列研究三——字符串

字符串的研究和KMP算法分析和实现 一.串的定义 串是计算机非数值处理的基本对象.串是一种特殊的线性表,它的每个结点仅由一个字符组成,并且单个元素是无意义的.    1.串(string):是由0个或多个字符组成的有限序列,记作:          S="a1a2...an"  (n>=0)          其中:S是串名,两个双引号括起来的字符序列为串的值.双引号不属于串.                   ai(1<=i<=n)为字母.数字或其它符号.    

数据结构与算法系列研究五——树、二叉树、三叉树、平衡排序二叉树AVL

树.二叉树.三叉树.平衡排序二叉树AVL 一.树的定义 树是计算机算法最重要的非线性结构.树中每个数据元素至多有一个直接前驱,但可以有多个直接后继.树是一种以分支关系定义的层次结构.    a.树是n(≥0)结点组成的有限集合.{N.沃恩}     (树是n(n≥1)个结点组成的有限集合.{D.E.Knuth})      在任意一棵非空树中:        ⑴有且仅有一个没有前驱的结点----根(root).        ⑵当n>1时,其余结点有且仅有一个直接前驱.         ⑶所有结

数据结构与算法系列研究二——栈和队列

栈和队列的相关问题分析 一.栈和队列定义 栈和队列是两种重要的数据结构.从结构特性角度看,栈和队列也是线性表,其特殊性在于它们的基本操作是线性表的子集,是操作受限的线性表,可称为限定性的数据结构:从数据类型角度看,其操作规则与线性表大不相同,是完全不同于线性表的抽象数据类型.                    图1 栈的结构                                                 图2 队列的结构   1.1.栈是限定在表的一端进行插入和删除操作的线性

数据结构与算法系列研究七——图、prim算法、dijkstra算法

图.prim算法.dijkstra算法 1. 图的定义 图(Graph)可以简单表示为G=<V, E>,其中V称为顶点(vertex)集合,E称为边(edge)集合.图论中的图(graph)表示的是顶点之间的邻接关系. (1) 无向图(undirect graph)      E中的每条边不带方向,称为无向图.(2) 有向图(direct graph)      E中的每条边具有方向,称为有向图.(3) 混合图       E中的一些边不带方向, 另一些边带有方向.(4) 图的阶      指

数据结构与算法系列研究八——链地址法建哈希表

链地址法建哈希表 一.实验内容    建立n元关键字哈希表,用链地址法解决冲突,输出建立的哈希表.(按链表的顺序),输入任意元素实现查找查找结果分成功和失败,要求查找时可以反复输入关键字查找,直至输入停止标识符时程序结束. 二.输入与输出  输入:可以用随机数法产生n元关键字,然后,产生哈希表,输入要查找的关键字判断是否存在.  输出:输出哈希表,输出查找的结果.三.关键数据结构和核心算法  关键数据结构:     链式哈希表由一组数组作为"头指针",每个数组之后都有一个链表,该链表中

数据结构与算法系列研究九——排序算法的一些探讨

四种排序 一.实验内容     输入20个整数,分别用希尔排序.快速排序.堆排序和归并排序实现由小到大排序并输出排序结果.二.关键数据结构与核心算法   关键数据结构:由于是排序为了简单起见,选用线性表中的数组作为存储结构.   核心算法:   1.希尔排序    希尔排序的核心还是直接插入法,但是插入的位置有所讲究.要把数组分为许多段,每一段的长度除了最后的有可能不同之外,其他的都相同.该段的长度即为增量,在最后一次必须为一,此时程序变成了直接插入.每次进行隔段插入,不断地调整是的数组变得隔段

数据结构 第4章 串、数组和广义表 单元小结(1)重点 BF算法

BF算法 考试必考 !!!!!背下来!!!! int lndex_BF(string s,string t,int pos) {//返回模式t在主串s中第pos个字符开始第一次出现的位置下标 //若不存在,则返回值为-1 //其中,t非空,1<=pos<=StrLength(s) int i,j; i = pos-1;//下标 j = 0;//下标 while(i<s.length()&&j<t.length()){ if(s[i] == t[j]) { ++i;