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

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

1、数组和广义表的定义

    数组的定义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维数组。
       数组是线性结构,基于两点:
          一、一个 n维数组被定义为一个线性表,它的元素是一个 n-1维数组。
          二、一个 n维数组的数据元素受n个关系的约束,且每一个关系都是线性的。ios

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

 数组的基本操做:
    一、数组初始化:肯定数组维数、长度,分配存储空间。
         initarray(&A,n,bound[ ]);  
             bound[ ]= b1,b2......bn
   二、撤消数组
         destroyarray (&A);
   三、求数组元素值
        value(A,&e,index[ ]);           
              index[ ]= i1,i2,......in
   四、为数组元素赋值
        assign(&A,e,index[ ]);
 数组的顺序表示及实现:
     用一遍地址连续的存储单元依次存放数据元素。
     一、数据类型描述
        #define MAX_ARRAY_DIM 8
       typedef struct {
          ElemType *base;   //数组元素空间
          int dim;          //数组维数
          int *bounds;      //数组维长
          int *constant;    //常数因子
      }ARRAY;
 矩阵的压缩存储:
    一、矩阵压缩存储的概念
        特殊矩阵:值相同元素或0元素在矩阵中分布有必定规律。
       ⒈对称矩阵:矩阵中的元素知足
                     aij=aji       1≤i,j≤n
       ⒉三角矩阵:上(下)三角矩阵指矩阵的下(上)三角(不包括对角线)中的元素均为常数c或0的n阶矩阵。
       ⒊对角矩阵(带状矩阵):矩阵中全部非0元素集中在以主对角线为中心的区域中。
      稀疏矩阵:非0元素不多( ≤ 5%)且分布无规律。
    二、矩阵的压缩存储
         为多个相同值的元分配一个存储单元;对零元不分配空间。
       对称矩阵的压缩存储
       存储分配策略: 每一对对称元只分配一个存储单元,即只存储下三角(包括对角线)的元, 所需空间数为:
                        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、稀疏矩阵的十字链表实现

  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; /**************构建完毕**************/
View Code

  算法描述:
      创建十字链表,数据结构

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

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

       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; }
View Code

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

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

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

 

 2.五、全部程序

 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 }
View Code
相关文章
相关标签/搜索