題目:實現一個函數,要求吧字符串中的所有空格替換成“%20”。例如“hello world " ——> ”hello%20world%20"
實際背景:在網絡編程中,如果URL參數中含有特殊的字符,如空格、'#'等,導致服務器端無法識別時,就把這些特殊的字符轉換成可以識別的字符。規則:%加上十六進制的ascii碼,例如‘#’的ascii碼是0x23(16進制),就替換成%23,看下圖地址欄中,輸入“hello#hao"
分析:
注意:在c語言中存字符串最后一位是認為”\0“的,例如a[] = "12",那么a的長度為3,a[0]='1',a[1]='2',a[2]='\0',‘\0’是空字符,其ascii碼為0如下圖。相比之下:b[] = {'1', '2'}長度則為2.
思路一:可以再造一個數組,遇到空格就替換成”%20“這樣就造成了空間的浪費,可以實現但不是最佳
思路二:
- 從前往后:在原基礎上進行替換(當然前提是空間足夠),如果從前往后遇到空格就替換,必然會對原來的字符造成覆蓋,不可以實現。
- 從后往前:如果倒着遇到空格就往后移動同時替換,那么如果全部是空格,那勢必造成時間復雜度為O(n2)的開銷。可以實現,但不是最佳
思路三:因為把空格替換為”%20“,每次替換多2個字符,因此可以統計出字符串中空格的總個數,然后新數組大小為 “原數組大小 + 2*空格數 ”。從后往前處理:遇到非空格,直接搬到后面,遇到空格替換為”%20“. 直到待插入位置指針和原數組為指針重合位置。圖示如下:
參考代碼:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXLEN 100 void ReplaceBlack(char *p) { if (p != NULL) { int i, end, pos ,size_black = 0; size_t lenold = strlen(p), lennew; for (i=0; i != lenold; i++) { if (p[i] == ' ') size_black++; } lennew = lenold + 2 * size_black; if(lennew > MAXLEN) return; pos = lennew-1; end = lenold-1; while(pos != end) { if (p[end] != ' ') p[pos--] = p[end--]; else { p[pos--] = '0'; p[pos--] = '2'; p[pos--] = '%'; end--; } }//while } } int main() { char *a = (char*)malloc(sizeof(char) * MAXLEN); if(NULL != a) { strcpy(a, "hell world "); ReplaceBlack(a); printf("%s\n", a); free(a); } return 0; }
運行結果:
這樣實現的時間復雜度為O(n),又不浪費多余的空間。
舉一反三:
有兩個排好序(升序)的兩個數組A1,A2,如何高效的把A2的元素復制到A1的后邊(假設A1后邊有足夠的空間)
思路相同
- 先看看兩個數組總共的空間大小,知道了需要數組A1的總共的元素個數
- A1和A2同時從后往前處理,比較大的放在A1新的最后位置
- 重復處理,直到A2中沒有元素了,這是復制完了
參考代碼
#include <stdio.h> #include <string.h> int ReplaceBlack(int *a, int *b, int lena, int lenb, int length) { int i, token = 0; int total = lena + lenb; if (a != NULL && b != NULL && (total <= length)) { lena--; lenb--; i = total - 1; while(lena != -1 && lenb != -1) { if(a[lena] >= b[lenb]) a[i--] = a[lena--]; else a[i--] = b[lenb--]; } while(lenb != -1) a[i--] = b[lenb--]; token = 1; } return token; } int main() { int length = 100; int i, a[100] = {1, 2, 3, 5}, b[] = {0, 4, 7}; int lena = 4, lenb = 3; if(0 != ReplaceBlack(a, b, lena , lenb, length)) for(i = 0; i< lena+lenb; i++) printf("%d\n", a[i]); return 0; }
注意幾個技術細節問題
1.strlen與sizeof的區別與聯系
- 兩者返回類型都是size_t
- strlen是字符串函數,sizeof是運算符
- sizeof可以把類型當做參數,例如sizeof(int),strlen()對象只能是指向字符串類型的指針
- 數組做sizeof參數不退化,傳遞給strlen()就退化為指針了。例如char a[] = "12" sizeof(a) = 3*1 = 3,此時a指數組; strlen(a) = 3,此時a指指針
數組作為參數傳遞給函數時,傳遞的是指針不是數組。例如fuc(char *a),在函數中計算sizeof(a) 是指針的大小4(32位系統)或8(64位系 統)
程序演示
#include <stdio.h> #include <string.h> void fuc1(char *p) { printf("fuction:%s\n", p); printf("fuction:sizeof:%lu,strlen:%lu\n", sizeof(p), strlen(p)); } int main() { char d[] = "hello"; printf("main:%s\n", d); printf("main:sizeof:%lu,strlen:%lu\n", sizeof(d), strlen(d)); fuc1(d); return 0; }
結果
- sizeof在編譯時就決定了,而strlen()函數在計算時才得出
2. 動態數組和靜態數組的區別
聲明
動態數組如:char *a = (char *)malloc(sizeof(char) * 100); 由程序員自己分配空間
靜態數組如:char a[100];由操作系統非配空間
初始化
在聲明中靜態數組是可以的,如:char a[100]=”hello",但是動態的不可以。
主要涉及到molloc()\free()的理解和堆棧,下邊地址介紹的相當詳細:
淺談C中的malloc和free:http://www.bccn.net/Article/kfyy/cyy/jszl/200608/4238.html
堆棧(百度百科):http://baike.baidu.com/view/93201.htm