1. 程式人生 > >程式設計師程式設計藝術-----第四章-----現場編寫類似strstr/strcpy/strpbrk的函式

程式設計師程式設計藝術-----第四章-----現場編寫類似strstr/strcpy/strpbrk的函式

               第四章、現場編寫類似strstr/strcpy/strpbrk的函式   

作者:July。
    說明: 如果在部落格中程式碼使用了\n,csdn blog系統將會自動回給我變成/n。據後續驗證,可能是原來舊blog版本的bug,新版已不存在此問題。至於,本文程式碼,日後統一修正。July、2012.05.02。  
    微博:http://weibo.com/julyweibo  。
    出處:http://blog.csdn.net/v_JULY_v 
    wiki:http://tctop.wikispaces.com/
----------------------------------------------


前奏

    有網友向我反應,之前三章(http://t.cn/hgVPmH)的面試題目,是否有點太難了。誠如他所說,絕大部分公司的面試題不會像微軟等公司的面試題目出的那麼變態,或複雜。

    面試考察的是你對基礎知識的掌握程度,及程式設計能力是否過硬的一種檢測,所以,紮實基礎知識,提高程式設計能力,比去看什麼所謂的面經,或去背面試題目的答案強多了。

    很多中、小型公司自己的創造能力,包括人力,物力資源都有限,所以,他們的面試題目除了copy一些大公司的題庫之外(當然,考察你對基礎知識的掌握情況,是肯定不會放過的),還有一個途徑就是讓你在限定時間內(如十分鐘),當場實現一些類似strcpy/strcat/strpbrk等庫函式,這個主要看你對細節的把握,以及程式設計能力是否之紮實了。

    同時,本章裡出現的程式碼(除了第4節的c標準庫部分原始碼)都是個人限定在短時間內(正好,突出現場感)編寫的,很多問題,難免有所考慮不周。所以,如果你發現本章任何一段程式碼有任何問題,懇請不吝指正。


第一節、字串查詢
1.1題目描述:
給定一個字串A,要求在A中查詢一個子串B。
如A="ABCDF",要你在A中查詢子串B=“CD”。

分析:比較簡單,相當於實現strstr庫函式,主體程式碼如下:

  1. //在字串中查詢指定字串的第一次出現,不能找到則返回-1      
  2. int strstr(char *string, char *substring)      
  3. {     
  4.     if (string == NULL || substring == NULL)        
  5.         return -1;        
  6.     int lenstr = strlen(string);     
  7.     int lensub = strlen(substring);     
  8.     if (lenstr < lensub)        
  9.         return -1;         
  10.     int len = lenstr - lensub;  
  11.     for (int i = 0; i <= len; i++)   //複雜度為O(m*n)     
  12.     {     
  13.         for (int j = 0; j < lensub; j++)     
  14.         {     
  15.             if (string[i+j] != substring[j])     
  16.                 break;     
  17.         }     
  18.         if (j == lensub)     
  19.             return i + 1;     
  20.     }     
  21.     return -1;     
  22. }    

    讀者反饋@xiaohui5319:樓主啊,對於你那個strstr的函式,我覺得有點小問題。我查了一下C標準庫的原始碼,它給的宣告是這樣的,兩個引數都有const。

char *

STRSTR (const char *haystack_start, const char *needle_start)

    而且標準庫中沒有呼叫strlen函式,因為假如你是標準庫的設計者,strlen()函式還沒設計出來,你怎麼去計算兩個字串的長度?是不是隻能通過指標移動來實現,我覺得這些都是微軟要考察的地方。

    此外:還有int lenstr=strlen(string);這是不安全的?
    strlen函式的返回型別是size_t型,也就是無符號整型,假如我的陣列長度很長(假如是用堆分配的,可以很大很大),長過2的31次方減1的話,會發生一處,你這lenstr就會變成負值了
    用size_t型別最保險。

    以後,本程式設計藝術系列中有任何問題,暫未來得及及時修正,請讀者多加思考,多加辨明

    上述程式已經實現了在字串中查詢第一個子串的功能,時間複雜度為O(n*m),也可以用KMP演算法,複雜度為O(m+n)。為人打通思路,提高他人創造力,我想,這是狂想曲與其它的面試解答所不同的地方,也是我們寫狂想曲系列文章的意義與價值之所在。

1.2、題目描述

在一個字串中找到第一個只出現一次的字元。如輸入abaccdeff,則輸出b。 

程式碼則可以如下編寫:

  1. //查詢第一個只出現一次的字元,     
  2. //[email protected] yansha     
  3. //July、updated,2011.04.24.     
  4. char FirstNotRepeatChar(char* pString)     
  5. {     
  6.     if(!pString)     
  7.         return '/0';     
  8.     const int tableSize = 256;    
  9.     //有點要提醒各位注意,一般常數的空間消耗,如這裡的256,我們也認為此空間複雜度為O(1)。  
  10.     int hashTable[tableSize] = {0}; //存入陣列,並初始化為0     
  11.     char* pHashKey = pString;     
  12.     while(*(pHashKey) != '/0')     
  13.         hashTable[*(pHashKey++)]++;     
  14.     while(*pString != '/0')     
  15.     {     
  16.         if(hashTable[*pString] == 1)     
  17.             return *pString;     
  18.         pString++;     
  19.     }     
  20.     return '/0';  //沒有找到滿足條件的字元,退出     
  21. }    

程式碼二,bitmap:

  1. # include<stdio.h>  
  2. # include<string.h>  
  3. const int N = 26;  
  4. int bit_map[N];  
  5. void findNoRepeat(char *src)  
  6. {  
  7.     int pos;  
  8.     char *str = src;  
  9.     int i ,len = strlen(src);  
  10.     //統計  
  11.     for(i = 0 ; i < len ;i ++)  
  12.         bit_map[str[i]-'a'] ++;  
  13.     //從字串開始遍歷 其bit_map==1 那麼就是結果  
  14.     for(i = 0 ; i < len ; i ++)  
  15.     {  
  16.         if(bit_map[str[i]-'a'] == 1)  
  17.         {  
  18.             printf("%c",str[i]);  
  19.             return ;  
  20.         }  
  21.     }  
  22. }  
  23. int main()  
  24. {     
  25.     char *src = "abaccdeff";  
  26.     findNoRepeat(src);  
  27.     printf("/n");  
  28.     return 0;  
  29. }  
  

第二節、字串拷貝
題目描述:
要求實現庫函式strcpy,
原型宣告:extern char *strcpy(char *dest,char *src); 
功能:把src所指由NULL結束的字串複製到dest所指的陣列中。  
說明:src和dest所指記憶體區域不可以重疊且dest必須有足夠的空間來容納src的字串。  
返回指向dest的指標。

    分析:如果編寫一個標準strcpy函式的總分值為10,下面給出幾個不同得分的答案

  1. //得2分     
  2. void strcpy( char *strDest, char *strSrc )     
  3. {     
  4.     while( (*strDest++ = * strSrc++) != '/0' );     
  5. }      
  6. //得4分     
  7. void strcpy( char *strDest, const char *strSrc )      
  8. {     
  9.     //將源字串加const,表明其為輸入引數,加2分     
  10.     while( (*strDest++ = * strSrc++) != '/0' );     
  11. }      
  12. //得7分     
  13. void strcpy(char *strDest, const char *strSrc)      
  14. {     
  15.     //對源地址和目的地址加非0斷言,加3分     
  16.     assert( (strDest != NULL) && (strSrc != NULL) );     
  17.     while( (*strDest++ = * strSrc++) != '/0' );     
  18. }      
  19. //得9分     
  20. //為了實現鏈式操作,將目的地址返回,加2分!     
  21. char * strcpy( char *strDest, const char *strSrc )      
  22. {     
  23.     assert( (strDest != NULL) && (strSrc != NULL) );     
  24.     char *address = strDest;      
  25.     while( (*strDest++ = * strSrc++) != '/0' );      
  26.     return address;     
  27. }    
  28. //得10分,基本上所有的情況,都考慮到了  
  29. //如果有考慮到源目所指區域有重疊的情況,加1分!     
  30. char * strcpy( char *strDest, const char *strSrc )      
  31. {     
  32.     if(strDest == strSrc) { return strDest; }  
  33.     assert( (strDest != NULL) && (strSrc != NULL) );     
  34.     char *address = strDest;      
  35.     while( (*strDest++ = * strSrc++) != '/0' );      
  36.     return address;     
  37. }    

第三節、小部分庫函式的實現
    考察此類編寫同庫函式一樣功能的函式經常見於大大小小的IT公司的面試題目中,以下是常見的字串庫函式的實現,希望,對你有所幫助,有任何問題,歡迎不吝指正:

  1. //@yansha:字串末尾要加結束符'/0',不然輸出錯位結果  
  2. char *strncpy(char *strDes, const char *strSrc, unsigned int count)      
  3. {      
  4.     assert(strDes != NULL && strSrc != NULL);      
  5.     char *address = strDes;      
  6.     while (count-- && *strSrc != '/0')      
  7.         *strDes++ = *strSrc++;   
  8.     *strDes = '/0';  
  9.     return address;      
  10. }   
  11. //查詢字串s中首次出現字元c的位置   
  12. char *strchr(const char *str, int c)   
  13. {   
  14.     assert(str != NULL);   
  15.     for (; *str != (char)c; ++ str)   
  16.         if (*str == '/0')   
  17.             return NULL;   
  18.         return str;   
  19. }   
  20. int strcmp(const char *s, const char *t)   
  21. {   
  22.     assert(s != NULL && t != NULL);   
  23.     while (*s && *t && *s == *t)   
  24.     {   
  25.         ++ s;   
  26.         ++ t;   
  27.     }   
  28.     return (*s - *t);   
  29. }   
  30. char *strcat(char *strDes, const char *strSrc)   
  31. {   
  32.     assert((strDes != NULL) && (strSrc != NULL));   
  33.     char *address = strDes;   
  34.     while (*strDes != '/0')   
  35.         ++ strDes;   
  36.     while ((*strDes ++ = *strSrc ++) != '/0')   
  37.         NULL;   
  38.     return address;   
  39. }   
  40. int strlen(const char *str)   
  41. {   
  42.     assert(str != NULL);   
  43.     int len = 0;   
  44.     while (*str ++ != '/0')   
  45.         ++ len;   
  46.     return len;   
  47. }   
  48. //此函式,夢修改如下     
  49. char *strdup_(char *strSrc)     
  50. //將字串拷貝到新的位置     
  51. {     
  52.     if(strSrc!=NULL)     
  53.     {     
  54.         char *start=strSrc;     
  55.         int len=0;     
  56.         while(*strSrc++!='/0')     
  57.             len++;     
  58.         char *address=(char *)malloc(len+1);     
  59.         assert(address != NULL);  
  60.         while((*address++=*start++)!='/0');      
  61.         return address-(len+1);      
  62.     }     
  63.     return NULL;     
  64. }     
  65. //多謝laoyi19861011指正  
  66. char *strstr(const char *strSrc, const char *str)   
  67. {   
  68.     assert(strSrc != NULL && str != NULL);   
  69.     const char *s = strSrc;   
  70.     const char *t = str;   
  71.     for (; *strSrc != '/0'; ++ strSrc)   
  72.     {   
  73.         for (s = strSrc, t = str; *t != '/0' && *s == *t; ++s, ++t)   
  74.             NULL;   
  75.         if (*t == '/0')   
  76.             return (char *) strSrc;   
  77.     }   
  78.     return NULL;   
  79. }   
  80. char *strncat(char *strDes, const char *strSrc, unsigned int count)   
  81. {   
  82.     assert((strDes != NULL) && (strSrc != NULL));   
  83.     char *address = strDes;   
  84.     while (*strDes != '/0')   
  85.         ++ strDes;   
  86.     while (count -- && *strSrc != '/0' )   
  87.         *strDes ++ = *strSrc ++;   
  88.     *strDes = '/0';   
  89.     return address;   
  90. }   
  91. int strncmp(const char *s, const char *t, unsigned int count)   
  92. {   
  93.     assert((s != NULL) && (t != NULL));   
  94.     while (*s && *t && *s == *t && count --)   
  95.     {   
  96.         ++ s;   
  97.         ++ t;   
  98.     }   
  99.     return (*s - *t);   
  100. }   
  101. char *strpbrk(const char *strSrc, const char *str)   
  102. {   
  103.     assert((strSrc != NULL) && (str != NULL));   
  104.     const char *s;   
  105.     while (*strSrc != '/0')   
  106.     {   
  107.         s = str;   
  108.         while (*s != '/0')   
  109.         {   
  110.             if (*strSrc == *s)   
  111.                 return (char *) strSrc;   
  112.             ++ s;   
  113.         }   
  114.         ++ strSrc;   
  115.     }   
  116.     return NULL;   
  117. }   
  118. int strcspn(const char *strSrc, const char *str)   
  119. {   
  120.     assert((strSrc != NULL) && (str != NULL));   
  121.     const char *s;   
  122.     const char *t = strSrc;   
  123.     while (*t != '/0')   
  124.     {   
  125.         s = str;   
  126.         while (*s != '/0')   
  127.         {   
  128.             if (*t == *s)   
  129.                 return t - strSrc;   
  130.             ++ s;   
  131.         }   
  132.         ++ t;   
  133.     }   
  134.     return 0;   
  135. }   
  136. int strspn(const char *strSrc, const char *str)   
  137. {   
  138.     assert((strSrc != NULL) && (str != NULL));   
  139.     const char *s;   
  140.     const char *t = strSrc;   
  141.     while (*t != '/0')   
  142.     {   
  143.         s = str;   
  144.         while (*s != '/0')   
  145.         {   
  146.             if (*t == *s)   
  147.                 break;   
  148.             ++ s;   
  149.         }   
  150.         if (*s == '/0')   
  151.             return t - strSrc;   
  152.         ++ t;   
  153.     }   
  154.     return 0;   
  155. }   
  156. char *strrchr(const char *str, int c)   
  157. {   
  158.     assert(str != NULL);   
  159.     const char *s = str;   
  160.     while (*s != '/0')   
  161.         ++ s;   
  162.     for (-- s; *s != (char) c; -- s)   
  163.         if (s == str)   
  164.             return NULL;   
  165.         return (char *) s;   
  166. }   
  167. char* strrev(char *str)   
  168. {   
  169.     assert(str != NULL);   
  170.     char *s = str, *t = str, c;   
  171.     while (*t != '/0')   
  172.         ++ t;   
  173.     for (-- t; s < t; ++ s, -- t)   
  174.     {   
  175.         c = *s;   
  176.         *s = *t;   
  177.         *t = c;   
  178.     }   
  179.     return str;   
  180. }   
  181. char *strnset(char *str, int c, unsigned int count)   
  182. {   
  183.     assert(str != NULL);   
  184.     char *s = str;   
  185.     for (; *s != '/0' && s - str < count; ++ s)   
  186.         *s = (char) c;   
  187.     return str;   
  188. }   
  189. char *strset(char *str, int c)   
  190. {   
  191.     assert(str != NULL);   
  192.     char *s = str;   
  193.     for (; *s != '/0'; ++ s)   
  194.         *s = (char) c;   
  195.     return str;   
  196. }   
  197. //@heyaming  
  198. //對原 strtok 的修改,根據MSDN,strToken可以為NULL.實際上第一次call strtok給定一字串,  
  199. //再call strtok時可以輸入NULL代表要接著處理給定字串。  
  200. //所以需要用一 static 儲存沒有處理完的字串。同時也需要處理多個分隔符在一起的情況。  
  201. char *strtok(char *strToken, const char *str)  
  202. {  
  203.     assert(str != NULL);  
  204.     static char *last;  
  205.     if (strToken == NULL && (strToken = last) == NULL)  
  206.         return (NULL);  
  207.     char *s = strToken;  
  208.     const char *t = str;  
  209.     while (*s != '/0')  
  210.     {  
  211.         t = str;  
  212.         while (*t != '/0')  
  213.         {  
  214.             if (*s == *t)  
  215.             {  
  216.                 last = s + 1;  
  217.                 if (s - strToken == 0) {  
  218.                     strToken = last;  
  219.                     break;  
  220.                 }  
  221.                 *(strToken + (s - strToken)) = '/0';  
  222.                 return strToken;  
  223.             }  
  224.             ++ t;  
  225.         }  
  226.         ++ s;  
  227.     }  
  228.     return NULL;  
  229. }  
  230. char *strupr(char *str)   
  231. {   
  232.     assert(str != NULL);   
  233.     char *s = str;   
  234.     while (*s != '/0')   
  235.     {   
  236.         if (*s >= 'a' && *s <= 'z')   
  237.             *s -= 0x20;   
  238.         s ++;   
  239.     }   
  240.     return str;   
  241. }   
  242. char *strlwr(char *str)   
  243. {   
  244.     assert(str != NULL);   
  245.     char *s = str;   
  246.     while (*s != '/0')   
  247.     {   
  248.         if (*s >= 'A' && *s <= 'Z')   
  249.             *s += 0x20;   
  250.         s ++;   
  251.     }   
  252.     return str;   
  253. }   
  254. void *memcpy(void *dest, const void *src, unsigned int count)   
  255. {   
  256.     assert((dest != NULL) && (src != NULL));   
  257.     void *address = dest;   
  258.     while (count --)   
  259.     {   
  260.         *(char *) dest = *(char *) src;   
  261.         dest = (char *) dest + 1;   
  262.         src = (char *) src + 1;   
  263.     }   
  264.     return address;   
  265. }   
  266. void *memccpy(void *dest, const void *src, int c, unsigned int count)   
  267. {   
  268.     assert((dest != NULL) && (src != NULL));   
  269.     while (count --)   
  270.     {   
  271.         *(char *) dest = *(char *) src;   
  272.         if (* (char *) src == (char) c)   
  273.             return ((char *)dest + 1);   
  274.         dest = (char *) dest + 1;   
  275.         src = (char *) src + 1;   
  276.     }   
  277.