字符串在面試中也是經常考查的內容,C庫字符串的操作函數是常考內容(例如 strstr(),strtok(),strcpy(),strcmp()等),所以,平時我們不僅要學會使用字符串的操作,也要對其具體的實現方式有所瞭解,特別是字符串的字符串處理函數。下面我們通過分析幾道典型的例題進行學習和交流。
1.從字符串中刪除給定的某個字符或者幾個字符(要求儘量高效而且不增加額外的存儲)
思路:如果沒有後面的限制條件,題目會很簡單,要做到不增加額外的存儲,其實我們可以直接對源字符數組進行操作,這樣也能達到不用移動字符的效果,代碼如下:
//delete char
void DeleteChar(char str[],char c)
{
assert(str!=NULL);
int iDest=0,iSrc=0;
cout<<"Delete char o is:";
while(str[iSrc]!='\0')
{
if(str[iSrc]!=c)
{
str[iDest] = str[iSrc]; //str的形式要以字符數組形式給出,
//如果以字符指針形式給出則此時無法修改源字符串內容
cout<<str[iDest];
iDest++;
}
iSrc++;
}
cout<<endl;
}
結果如下但是如果是要刪除是一個字符數組中的幾個字符呢?其實此時設置一個標記數組就能解決問題,代碼如下:
//delete char array
void DeleteCharArr(char str[],char chrs[],int n)
{
assert(str!=NULL);
char temp[256] = {0};
for(int i = 0;i<n;i++)
temp[chrs[i]] = 1;
int iDest = 0,iSrc = 0;
cout<<"Delete chrs 'o' and 'l' is:";
while(str[iSrc]!='\0')
{
if(!temp[str[iSrc]])
{
str[iDest] = str[iSrc]; //str的形式要以字符數組形式給出,
//如果以字符指針形式給出則此時無法修改源字符串內容
cout<<str[iDest];
iDest++;
}
iSrc++;
}
cout<<endl;
}
結果如下:2.識別出字符串中的單詞個數
思路:英文單詞中字母通過空格來區分的,需要設置兩個指針 一個pBegin指向單詞的首地址;一個pEnd指向單詞的爲地址,識別完後再重置pBegin和pEnd即可。代碼如下:
//word number in the sentence
int WordsNum(char *str)
{
char *pBegin,*pEnd;
int num=0;
//if(str==NULL) return 0;
assert(str!=NULL);
while(*str!='\0')
{
pBegin = str;
while(*pBegin!=' ' && *pBegin!='\0')//word' 'word' 'word
pBegin++;
if(*pBegin=='\0')//the last word
{
num++;
break;
}
num++;
//cout<<*(pBegin+1)<<endl;
pEnd = pBegin+1;
str = pEnd;
}
cout<<num<<endl;
return num;
3.逆轉字符串
思路:一可以當作字符數組處理,利用循環語句實現前後字符的交換;二可以利用遞歸的方式
//reverse the str
void ReverseString(char *str)
{
int n;
char ctemp;
n = strlen(str);//str[] = "Hello world";最後以'\0'結束
for(int i = 0;i<n/2;i++)
{
ctemp = str[i];
str[i] = str[n-i-1];//<i,n-i-1>
str[n-i-1] = ctemp;
}
cout<<str<<endl;
}
4.實現內存拷貝memcpy()
思路:這時候一定要拷貝過程中出現內存重疊的可能性,不然就導致拷貝的數據的不一致,正常情況下把源字符串從頭到尾一個一個拷貝到指定的目的字符串位置,需要考慮到給定的目的字符串的地址是否也覆蓋了一部分源字符串,這是如果整箱拷貝後面的數據肯定會被污染,此時應該採用逆向拷貝的方式解決這個問題。下面是重疊的示意圖,
代碼如下:
//memcpy
void memcpy(void *pDest,const void *pSrc,int size)
{
assert(pDest!=NULL);
assert(pSrc!=NULL);
char *pStrSrc,*pStrDest;
//pDest與pSrc共享同一塊內存區域的時候採用逆向拷貝的方式避免原始數據被覆蓋
if(pDest<pSrc &&((char*)pDest+size)>pDest)
{//(char *)字符單元
pStrSrc = (char*)pSrc+size-1;
pStrDest = (char*)pDest+size-1;
while(size--)
*pStrDest-- = *pStrSrc--;
}
else
{//正常的拷貝方式
pStrSrc = (char*)pSrc;
pStrDest = (char*)pDest;
while(size--)
*pStrDest++ = *pStrSrc++;
}
}
5.Ip字符串與32位整數之間的轉化
思路:ip字符串中每個數值之間是以‘.’分割的,提取其中的數值可以採用題目2中的方式,用兩個指針表示一個值的起始和結束位置,然後提取進行移位累加。代碼如下:
//ipstring to int_32
int IpToString(char *ip)
{
char *pBegin,*pEnd;
int shift = 24;
long unsigned resultSum = 0,result=0;
pBegin=pEnd=ip;//兩個指針分別指向一個ip段的開始和結束
assert(ip!=NULL);//assert
while(*pEnd!='\0')
{//'.' 區分ip段,就像空格區分單詞一樣
while(*pEnd!='.' && *pEnd!='\0')
pEnd++;
int temp = 0;
while(pBegin<pEnd)
{//string to int
temp = temp*10+(*pBegin-'0');
pBegin++;
}
cout<<temp<<"<<"<<shift<<"==";
result=(temp<<shift);
cout<<result<<endl;
resultSum+=result;
shift = shift-8;
if(*pEnd='\0')
break;
pBegin++;
pEnd = pBegin;
}
cout<<"resultSum == "<<resultSum<<endl;
return resultSum;
}
結果如下:但如果是32整數轉換爲ip地址呢,只需要獲取每個字節並做出相應的轉換即可,代碼如下:
//int_32 to ip
char *Int32ToIp(long unsigned num)
{ //typedef unsigned char Uchar;
cout<<num<<"-->>";
printf("%u.%u.%u.%.u\n",
(Uchar)*((char*)&num+3),//最高位
(Uchar)*((char*)&num+2),
(Uchar)*((char*)&num+1),
(Uchar)*((char*)&num+0)//最低位
);
}
結果如下:6.將一組字符串排序(str[][] = {"Ba","Ac","aB","dA"...}==>{"Ac","Ba","aB","dA"})
思路:完全可以想過利用冒泡排序的思想,只不過平時針對的是數值,現在是字符串,代碼如下:
//sort a string arrry
void SortString(char *str[],int n)
{//char *str[4] = {"Ba","Ac","aB","dA"};
char *ptemp = NULL;
for(int i = 0;i<n;i++)
{
for(int j=n-1;j>i;j--)
{
if(strcmp(str[j],str[j-1])<0)
{
ptemp = str[j];
str[j] = str[j-1];
str[j-1] = ptemp;
}
}
}
}
結果如下:繼續學習和加油~~…………