第4章 數(shù)組和廣義表
本章主要介紹下列內(nèi)容:
1.數(shù)組的定義、基本運算和存儲結(jié)構(gòu)
2.特殊矩陣的壓縮存儲
3.廣義表的定義、術(shù)語、存儲結(jié)構(gòu)、運算
4.遞歸算法設(shè)計
課時分配:
1兩個學(xué)時,2兩個學(xué)時,3兩個學(xué)時, 4兩個學(xué)時
重點、難點:
特殊矩陣的壓縮存儲、廣義表的存儲結(jié)構(gòu)、遞歸算法設(shè)計
第一節(jié) 數(shù)組
1.?dāng)?shù)組的定義和基本運算
數(shù)組的特點是每個數(shù)據(jù)元素可以又是一個線性表結(jié)構(gòu)。因此,數(shù)組結(jié)構(gòu)可以簡單地定義為:若線性表中的數(shù)據(jù)元素為非結(jié)構(gòu)的簡單元素,則稱為一維數(shù)組,即為向量;若一維數(shù)組中的數(shù)據(jù)元素又是一維數(shù)組結(jié)構(gòu),則稱為二維數(shù)組;依次類推,若二維數(shù)組中的元素又是一個一維數(shù)組結(jié)構(gòu),則稱作三維數(shù)組。
結(jié)論:線性表結(jié)構(gòu)是數(shù)組結(jié)構(gòu)的一個特例,而數(shù)組結(jié)構(gòu)又是線性表結(jié)構(gòu)的擴(kuò)展。舉例:
其中,A是數(shù)組結(jié)構(gòu)的名稱,整個數(shù)組元素可以看成是由m個行向量和n個列向量組成,其元素總數(shù)為m×n。 在C語言中,二維數(shù)組中的數(shù)據(jù)元素可以表示成a[表達(dá)式1][表達(dá)式2],表達(dá)式1和表達(dá)式2被稱為下標(biāo)表達(dá)式,比如,a[i][j]。 數(shù)組結(jié)構(gòu)在創(chuàng)建時就確定了組成該結(jié)構(gòu)的行向量數(shù)目和列向量數(shù)目,因此,在數(shù)組結(jié)構(gòu)中不存在插入、刪除元素的操作。
二維數(shù)組結(jié)構(gòu)的基本操作:
(1)給定一組下標(biāo),修改該位置元素的內(nèi)容 Assign(A,elem,index1,index2)
(2)給定一組下標(biāo),返回該位置的元素內(nèi)容 Value(A,elem,index1,index2)
2.?dāng)?shù)組的存儲結(jié)構(gòu)
從理論上講,數(shù)組結(jié)構(gòu)也可以使用兩種存儲結(jié)構(gòu),即順序存儲結(jié)構(gòu)和鏈?zhǔn)酱鎯Y(jié)構(gòu)。然而,由于數(shù)組結(jié)構(gòu)沒有插入、刪除元素的操作,所以使用順序存儲結(jié)構(gòu)更為適宜。換句話說,一般的數(shù)組結(jié)構(gòu)不使用鏈?zhǔn)酱鎯Y(jié)構(gòu)。 組成數(shù)組結(jié)構(gòu)的元素可以是多維的,但存儲數(shù)據(jù)元素的內(nèi)存單元地址是一維的,因此,在存儲數(shù)組結(jié)構(gòu)之前,需要解決將多維關(guān)系映射到一維關(guān)系的問題。舉例:
數(shù)組結(jié)構(gòu)的定義:
#define MAX_ROW_INDEX 10
#define MAX_COL_INDEX 10
typedef struct{Elemtype elem[MAX_ROW_INDEX][MAX_COL_INDEX] } ARRAY;
基本操作算法舉例:
(1)給數(shù)組元素賦值
void Assign(ARRAY *A,Elemtype elem,int index1,int index2)
{ if (index1<0||index1>=MAX_ROW_INDEX||index2<0||index2>=MAX_COL_INDEX) exit(ERROR);
else A->elem[index1][index2]=elem; }
(2)返回給定位置的元素內(nèi)容
int Value(ARRAY A,Elemtype *elem,int index1,int index2)
{ if (index1<0||index1>=MAX_ROW_INDEX|| index2<0||index2>=MAX_COL_INDEX) return FALSE;
else { *elem= A.elem[index1][index2]; return OK;
3.矩陣的壓縮存儲
矩陣是在很多科學(xué)與工程計算中遇到的數(shù)學(xué)模型。在數(shù)學(xué)上,矩陣是這樣定義的:它是一個由s×n個元素排成的s行(橫向)n列(縱向)的表。下面就是一個矩陣
3.1特殊矩陣
k是對稱矩陣位于(i,j)位置的元素在一維數(shù)組中的存放位置。 操作算法的實現(xiàn):
int Value(int A[],Elemtype *elem,int i,int j) {
if(i<1||i>MAX_ROW_INDEX|| j<1||j>MAX_COL_INDEX) return FALSE;
else { if (i>=j) k=i*(i-1)/2+j-1;
else k=j*(j-1)/2+i-1;
*elem=A[k];
return TRUE; }
}
(2)下(上)三角矩陣 下三角矩陣的壓縮存儲與上面講述的對稱矩陣的壓縮存儲一樣,只是將上三角部分的常量值存儲在0單元,下三角和主對角上的元素從1號單元開始存放。 舉例:
操作算法的實現(xiàn):
int Value(int A[],Elemtype *elem,int i,int j)
{
if(i<1||i>MAX_ROW_INDEX||j<1||j>MAX_COL_INDEX) return FALSE;
else { if (i>=j) k=i*(i-1)/2+j;
else k=0;
*elem=A[k];
return TRUE;
}
}
(3)對角矩陣
我們以三階對角矩陣為例討論一下它的壓縮存儲方法。對于對角矩陣,壓縮存儲的主要思路是只存儲非零元素。這些非零元素按以行為主序的順序,從下標(biāo)為1 的位置開始依次存放在一維數(shù)組中,而0位置存放數(shù)值0。
操作算法的實現(xiàn):
int Value(int A[ ],Elemtype *elem,int i,int j)
{
if(i<1||i>MAX_ROW_INDEX||j<1||j>MAX_COL_INDEX) return FALSE;
else { if (j>=(i-1)&&j<=(i+1)) k=3*(i-1)+j-i+1;
else k=0;
*elem=A[k];
return TRUE;
}
}
3.2 稀疏矩陣的壓縮存儲
類型定義:
#define MAX_SIZE 100 最大的非零元素個數(shù)
typedef struct{
int i,j; //行序號、列序號
Elemtype value; //非零元素值
}Three_Elem;
typedef struct {
Three_Elem Elem[MAXSIZE]; //存儲非零元素的一維數(shù)組
int rows,cols,tu; //稀疏矩陣的總行數(shù)、列數(shù)及非零元素個數(shù)
}Matrix;
操作算法的實現(xiàn):
(1)返回元素內(nèi)容
int Value(Matrix M,Elemtype *elem,int i,int j)
if (i<1||i>rows||j<1||j>cols) exit(ERROR);
else { for (p=0;p<M.tu;p++)
if(M.elem[p].i==i&&M.elem[p].j==j)
{ *elem=M.elem[p].value; return OK; }
else if (M.elem[p].i>i||M.elem[p].i==i&&M.Elem[p].j>j) break;
*elem=0;
return OK;
}
}
(2)輸出三元組表示的稀疏矩陣
void Print(Matrix M)
{
for (p=0,i=1;i<=M.rows;i++) {
for (j=1;j<=M.cols;j++)
if(p<M.tu&&M.elem[p].i==i&&M.elem[p].j==j) printf("%4d",M.elem[p++].value;);
else printf("%4d",0);
printf("\n");
}
}
第二節(jié) 廣義表
1. 廣義表的定義
廣義表(Lists,又稱列表)是線性表的推廣。在第2章中,我們把線性表定義為n>=0個元素a1,a2,a3,…,an的有限序列。線性表的元素僅限于原子項,原子是作為結(jié)構(gòu)上不可分割的成分,它可以是一個數(shù)或一個結(jié)構(gòu),若放松對表元素的這種限制,容許它們具有其自身結(jié)構(gòu),這樣就產(chǎn)生了廣義表的概念。
廣義表是n(n>=0)個元素a1,a2,a3,…,an的有限序列,其中ai或者是原子項,或者是一個廣義表。通常記作LS=(a1,a2,a3,…,an)。LS是廣義表的名字,n為它的長度。若ai是廣義表,則稱它為LS的子表。
通常用圓括號將廣義表括起來,用逗號分隔其中的元素。為了區(qū)別原子和廣義表,書寫時用大寫字母表示廣義表,用小寫字母表示原子。若廣義表LS(n>=1)非空,則a1是LS的表頭,其余元素組成的表(a1,a2,…an)稱為LS的表尾。
顯然廣義表是遞歸定義的,這是因為在定義廣義表時又用到了廣義表的概念。廣義表的例子如下:
(1)A=()--A是一個空表,其長度為零。
(2)B=(e)--表B只有一個原子e,B的長度為1。
(3)C=(a,(b,c,d))--表C的長度為2,兩個元素分別為原子a和子表(b,c,d)。
(4)D=(A,B,C)--表D的長度為3,三個元素都是廣義表。顯然,將子表的值代入后,則有D=(( ),(e),(a,(b,c,d)))。
(5)E=(E)--這是一個遞歸的表,它的長度為2,
E相當(dāng)于一個無限的廣義表E=(a,(a,(a,(a,…)))).
從上述定義和例子可推出廣義表的三個重要結(jié)論:
(1)廣義表的元素可以是子表,而子表的元素還可以是子表。由此,廣義表是一個多層次的結(jié)構(gòu),可以用圖形象地表示。
(2)廣義表可為其它表所共享。例如在上述例(4)中,廣義表A,B,C為D的子表,則在D中可以不必列出子表的值,而是通過子表的名稱來引用。
(3)廣義表的遞歸性。
綜上所述,廣義表不僅是線性表的推廣,也是樹的推廣。
2. 廣義表的存儲結(jié)構(gòu)
由于廣義表(a1,a2,a3,…an)中的數(shù)據(jù)元素可以具有不同的結(jié)構(gòu),(或是原子,或是廣義表),因此,難以用順序存儲結(jié)構(gòu)表示,通常采用鏈?zhǔn)酱鎯Y(jié)構(gòu),每
個數(shù)據(jù)元素可用一個結(jié)點表示。
由于廣義表中有兩種數(shù)據(jù)元素,原子或廣義表,因此,需要兩種結(jié)構(gòu)的結(jié)點:一種是表結(jié)點,一種是原子結(jié)點。 下面介紹兩種廣義表的鏈?zhǔn)酱鎯Y(jié)構(gòu)。
表結(jié)點由三個域組成:標(biāo)志域、指示表頭的指針域和指示表尾的指針域;而原子域只需兩個域:標(biāo)志域和值域。
其類型定義如下:
typedef enum{ATOM,LIST}elemtag;
typedef struct glnode{
elemtag tag;
union{
atomtype atom;
struct {
struct glnode *hp,*tp;
}ptr;
};
} *glist;
例見書。
3. 分析求廣義表深度和長度的遞歸算法(見教材)
這部分內(nèi)容比較難,用1個課時講解,用1個課時答疑