(二)線性表的動態分配順序存儲
1. 頭文件及宏定義:
#include<stdio.h> #include<stdlib.h> #define LIST_INIT_SIZE 10 //初始分配量 #define LIST_INCREMENT 2 //分配增量 typedef int elemtype; typedef enum { OK, //0
TRUE, //1
FALSE, //2
ERROR, //3
OVERFLOW, //4
}Status;
|
2.數據結構定義:
typedef struct { elemtype * elem; //存儲空間基址
int length; //當前長度
int listsize; //當前分配的存儲容量
}SqList;
|
3.基本操作:
/*構造一個空的順序線性表*/ void InitList(SqList *L) { L->elem = (elemtype *)malloc(LIST_INIT_SIZE*sizeof(elemtype)); if(L->elem == NULL) exit(OVERFLOW); L->length = 0; L->listsize = 0; } /*銷燬順序線性表*/ void DestroyList(SqList *L) { free(L->elem); L->elem = NULL; L->length = 0; L->listsize = 0; } /*將線性表重置爲空表*/ void ClearList(SqList *L) { L->length = 0; } /*判斷線性表是否爲空表,是返回true,否返回false*/ Status ListEmpty(SqList L) { if(L.length == 0) return TRUE; else return FALSE; } /*返回順序線性表中元素個數*/ int ListLength(SqList *L) { return L->length; } /*返回第i個元素的值到e*/ Status GetElem(SqList *L,int i,elemtype *e) { if(i<1 || i>L->length) return ERROR; *e = *(L->elem+i-1); return OK; } /*若cur_e是L中的元素,且不是第一個,則用pre_e返回它的前驅*/ Status PriorElem(SqList *L,elemtype cur_e, elemtype * pre_e) { int i; elemtype * p = L->elem+1;//從第二個元素開始判斷
for(i=0;i<L->length-1;i++,p++) if(*p == cur_e) break; if(i < L->length-1) *pre_e = *--p; else return ERROR; return OK; } /*若cur_e是L中的元素,且不是最後一個,則用next_e返回它的前驅*/ Status NextElem(SqList *L,elemtype cur_e, elemtype * next_e) { int i; elemtype * p = L->elem; for(i=0;i<L->length-1;i++,p++)//循環到倒數第二個元素
if(*p == cur_e) break; if(i < L->length-1) *next_e = *++p; else return ERROR; return OK; } /*在L中的i個位置之前插入一個新的元素e*/ Status ListInsert(SqList *L,int i,elemtype e) { elemtype *newbase,*q,*p; if(i<1 || i>L->length+1)//listsize必須要比length大才能插入,且插入後必須要使元素的內存連續,所以i可以爲length+1
return ERROR; if(L->length == L->listsize) { newbase=(elemtype*)realloc(L->elem,(L->listsize+LIST_INCREMENT)*sizeof(elemtype));//重新分配內存,增加空間
if(!newbase) exit(OVERFLOW); L->elem = newbase; L->listsize += LIST_INCREMENT; } q = L->elem+i-1;//q爲插入的位置
for(p = L->elem+L->length-1;p>=q;p--) *(p+1) = *p;//插入位置及之後的所有元素向表尾移動
*q = e; //插入e
++(L->length); return OK; } /*刪除L中的第i個數據元素,並用e返回其值*/ Status ListDelete(SqList *L,int i,elemtype *e) { elemtype *p,*q; if(i<1 || i>L->length) return ERROR; q = L->elem+i-1; //要刪除的位置
*e = *q; p = L->elem+L->length;//表尾位置
for(++q;q<=p;++q) *(q-1) = *q; //要刪除位置之後的所有元素前移一位
L->length--; return OK; }
/*返回L中第一個與e滿足關係compare()關係的元素的位置*/ int LocateElem(SqList *L,elemtype e,Status(*compare)(elemtype,elemtype)) { int i = 1; elemtype *p = L->elem; while(i<=L->length && !compare(*p++,e)) ++i; if(i<=L->length) return i; else return 0; }
/*依次對L中的每一個元素調用函數visit(),來改變每一個元素的值。 對L中的元素用某個關係進行歷遍*/
void ListTraverse(SqList *L,void(*visit)(elemtype *)) { elemtype *p = L->elem; int i; for(i=1; i<=L->length; i++,p++) visit(p); }
|
以上操作中最後兩個函數比較特殊,
int LocateElem(SqList *L,elemtype e,Status(*compare)(elemtype,elemtype))
void ListTraverse(SqList *L,void(*visit)(elemtype *))
特殊在這兩個函數的參數 中都有一個 type1(* name)(type2,...,typen)這樣的參數,這是用函數做參數的寫法,其中name是一個指針形參,name所指向的的數據類型爲 一個函數,這個函數的返回類型爲type1,參數類型爲type2,...,typen.
在調用該類函數時,只要把指定類型的函數的地址當作實參傳入即可,也就是函數的名字,不需要加括號。
和形參compare匹配的實參類型,如myequle,IsSquare定義如下:
Status myequle(elemtype c1,elemtype c2) { //判斷是否相等
if(c1 == c2) return TRUE; else return FALSE; }
Status IsSquare(elemtype a,elemtype b) { //判斷a是否是b的平方關係,
if(a == b*b) return TRUE; else return FALSE; }
|
調用:
LocateElem(L,e,myequle);
LocateElem(L,e,IsSquare);
//和形參visit匹配的實參類型,如print,inc,定義如下:
void print(elemtype *c) { printf("%d",*c); } void inc(elemtype *c) { (*c)++; }
|
4.歸併:
void MergeList(SqList *La,SqList *Lb,SqList *Lc) { elemtype *pa,*pa_last,*pb,*pb_last,*pc; pa = La->elem; pb = Lb->elem; Lc->listsize = Lc->length = La->length+Lb->length; pc = Lc->elem = (elemtype*)malloc(Lc->listsize*sizeof(elemtype)); if(!pc) exit(OVERFLOW); pa_last = pa+La->length-1; pb_last = pb+Lb->length-1; while(pa<=pa_last && pb<=pb_last) { if(*pa<=*pb) *pc++ = *pa++; else *pc++ = *pb++; } while(pa<=pa_last) *pc++ = *pa++; while(pb<=pb_last) *pc++ = *pb++; }
|