數據結構 | 串的塊鏈存儲表示法


————————————————————————————————————————————

  • 串的塊鏈存儲表示法

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

相關要點:

  • 通過鏈表方式存儲時給每一個結點的字符數組分配固定的大小(塊),大小可以根據需要手動調節,#define CHUNKSIZE 4 //塊大小。
  • 由於串長不一定是結點大小的整倍數,所以鏈表的最后一個結點不一定被字符占滿,未滿的位置使用#符號補全
  • 定義頭指針和尾指針指向鏈表,並給出一個變量存儲鏈表的長度。設立尾指針的目的是為了便於進行聯結操作。
  • 在鏈式存儲方式中,結點大小的選擇影響着串處理的效率。注意串值的存儲密度,存儲密度 = 串值所占的存儲位/實際分配的存儲位。
  • 串值的鏈式存儲結構對聯結操作有方便之處,但總體上不如另兩種存儲結構靈活,占用存儲量大且操作復雜。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

存儲結構:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

實現代碼:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #define OK 1
  5 #define ERROR 0
  6 #define TRUE 1
  7 #define FALSE 0
  8 #define OVERFLOW -2
  9 typedef int Status;
 10 /* 存儲結構 */
 11 #define blank '#'
 12 #define CHUNKSIZE 4 //塊大小
 13 typedef struct Chunk
 14 {
 15     char ch[CHUNKSIZE];
 16     struct Chunk *next;
 17 } Chunk;
 18 typedef struct
 19 {
 20     Chunk *head, *rear; //串的頭和尾指針
 21     int curlen; //串的當前長度
 22 } LString;
 23 /* 函數列表 */
 24 void InitString(LString *T);
 25 Status StrAssign(LString *T, char *chars);
 26 Status StrCopy(LString *T, LString S);
 27 Status StrEmpty(LString S);
 28 int StrCompare(LString S, LString T);
 29 int StrLength(LString S);
 30 Status ClearString(LString *S);
 31 Status Concat(LString *T, LString S1, LString S2);
 32 Status SubString(LString *Sub, LString S, int pos, int len);
 33 int Index(LString S, LString T, int pos);
 34 void Zip(LString *S); //壓縮串
 35 Status StrInsert(LString *S, int pos, LString T);
 36 Status StrDelete(LString *S, int pos, int len);
 37 Status Replace(LString *S, LString T, LString V);
 38 void StrPrint(LString T);
 39 void DestroyString();
 40 /* 主函數 */
 41 int main()
 42 {
 43     int pos, len, flag;
 44     char *s1 = "Hello,LString!", *s2 = "Booooo!",  *s3 = "123#4", *s4 = "Hello,LString!!", *s5 = "Insert!", *s6 = "Insert!!", *s7 = "***", *s8 = "o"; //此種賦值方式,最后一個字符結束后,下一個字符位為空,可以通過*s1==NULL判斷字符串結束
 45     LString t1, t2, t3, t4, t5, t6;
 46     InitString(&t1); //初始化t1
 47     InitString(&t2); //初始化t2
 48     printf("--------------------------\n");
 49     printf("StrEmpty...OK.\n");
 50     if(StrEmpty(t1))
 51         printf("t1 is Empty\n");
 52     else
 53         printf("t1 is not Empty\n");
 54     printf("StrLength...OK.\n");
 55     printf("length:%d\n", StrLength(t1));
 56     printf("--------------------------\n");
 57     printf("StrAssign...OK.\n");
 58     StrAssign(&t1, s1);
 59     printf("t1:");
 60     StrPrint(t1);
 61     printf("length:%d\n", StrLength(t1));
 62     StrAssign(&t2, s2);
 63     printf("t2:");
 64     StrPrint(t2);
 65     printf("length:%d\n", StrLength(t2));
 66     printf("--------------------------\n");
 67     printf("StrCopy...OK.\n");
 68     StrCopy(&t3, t1);
 69     printf("t3:");
 70     StrPrint(t3);
 71     printf("--------------------------\n");
 72     InitString(&t4);
 73     StrAssign(&t4, s4);
 74     flag = StrCompare(t1, t4);
 75     printf("StrCompare...OK.\n");
 76     StrPrint(t1);
 77     if (flag == 0)
 78         printf("==\n");
 79     else if(flag > 0)
 80         printf(">\n");
 81     else if(flag < 0)
 82         printf("<\n");
 83     StrPrint(t4);
 84     printf("--------------------------\n");
 85     printf("ClearString...OK.\n");
 86     ClearString(&t3);
 87     if(StrEmpty(t3))
 88         printf("t3 is Empty\n");
 89     else
 90         printf("t3 is not Empty\n");
 91     printf("--------------------------\n");
 92     printf("Concat...OK.\n");
 93     InitString(&t5);
 94     Concat(&t5, t1, t2);
 95     printf("t5:");
 96     StrPrint(t5);
 97     printf("length:%d\n", StrLength(t5));
 98     printf("--------------------------\n");
 99     printf("StrInsert Insert! ...OK.\n");
100     StrAssign(&t3, s5);
101     StrInsert(&t5, 21, t3);
102     StrPrint(t5);
103     printf("length:%d\n", StrLength(t5));
104     printf("--------------------------\n");
105     printf("StrDelete pos:13 len:5 ...OK.\n");
106     StrDelete(&t5, 13, 5);
107     StrPrint(t5);
108     printf("length:%d\n", StrLength(t5));
109     printf("--------------------------\n");
110     printf("SubString He ...OK.\n");
111     SubString(&t6, t5, 1, 2);
112     printf("length:%d\n", StrLength(t6));
113     printf("--------------------------\n");
114     printf("Index Insert!! ...OK.\n");
115     StrPrint(t5);
116     ClearString(&t3);
117     StrAssign(&t3, s6);
118     printf("index pos:%d\n", Index(t5, t3, 1));
119     printf("--------------------------\n");
120     printf("Replace o -> *** ...OK.\n");
121     ClearString(&t3);
122     StrAssign(&t3, s7);
123     ClearString(&t2);
124     StrAssign(&t2, s8);
125     Replace(&t5, t2, t3);
126     StrPrint(t5);
127     printf("--------------------------\n");
128     return OK;
129 }
130 /* 初始化空串,不分配空間 */
131 void InitString(LString *T)
132 {
133     T->head = NULL;
134     T->rear = NULL;
135     T->curlen = 0;
136 }
137 Status StrAssign(LString *T, char *chars)
138 {
139     int len, blockNum, i, j;
140     Chunk *p, *q;
141     len = strlen(chars);
142     if (!len || strchr(chars, blank)) //長度為0或包含#時結束
143         return ERROR;
144     T->curlen = len;
145     blockNum = len / CHUNKSIZE; //計算結點數
146     if (len % CHUNKSIZE)
147         ++blockNum;
148     for (i = 0; i < blockNum; ++i) //循環生成新節點
149     {
150         p = (Chunk *)malloc(sizeof(Chunk));
151         if (!p)
152             exit(OVERFLOW);
153         if (T->head == NULL) //如果是第一個節點
154             T->head = q = p;
155         else
156         {
157             q->next = p;
158             q = p;
159         }
160         for (j = 0; j < CHUNKSIZE && *chars; ++j) //每次新增一個塊鏈即賦值,chars指針隨之++,當chars指向空字符時結束
161         {
162             *(q->ch + j) = *chars;
163             ++chars;
164         }
165         if (!*chars) //當*chars指向空字符(最后一個鏈塊時)
166         {
167             T->rear = p;
168             T->rear->next = NULL;
169             for (; j < CHUNKSIZE; ++j) //當chars結束時j的值直接在此處使用
170                 *(q->ch + j) = blank;
171         }
172     }
173     return OK;
174 }
175 Status StrCopy(LString *T, LString S)
176 {
177     //另一個思路:將S中的內容讀到char*中,調用StrAssign();
178     Chunk *h = S.head, *p, *q;
179     if (!h)
180         return ERROR;
181     T->head = (Chunk *)malloc(sizeof(Chunk)); //創建頭節點
182     p = T->head;
183     *p = *h; //將S頭節點的內容復制給T頭節點
184     h = h->next;
185     while(h)
186     {
187         q = p;
188         p = (Chunk *)malloc(sizeof(Chunk));
189         q->next = p;
190         *p = *h;
191         h = h->next;
192     }
193     p->next = NULL;
194     T->rear = p;
195     return OK;
196 }
197 Status StrEmpty(LString S)
198 {
199     if (!S.curlen)
200         return TRUE;
201     else
202         return FALSE;
203 }
204 int StrCompare(LString S, LString T)
205 {
206     int i = 0;
207     Chunk *ps = S.head, *pt = T.head;
208     while(ps && pt) //當有一個節點指向NULL時結束循環
209     {
210         for (i = 0; i < CHUNKSIZE; ++i) //節點內遍歷
211         {
212             if (*(ps->ch + i) != *(pt->ch + i)) //如果指向的元素不同,則返回相減結果
213             {
214                 if (*(ps->ch + i) == blank)
215                     return -1;
216                 else if (*(pt->ch + i) == blank)
217                     return 1;
218                 return *(ps->ch + i) - *(pt->ch + i);
219             }
220         }
221         ps = ps->next; //該節點對比結束,進入下一節點對比
222         pt = pt->next;
223     }
224     return ps - pt; //當有一個指向NULL時,該指針為0,返回相減結果即可
225 }
226 // int StrCompare(LString S, LString T) //書上源碼
227 // {
228 //     /* 若S>T,則返回值>0;若S=T,則返回值=0;若S<T,則返回值<0 */
229 //     int i = 0; /* i為當前待比較字符在S,T串中的位置 */
230 //     Chunk *ps = S.head, *pt = T.head; /* ps,pt分別指向S和T的待比較塊 */
231 //     int js = 0, jt = 0; /* js,jt分別指示S和T的待比較字符在塊中的位序 */
232 //     while(i < S.curlen && i < T.curlen)
233 //     {
234 //         i++; /* 分別找S和T的第i個字符 */
235 //         while(*(ps->ch + js) == blank) /* 跳過填補空余的字符 */
236 //         {
237 //             js++;
238 //             if(js == CHUNKSIZE)
239 //             {
240 //                 ps = ps->next;
241 //                 js = 0;
242 //             }
243 //         }; /* *(ps->ch+js)為S的第i個有效字符 */
244 //         while(*(pt->ch + jt) == blank) /* 跳過填補空余的字符 */
245 //         {
246 //             jt++;
247 //             if(jt == CHUNKSIZE)
248 //             {
249 //                 pt = pt->next;
250 //                 jt = 0;
251 //             }
252 //         }; /* *(pt->ch+jt)為T的第i個有效字符 */
253 //         if(*(ps->ch + js) != *(pt->ch + jt))
254 //             return *(ps->ch + js) - *(pt->ch + jt);
255 //         else /* 繼續比較下一個字符 */
256 //         {
257 //             js++;
258 //             if(js == CHUNKSIZE)
259 //             {
260 //                 ps = ps->next;
261 //                 js = 0;
262 //             }
263 //             jt++;
264 //             if(jt == CHUNKSIZE)
265 //             {
266 //                 pt = pt->next;
267 //                 jt = 0;
268 //             }
269 //         }
270 //     }
271 //     return S.curlen - T.curlen;
272 // }
273 int StrLength(LString S)
274 {
275     return S.curlen;
276 }
277 Status ClearString(LString *S)
278 {
279     Chunk *p, *q;
280     if (!S->curlen)
281         return ERROR;
282     p = S->head;
283     while(p)
284     {
285         q = p->next;
286         free(p);
287         p = q;
288     }
289     S->head = NULL;
290     S->rear = NULL;
291     S->curlen = 0;
292     return OK;
293 }
294 Status Concat(LString *T, LString S1, LString S2)
295 {
296     LString T1, T2;
297     InitString(&T1);
298     InitString(&T2);
299     StrCopy(&T1, S1);
300     StrCopy(&T2, S2);
301     T->curlen = S1.curlen + S2.curlen;
302     T->head = T1.head;
303     T1.rear->next = T2.head;
304     T->rear = T2.rear;
305     return OK;
306 }
307 void StrPrint(LString T)
308 {
309     Chunk *p;
310     p = T.head;
311     int i;
312     while(p)
313     {
314         for (i = 0; i < CHUNKSIZE; ++i)
315             if (*(p->ch + i) != blank)
316                 printf("%c", *(p->ch + i));
317         p = p->next;
318     }
319     printf("\n");
320 }
321 void DestroyString() //無法銷毀
322 {
323     ;
324 }
325 Status StrInsert(LString *S, int pos, LString T) //書上源碼
326 {
327     /* 1≤pos≤StrLength(S)+1。在串S的第pos個字符之前插入串T */
328     int i, j, k;
329     Chunk *p, *q;
330     LString t;
331     if(pos < 1 || pos > StrLength(*S) + 1) /* pos超出范圍 */
332         return ERROR;
333     StrCopy(&t, T); /* 復制T為t */
334     Zip(S); /* 去掉S中多余的填補空余的字符 */
335     i = (pos - 1) / CHUNKSIZE; /* 到達插入點要移動的塊數 */
336     j = (pos - 1) % CHUNKSIZE; /* 到達插入點在最后一塊上要移動的字符數 */
337     p = (*S).head;
338     if(pos == 1) /* 插在S串前 */
339     {
340         t.rear->next = (*S).head;
341         (*S).head = t.head;
342     }
343     else if(j == 0) /* 插在塊之間 */
344     {
345         for(k = 1; k < i; k++)
346             p = p->next; /* p指向插入點的左塊 */
347         q = p->next; /* q指向插入點的右塊 */
348         p->next = t.head; /* 插入t */
349         t.rear->next = q;
350         if(q == NULL) /* 插在S串后 */
351             (*S).rear = t.rear; /* 改變尾指針 */
352     }
353     else /* 插在一塊內的兩個字符之間 */
354     {
355         for(k = 1; k <= i; k++)
356             p = p->next; /* p指向插入點所在塊 */
357         q = (Chunk *)malloc(sizeof(Chunk)); /* 生成新塊 */
358         for(i = 0; i < j; i++)
359             *(q->ch + i) = blank; /* 塊q的前j個字符為填補空余的字符 */
360         for(i = j; i < CHUNKSIZE; i++)
361         {
362             *(q->ch + i) = *(p->ch + i); /* 復制插入點后的字符到q */
363             *(p->ch + i) = blank; /* p的該字符為填補空余的字符 */
364         }
365         q->next = p->next;
366         p->next = t.head;
367         t.rear->next = q;
368     }
369     (*S).curlen += t.curlen;
370     Zip(S);
371     return OK;
372 }
373 // Status StrInsert(LString *S, int pos, LString T) //插入字符串操作,有BUG
374 // {
375 //     //在塊之間插入新的字符串,並利用zip壓縮將串中多余的#去處
376 //     int i, j, insertPos, blockPos;
377 //     if (pos >= S->curlen) //如果pos越界,則定位在頭或尾位置
378 //         pos = S->curlen + 2;
379 //     else if (pos <= 0)
380 //         return ERROR;
381 //     Chunk *h = S->head, *p, *q;
382 //     insertPos = pos % CHUNKSIZE; //確定塊中要插入的位置
383 //     if (pos % CHUNKSIZE == 0) //如果插入的位置是塊之間
384 //     {
385 //         blockPos = pos / CHUNKSIZE; //定位要插入塊的位置
386 //         q = S->head; //q指向頭結點
387 //         for (i = blockPos; i > 1; --i) //q指向正在被分開的塊
388 //             q = q->next;
389 //     }
390 //     else //如果是塊中
391 //     {
392 //         blockPos = (pos / CHUNKSIZE) + 1; //定位塊
393 //         //將塊要插入的位置前后分離
394 //         p = (Chunk *)malloc(sizeof(Chunk)); //申請一個新的結點,將插入點后的半個結點挪過去
395 //         q = S->head; //q指向頭結點
396 //         for (i = blockPos; i > 1; --i) //q指向正在被分開的塊
397 //             q = q->next;
398 //         j = i = CHUNKSIZE - (pos % CHUNKSIZE); //使用i,j來存儲需要挪的個數(該結點從后往前數)
399 //         for (; i > 0; --i) //將有效字符挪到新節點中,對應原結點中的位置為#
400 //         {
401 //             *(p->ch + CHUNKSIZE - i) = *(q->ch + CHUNKSIZE - i);
402 //             *(q->ch + CHUNKSIZE - i) = blank;
403 //         }
404 //         for (; j < CHUNKSIZE; ++j)
405 //             *(p->ch + CHUNKSIZE - j - 1) = blank;
406 //         p->next = q->next; //將結點重新連接起來
407 //         q->next = p;
408 //     }
409 //     //此時需要在q之后插入新節點即可,插入完畢后壓縮
410 //     T.rear->next = q->next;
411 //     q->next = T.head;
412 //     S->curlen += T.curlen;
413 //     Zip(S);
414 //     return OK;
415 // }
416 void Zip(LString *S) //壓縮串
417 {
418     int i, j = 0;
419     char *q; //將字符串讀入*q
420     q = (char *)malloc(((*S).curlen + 1) * sizeof(char));
421     Chunk *p = S->head;
422     while(p)
423     {
424         for (i = 0; i < CHUNKSIZE; ++i)
425             if (*(p->ch + i) != blank)
426             {
427                 *(q + j) = *(p->ch + i);
428                 j++;
429             }
430         p = p->next;
431     }
432     *(q + j) = 0; //串結束符
433     ClearString(S); //清空字符串S
434     StrAssign(S, q); //將讀入的字符串重新賦值給S
435 }
436 Status StrDelete(LString *S, int pos, int len) //刪除長度為len的子串,將被刪除的位置替換成為#再壓縮即可
437 {
438     if (pos > S->curlen || pos < 1 || pos + len > S->curlen)
439         return ERROR;
440     Chunk *p, *q;
441     int i, j = 0, n = 0;
442     p = S->head;
443     pos--;
444     while(n < pos)
445     {
446         j++;
447         if (j == CHUNKSIZE)
448         {
449             p = p->next;
450             j = 0;
451         }
452         n++;
453     }
454     while(n < pos + len)
455     {
456         *(p->ch + j) = blank;
457         j++;
458         if (j == CHUNKSIZE)
459         {
460             p = p->next;
461             j = 0;
462         }
463         n++;
464     }
465     Zip(S);
466     return OK;
467 }
468 Status SubString(LString *Sub, LString S, int pos, int len) //返回某位置長度為len的子串
469 {
470     Chunk *p;
471     char *q;
472     if (pos > S.curlen || pos < 0 || pos + len - 1 > S.curlen)
473         return ERROR;
474     q = (char *)malloc((len + 1) * sizeof(char));
475     int i = 0, j = 0, n;
476     p = S.head;
477     while(j < pos) //逐個位置索引到pos
478     {
479         if (j == pos - 1)
480             break;
481         ++j;
482         ++i;
483         if (i == CHUNKSIZE)
484         {
485             p = p->next;
486             i = 0;
487         }
488     }
489     j = 0;
490     while(j < len) //逐個位置賦值
491     {
492         *(q + j) = *(p->ch + i);
493         i++;
494         if (i == CHUNKSIZE)
495         {
496             p = p->next;
497             i = 0;
498         }
499         j++;
500     }
501     *(q + j) = 0;
502     InitString(Sub); //初始化子串
503     StrAssign(Sub, q); //將q中賦值
504     Sub->curlen = len;
505     return OK;
506 }
507 // Status SubString(LString *Sub, LString S, int pos, int len) //書上源碼
508 // {
509 //     /* 用Sub返回串S的第pos個字符起長度為len的子串。 */
510 //     /* 其中,1≤pos≤StrLength(S)且0≤len≤StrLength(S)-pos+1 */
511 //     Chunk *p, *q;
512 //     int i, k, n, flag = 1;
513 //     if(pos < 1 || pos > S.curlen || len < 0 || len > S.curlen - pos + 1)
514 //         return ERROR;
515 //     n = len / CHUNKSIZE; /* 生成空的Sub串 */
516 //     if(len % CHUNKSIZE)
517 //         n++; /* n為塊的個數 */
518 //     p = (Chunk *)malloc(sizeof(Chunk));
519 //     (*Sub).head = p;
520 //     for(i = 1; i < n; i++)
521 //     {
522 //         q = (Chunk *)malloc(sizeof(Chunk));
523 //         p->next = q;
524 //         p = q;
525 //     }
526 //     p->next = NULL;
527 //     (*Sub).rear = p;
528 //     (*Sub).curlen = len;
529 //     for(i = len % CHUNKSIZE; i < CHUNKSIZE; i++)
530 //         *(p->ch + i) = blank; /* 填充Sub尾部的多余空間 */
531 //     q = (*Sub).head; /* q指向Sub串即將復制的塊 */
532 //     i = 0;  //i指示即將復制的字符在塊中的位置
533 //     p = S.head; /* p指向S串的當前塊 */
534 //     n = 0; /* n指示當前字符在串中的序號 */
535 //     while(flag)
536 //     {
537 //         for(k = 0; k < CHUNKSIZE; k++) /* k指示當前字符在塊中的位置 */
538 //             if(*(p->ch + k) != blank)
539 //             {
540 //                 n++;
541 //                 if(n >= pos && n <= pos + len - 1) /* 復制 */
542 //                 {
543 //                     if(i == CHUNKSIZE)
544 //                     {
545 //                         /* 到下一塊 */
546 //                         q = q->next;
547 //                         i = 0;
548 //                     }
549 //                     *(q->ch + i) = *(p->ch + k);
550 //                     i++;
551 //                     if(n == pos + len - 1) /* 復制結束 */
552 //                     {
553 //                         flag = 0;
554 //                         break;
555 //                     }
556 //                 }
557 //             }
558 //         p = p->next;
559 //     }
560 //     return OK;
561 // }
562 int Index(LString S, LString T, int pos) //在S中索引子串T
563 {
564     int i, j;
565     LString sub;
566     if (pos < 1 || pos > S.curlen - T.curlen)
567         return ERROR;
568     while(pos <= S.curlen - T.curlen + 1)
569     {
570         SubString(&sub, S, pos, T.curlen);
571         if (StrCompare(sub, T) == 0)
572             return pos;
573         else
574             ++pos;
575     }
576     return ERROR;
577 }
578 Status Replace(LString *S, LString T, LString V) //將S中的T替換成V
579 {
580     if (StrEmpty(T))
581         return ERROR;
582     int pos = 1;
583     do
584     {
585         pos = Index(*S, T, pos);
586         if (pos)
587         {
588             StrDelete(S, pos, T.curlen);
589             StrInsert(S, pos, V);
590             pos += V.curlen;
591         }
592     }
593     while(pos);
594     return OK;
595 }

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

運行結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM