1. 程式人生 > >數據結構與算法系列研究四——數組和廣義表

數據結構與算法系列研究四——數組和廣義表

cout stdlib.h idt fcc 地址 space stream emp style

稀疏矩陣的十字鏈表實現和轉置

一、數組和廣義表的定義

數組的定義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;
/**************構建完畢**************/
View Code

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

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

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




數據結構與算法系列研究四——數組和廣義表