数据结构 | 串的块链存储表示法

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

  • 串的块链存储表示法

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

相关要点:

  • 通过链表方式存储时给每一个结点的字符数组分配固定的大小(块),大小可以根据需要手动调节,#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 }

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

运行结果:

posted @ 2017-05-26 18:46  hugh.dong  阅读(5298)  评论(0编辑  收藏  举报