————————————————————————————————————————————
- 串的块链存储表示法
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
相关要点:
- 通过链表方式存储时给每一个结点的字符数组分配固定的大小(块),大小可以根据需要手动调节,#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("-------------------------- "); 49 printf("StrEmpty...OK. "); 50 if(StrEmpty(t1)) 51 printf("t1 is Empty "); 52 else 53 printf("t1 is not Empty "); 54 printf("StrLength...OK. "); 55 printf("length:%d ", StrLength(t1)); 56 printf("-------------------------- "); 57 printf("StrAssign...OK. "); 58 StrAssign(&t1, s1); 59 printf("t1:"); 60 StrPrint(t1); 61 printf("length:%d ", StrLength(t1)); 62 StrAssign(&t2, s2); 63 printf("t2:"); 64 StrPrint(t2); 65 printf("length:%d ", StrLength(t2)); 66 printf("-------------------------- "); 67 printf("StrCopy...OK. "); 68 StrCopy(&t3, t1); 69 printf("t3:"); 70 StrPrint(t3); 71 printf("-------------------------- "); 72 InitString(&t4); 73 StrAssign(&t4, s4); 74 flag = StrCompare(t1, t4); 75 printf("StrCompare...OK. "); 76 StrPrint(t1); 77 if (flag == 0) 78 printf("== "); 79 else if(flag > 0) 80 printf("> "); 81 else if(flag < 0) 82 printf("< "); 83 StrPrint(t4); 84 printf("-------------------------- "); 85 printf("ClearString...OK. "); 86 ClearString(&t3); 87 if(StrEmpty(t3)) 88 printf("t3 is Empty "); 89 else 90 printf("t3 is not Empty "); 91 printf("-------------------------- "); 92 printf("Concat...OK. "); 93 InitString(&t5); 94 Concat(&t5, t1, t2); 95 printf("t5:"); 96 StrPrint(t5); 97 printf("length:%d ", StrLength(t5)); 98 printf("-------------------------- "); 99 printf("StrInsert Insert! ...OK. "); 100 StrAssign(&t3, s5); 101 StrInsert(&t5, 21, t3); 102 StrPrint(t5); 103 printf("length:%d ", StrLength(t5)); 104 printf("-------------------------- "); 105 printf("StrDelete pos:13 len:5 ...OK. "); 106 StrDelete(&t5, 13, 5); 107 StrPrint(t5); 108 printf("length:%d ", StrLength(t5)); 109 printf("-------------------------- "); 110 printf("SubString He ...OK. "); 111 SubString(&t6, t5, 1, 2); 112 printf("length:%d ", StrLength(t6)); 113 printf("-------------------------- "); 114 printf("Index Insert!! ...OK. "); 115 StrPrint(t5); 116 ClearString(&t3); 117 StrAssign(&t3, s6); 118 printf("index pos:%d ", Index(t5, t3, 1)); 119 printf("-------------------------- "); 120 printf("Replace o -> *** ...OK. "); 121 ClearString(&t3); 122 StrAssign(&t3, s7); 123 ClearString(&t2); 124 StrAssign(&t2, s8); 125 Replace(&t5, t2, t3); 126 StrPrint(t5); 127 printf("-------------------------- "); 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(" "); 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 }
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
运行结果: