1. 程式人生 > >數據結構之線性表再思考

數據結構之線性表再思考

() exit 足夠 dem over 存儲空間 next stdio.h 就是

數據結構學起來妙不可言,賊有意思。

很久沒寫博客了,今天來一篇長的。前面寫的關於線性表的代碼和思路,經過我多次反復思考,又有了新的收獲,與大家一起分享。

1、線性表的定義

首先要明白什麽是線性表,一種常用且最簡單的數據結構。

數據結構通俗來說就是:裝水的杯子,有的是圓的、有的是方的。

官方定義:線性表是n個數據元素的有限序列。

把這個樣的一個數據模型映射到計算機,等同找一塊存儲空間給它,而線性表一般是申請動態內存,因此就是在堆上給它分配一塊內存。

看下圖

技術分享圖片

通過圖我們可以了解到,線性表數據結構如何映射在計算機中。有了這個基礎就可以進入代碼邏輯部分。/* 圖像中的堆是為了更好講解 */

2、線性表的操作思路

在嚴奶奶的書有如下操作集合

1、初始化

2、銷毀

3、置空表

4、空表檢測

5、返回元素個數

6、返回指定位序元素的值

7、返回表中第一個與指定值滿足compare()的數據元素的位序,若不存在,返回0

8、返回指定元素的前驅

9、返回指定元素的後驅

10、插入元素

11、刪除元素

12、遍歷

13、表A和表B,A = A U B 減去 A n B(A和B的補集)

14、已知非遞減序列表A和表B為合並非遞減序列表的表C , C = A U B

在所有的操作之前,需要定義一個表的結構體:

結構體:

表首地址

表使用長度

表最大長度

代碼實現:

typedef struct ChangeList{

ElemType *elem;

int Length;

int ListSize;

}CL;

初始化

1、表首地址指向申請內存的首地址及定義申請的內存多少

2、進行動態內存分配成功的檢測

3、表使用長度歸0

4、表最大長度被賦值申請內存長度

/* 動態內存的申請通過malloc函數來實現,需配套使用free函數釋放內存 */

銷毀

1、釋放動態內存

2、表首地址指向NULL(消除表頭指針)

3、表使用長度歸0 (消除表使用長度)

4、表最大長度歸0 (消除表最大長度)

/*

2的作用是假設1失敗,作為補救手段;

銷毀就是表的所有結構全部銷毀,包括首指針、使用長度、最大長度

結構體在內存中存儲是按照最大變量類型所占字節依次排序的,如果表的三個變量沒有全部銷毀,則會造成內存浪費。

*/

置空表

1、使用長度歸0

空表判斷

1、判斷使用長度是否為0

返回元素個數

1、返回使用長度

返回指定位序元素的值

1、判斷位序是否合法

2、返回指定位序的值

7、返回表中第一個與指定值滿足compare()的數據元素的位序,若不存在,返回0

在此之前,需要寫一個compare函數

Compare( ElemType ElemOne , ElemType ElemTwo )

函數作用:兩數相等,返回TURE;兩數不等,返回FALSE

1、在使用長度的範圍內判斷第一個滿足要求的元素,並返回該元素的位序

返回前驅

1、位序2至使用長度之間判定

2、若有與指針值相等的元素,返回其元素前驅

返回後驅

1、位序1至 (使用長度 – 1)之間判定

2、若有與指針值相等的元素,返回其元素前驅

插入元素

1、判斷插入位序是否合法

2、判斷存儲空間是否滿了,如果滿,追加動態內存

3、追加動態分配成功檢測,失敗結束程序

4、新基址

5、最大長度加一

刪除元素

1、判斷刪除序號是否合法

2、返回刪除元素

3、刪除位置之後的元素左移

4、表長減一

遍歷

1、visit函數(自己編寫,打印單個指定位置的值)

2、把visit函數作為函數參數,遍歷

表A和表B,A = A U B 減去 A n B(A和B的補集)

1、獲取B中的元素

2、進行LocateEem判斷

3、插入不符合LocateEem判斷的B中的元素到A中

4、A實際長度增加

已知非遞減序列表A和表B為合並非遞減序列表的表C , C = A U B

1、為C申請動態內存,分配成功檢測

2、合並算法

2-1、比較大小

2-2、插入C,C表增加

2-3、A、B是否有一方先插完,再繼續插入另一表

代碼:

  1 /***************************************
  2 程序日期:2018/10/04/21:47 to 
  3 程序功能:實現動態線性表的各類操作,詳細參見各函數註解
  4 程序結構:作為晚輩,我自己的代碼很多沒有嚴奶奶的精煉。 
  5 程序作者:Jamu Foo
  6 ***************************************/
  7 #include<stdio.h>
  8 #include<stdlib.h>
  9 /**************************************/ 
 10 #define Status int        /* 方便對線性表的數據類型更改,本實驗代碼為整數型 */    
 11 #define ElemType int
 12 /**************************************/
 13 #define MaxSize 100        /* MaxSize為固態線性表最大長度 100 */
 14 #define Increment 10    /* Increment為固態線性表單次增長量 10 */
 15 /**************************************/    
 16 
 17 #define OVERFLOW -1        /* 溢出標誌 */
 18 #define INFEASIBLE -2    /* 不可實行標誌 */
 19 #define TURE 0            /* 操作正確標誌1 */
 20 #define FALSE 1            /* 操作錯誤標誌1 */
 21 #define OK 0            /* 操作正確標誌2 */
 22 #define ERROR 1            /* 操作錯誤標誌2 */
 23 /**************************************/
 24 typedef struct ChangeList{ /* 動態線性表定義 */                          
 25     ElemType *elem;        /* 存儲空間的首地址 */
 26     int Length;            /* 當前長度 */
 27     int ListSize;        /* 當前分配的存儲容量 */
 28 }CL;
 29 /**************************************/
 30 /*函數聲明集合*/
 31 
 32 Status compare( ElemType ElemOne, ElemType ElemTwo );    /* 兩數相等返回0,否則返回1 */
 33 Status InitList( CL *L );        /* 初始化 */
 34 Status DestoryList( CL *L );    /* 銷毀表 */
 35 Status ClearList( CL *L );        /* 置空表 */
 36 Status ListEmpty( CL *L );        /* 空表檢測 */
 37 Status LisTLength( CL *L );        /* 返回表長 */
 38 Status GetElem( CL *L, int i, ElemType *e); /* 用e返回第i個位置的元素的值 */
 39 Status LocateElem( CL *L, ElemType e, Status ( *compare)( ElemType, ElemType ) ); /* 返回表中數據元素與e滿足conpare關系的位序 */
 40 Status PriorElem( CL *L,ElemType cur_e, ElemType *pre_e ); /* cur_e為L中的數據元素,且不是第一個,則用pre_e返回它的前驅,否則操作失敗 */
 41 Status NextElem( CL *L,ElemType cur_e, ElemType *next_e ); /* cur_e為L中的數據元素,且不是最後個,則用next_e返回它的後驅,否則操作失敗 */
 42 Status ListInsert ( CL *L, int i, ElemType ie ); /* 在序號i之前插入ie */
 43 Status ListDelete ( CL *L, int i, ElemType *de );/* 刪除序號i的元素 */
 44 void visit( ElemType *p ); /* 打印單個指定位置的值 */ 
 45 Status ListTraverse( CL *L, void ( *visit )( ElemType* ) ); /* 遍歷 */
 46 void Union(CL *La, CL *Lb);
 47 void MergeList( CL *La, CL *Lb, CL *Lc );/* 已知線性表La和Lb的元素按值非遞減排列(俗話:按從小到大排列) */
 48                                         /*  將La和Lb合並為Lc,Lc的元素按值非遞減排列 */
 49 
 50 /*************************************/
 51 int main(void){    /*主函數*/
 52     CL Ldemo;
 53     ElemType e = 11;
 54     int i = 1;
 55     int ie = 100;
 56     int flag = 1;
 57     int gete = 0;
 58     ElemType deOne = 0;
 59     InitList( &Ldemo );
 60 
 61     flag = LisTLength( &Ldemo );
 62     printf( "表長%d\n", flag );
 63 
 64     for( i; i <= 10; i++ ) {    /* 插入實驗 */
 65         ListInsert( &Ldemo, i, ie );
 66         flag = LisTLength( &Ldemo );
 67         printf( " 表長 %d\n", flag );
 68         ie++;
 69     }/* 第一個for循環後i的值變11,因此要重新給i賦值1 */
 70     for( i = 1 ; i <= 10; i++ ){
 71         GetElem( &Ldemo, i, &gete );
 72         printf( "獲取到的元素的值為%d\n", gete );
 73     }
 74     return 0;
 75 //    ListDelete( &Ldemo, 1, &deOne );    
 76 /* 疑點,當ListDelete函數的第二個參數為聲明且初始化為0的指針變量,會出現莫名錯誤 */                            
 77 /* 解答,因為指針初始化=0,表示指向0X00(不可讀不可寫的內存),進入delete函數後,拷貝了一個不影響的副本變量並賦值給*de,這時候的*de指向內存0X00,*de = *p這個操作,便出錯了 */
 78 /* 每個指針只能指向一個地址,假設 指針a 初始化指向0X00,指針b指向i=1的地方,那麽經過指針a = 指針b的操作,就變成了指針b指向指針a,指針a指向i=1的地方*/
 79 }
 80 /**************************************/
 81 Status compare( ElemType ElemOne, ElemType ElemTwo ){
 82     if( ElemOne != ElemTwo ){    /* 兩數相同返回TURE,否則返回FALSE */
 83         return FALSE;
 84     }
 85     else{
 86         return TURE;
 87     }
 88 }
 89 /**************************************/
 90 Status InitList( CL *L ){    
 91     /* malloc 申請內存成功 則返回 void* 類型的內存首地址,否則返回NULL,void* 可強制轉換為任何類型 */
 92     L->elem = ( ElemType * )malloc( MaxSize * sizeof(ElemType));
 93     if( L->elem == NULL ) exit(OVERFLOW); /* 分配失敗,結束程序 */
 94     L->Length = 0;
 95     L->ListSize = MaxSize;
 96     printf("動態線性表初始化成功\n"); 
 97     return OK;
 98 }
 99 /**************************************/
100 Status DestoryList( CL *L ){ /* 線性表定義中三個變量都要回到初始化 */
101     free( L->elem );    /* 使用後該指針變量一定要重新指向NULL,防止野指針出現,有效 規避誤操作 */
102                         /* 指向NULL的指針,NULL其實就是地址為0X0000的內存,這裏不可讀不可寫, */
103     L->elem = NULL;        /* 如果釋放成功,則返回NULL,無論繼續free多少次都沒關系,如果釋放不成功,再次free會出錯 */
104     L->Length = 0;        /*  */
105     L->ListSize = 0;
106     printf("銷毀線性表成功\n");
107     return OK;
108 }
109 /**************************************/
110 Status ClearList( CL *L ){
111     L->Length = 0;    /*表當前長度歸0*/
112     return OK;
113 }
114 /**************************************/
115 Status ListEmpty( CL *L ){    /* 空表判斷 */
116     if( L->Length == 0 ){    /* 為空表 */
117         printf("List is empty \n");
118         return TURE;
119     }
120     else{                    /* 非空表 */
121         printf("List is not empty \n");
122         return FALSE;
123     }
124 }
125 /**************************************/
126 Status LisTLength( CL *L ){ /* 返回表的長度,嚴書這裏並沒有要求做表長是否合法的判斷 */
127     return L->Length;
128 }
129 /**************************************/
130 Status GetElem( CL *L, int i, ElemType *e ){
131     if( i < 1 || i > MaxSize ){    /* 判斷i是否合法 */
132         exit( ERROR );    
133     }
134     *e = *( L->elem + i - 1 );    /* 用*e返回序號為i的元素的值 */
135     return OK;
136 }
137 /**************************************/
138 Status LocateElem( CL *L, ElemType e,Status ( *compare )( ElemType, ElemType ) ){
139     int i = 1;
140     while( i++ <= L->Length  ){        /* 最多只能加 L->Length次 */
141         if( ! compare( *( L->elem ++ ) , e ) ){
142         return i; 
143         }
144     }
145     return FALSE;
146 }
147 /**************************************/
148 Status PriorElem( CL *L, ElemType cur_e, ElemType *pre_e ){
149     int i = 2;
150     ElemType *p = L->elem + 1;
151     while( i++ <= L->Length ){ 
152         if( cur_e == *( p ++ ) ){
153             *pre_e = *( p -- );
154             return *pre_e;
155         }
156     }
157     printf("操作失敗\n");
158     return FALSE;
159 }
160 /**************************************/
161 Status NextElem( CL *L,ElemType cur_e, ElemType *next_e ){
162     int i = 1;
163     ElemType *p = L->elem;
164     while( i++ < L->Length ){ 
165         if( cur_e == *( p ++ ) ){
166             *next_e = *( p ++ );
167             return *next_e;
168         }        
169     }
170         printf("操作失敗\n");
171         return FALSE;
172 }
173 /**************************************/
174 Status ListInsert ( CL *L, int i, ElemType ie ){
175 
176     ElemType *newbase = 0, *p = 0, *q = 0;
177     if( i < 1 || i > MaxSize + 1   ){    /* 插入序號i不合法 */
178         printf(" 錯誤的插入序號\n" );
179         return ERROR;
180     }
181     if( L->Length >= L->ListSize ){        /* 存儲空間滿,增加分配 */
182         newbase = ( ElemType * )realloc( L->elem, ( L->ListSize + Increment ) * sizeof(ElemType) );    
183         /* newbase的作用:無法提前得知舊內存後是否有足夠的內存用於動態內存調整。假設沒有足夠內存,
184             L->elem = ( ElemType * )realloc( L->elem, ( L->ListSize + Increment ) * sizeof(ElemType) );
185             一旦realloc分配失敗,返回NULL,但參數中的L->elem並沒有被釋放。如果直接把realloc的返回值賦給L->elem
186             則會造成L->elem原來指向的內存丟失,造成泄露
187         */
188         /* realloc 函數 如果分配失敗,則第一個參數返回NULL */
189         if( !newbase ){            /* 分配失敗檢查 */
190             exit( OVERFLOW );    /* 存儲分配失敗 */
191         }
192         L->elem = newbase;            /* 新基址 */    
193         L->ListSize += Increment;    /* 增加存儲容量 */
194     }
195     q = L->elem + i - 1;    /* q 為插入位置 */
196     for( p = L->elem + L->Length - 1; p >= q; --p){ /* 插入位置及元素的右移動 */
197         *( p + 1) = * p;
198     }
199     *q = ie;    /* 插入ie */
200     ++L->Length;    /* 表長加1 */
201     ++L->ListSize;    /* 表最大長度加1 */
202     printf( "第%d位插入%d", i, ie );
203     return OK;
204     /* 
205         realloc函數
206         原型:extern void *realloc(void *mem_address, unsigned int newsize);
207         功能:動態內存調整;內存是有限的。
208     分配成功:返回 指向 被分配內存的指針    
209         newsize > oldsize:
210             1、malloc申請的動態內存後面有足夠的續接內存。則newbase = oldbsae,size = newsize
211             2、malloc申請的動態內存後面無足夠的續接內存。則申請一塊新內存,把舊內存的數據復制過去
212                 newbase = 新內存的首地址,size = newsize ,free(oldbase)
213         newsize < oldsize:
214             1、newbase = oldbase;size = newsize,把舊內存中的數據復制到新內存,會造成數據丟失,
215         
216         oldbase = ( int* )malloc ( oldsize * sizeof(int));
217         newbase = ( int* )realloc ( oldbase, newsize * sizeof(int) );
218     1、當realloc中第一個參數為NULL,第二個參數不為0,等同與malloc函數
219     2、當第一個參數不為NULL,第二個參數為0,相當於free函數
220     分配失敗:返回NULL
221     */
222 }
223 /**************************************/
224 Status ListDelete ( CL *L, int i, ElemType *de ){
225     ElemType *p = 0, *q = 0;
226     if( i < 1 || i > L->Length ){    /* 刪除序號檢查 */
227         printf(" 錯誤的刪除序號\n" );
228         return ERROR;
229     }
230     p = L->elem + i - 1;
231     *de = *p;    /* 用*de返回被刪除的元素 */
232     q = L->elem + L->Length - 1;
233     for( ++p; p <= q; ++p ){    /* 被刪除後的元素左移 */
234         *( p - 1 ) = *p; 
235     }
236     L->Length--;
237     printf( "刪除序號為%d的元素,其值為%d\n", i, *de );
238     return    OK;
239 }
240 /**************************************/
241 void visit( ElemType *p ){
242     printf("%d ", *p );
243 }
244 /**************************************/
245 Status ListTraverse( CL *L, void ( *visit )( ElemType* ) ){
246     ElemType *p = 0;
247     int i = 0;
248     p = L->elem;
249     for( i = 1; i < L->Length; i++){
250         visit(p++);
251     }
252     printf("\n");
253     return OK;
254 }
255 /**************************************/
256 void Union(CL *La, CL *Lb){
257     int La_len = 0, Lb_len = 0;
258     int i = 0;
259     ElemType e = 0;
260     La_len = La->Length;
261     Lb_len = Lb->Length;
262     for( i; i < La_len; i++ ){    
263         GetElem( Lb, i, &e );    //一次返回Lb中的所有元素
264         if( !LocateElem( La, e, compare ) ){ //與La不相同的插入La中
265             ListInsert( La, ++La_len, e );
266         }
267     }
268 }
269 /**************************************/
270 void MergeList( CL *La, CL *Lb, CL *Lc ){
271     int *pa = La->elem;    /* 表a首指針 */
272     int *pb = Lb->elem; /* 表b首指針 */
273     int *pc = Lc->elem; /* 表c首指針 */
274     int *pa_last = La->elem + La->Length - 1; /* 表a尾指針 */
275     int *pb_last = Lb->elem + Lb->Length - 1; /* 表b尾指針 */
276     Lc->ListSize = Lc->Length = La->Length + Lb->Length;    /* Lc的長度為(La + Lb)的長度 */
277     Lc->elem = ( ElemType* )malloc( Lc->ListSize * sizeof( ElemType ) ); /* 申請的動態內存最大長度為Lc->Length */
278     if( !Lc->elem){
279         exit( OVERFLOW );    /* 分配失敗 */
280     }/* 合並算法 */
281     while( pa <= pa_last && pb <= pb_last ){    /* 此處為嚴蔚敏奶奶的代碼,相當的有意思 */
282         if( *pa <= *pb ){                        /* 忽略面向過程,直接面向了對象 */
283             *pc++ = *pa++;/* 思路:假設思考路線為個個比較過程,表的數量一旦過大,那麽將十分復雜 */
284         }      /* 無論表做多少容量的合並工作,都是要從兩個表的首指針到尾指針,提取出來作為循環判斷條件 */
285         else{ /* 有了循環條件,還需要判斷排序條件,提取出來誰大誰排序。循環和判斷條件都滿足,第一個while就出來了 */    
286             *pc++ = *pb++;
287         } /* 第一個while的判斷跳出條件,為其中一個表排序完成,因此還需要為另外一個表剩余元素做排序,得到後兩個while*/
288     }
289     while( pa <= pa_last ){
290         *pc++ = *pa++;
291     }
292     while( pb <= pb_last ){
293         *pc++ = *pb++;
294     }
295 }
296 /**************************************/

數據結構之線性表再思考