字符串面試題——字符串逆序

字符串面試題——字符串逆序

幾點說明

1. 所有題目全部來自網絡,書籍,或者我自己的面試經歷,本人只是負責蒐集整理。在此對原作者表示感謝!

2. 我已經盡力確保文字及程序的正確性,但我畢竟是凡人,如果您發現了文章中的錯誤,或者有更好的解法,請一定留言相告,以免誤導大家!

3. 所有代碼都採用C/C++編寫

很早就準備寫一個字符串系列的面試題,本來已經寫好了,大概有十幾道題,但是寫完才發現,文章好長,連我自己都沒有耐心讀下去了,索性就將其拆分成幾個系列,一來分開後篇幅變小,看起來比較方便。二來也更有針對性,便於精雕細作。比如這篇,在原來的文章中只佔很小的篇幅,但是獨立出來才發現,東西也不少。既然是第一篇,就來個最最簡單的字符串逆序吧。

字符串逆序可以說是最經常考的題目。這是一道入門級的題目,相信80%的程序員經歷過這道題。給定一個字符串s,將s中的字符順序顛倒過來,比如s="abcd",逆序後變成s="dcba"。

普通逆序

基本上沒有這麼考的,放在這裏主要是爲了和後面的原地逆序做個對比。很簡單,直接分配一個與原字符串等長的字符數組,然後反向拷貝一下即可。

複製代碼

char* Reverse(char* s)
{
    //將q指向字符串最後一個字符
    char* q = s ;
    while( *q++ ) ;
    q -= 2 ; 

    //分配空間,存儲逆序後的字符串。
    char* p = newchar[sizeof(char) * (q - s + 2)] ; 
    char* r = p ;

    // 逆序存儲
    while(q >= s)
        *p++ = *q-- ;
    *p = '\0' ;

    return r ;
}

複製代碼

原地逆序

英文叫做in-place reverse。這是最常考的,原地逆序意味着不允額外分配空間,主要有以下幾種方法,思想都差不多,就是將字符串兩邊的字符逐個交換,如下圖。給定字符串"abcdef",逆序的過程分別是交換字符a和f,交換字符b和e,交換字符c和d。

一 設置兩個指針,分別指向字符串的頭部和尾部,然後交換兩個指針所指的字符,並向中間移動指針直到交叉。

複製代碼

char* Reverse(char* s)
{
    // p指向字符串頭部
    char* p = s ;

    // q指向字符串尾部
    char* q = s ;
    while( *q )
        ++q ;
    q -- ;

    // 交換並移動指針,直到p和q交叉
    while(q > p)
    {
        char t = *p ;
        *p++ = *q ;
        *q-- = t ;
    }

    return s ;
}

複製代碼

二 用遞歸的方式,需要給定逆序的區間,調用方法:Reverse(s, 0, strlen(s)) ;

複製代碼

// 對字符串s在區間left和right之間進行逆序,遞歸法
void Reverse( char* s, int left, int right )
{
    if(left >= right)
        return;

    char t = s[left] ;
    s[left] = s[right] ;
    s[right] = t ;

    Reverse(s, left + 1, right - 1) ;
}

複製代碼

三 非遞歸法,同樣指定逆序區間,和方法一沒有本質區別,一個使用指針,一個使用下標。

複製代碼

// 對字符串str在區間left和right之間進行逆序
char* Reverse( char* s, int left, int right )
{
    while( left < right )
    {
        char t = s[left] ;
        s[left++] = s[right] ;
        s[right--] = t ;
    }

    return s ;
}

複製代碼

不允許臨時變量的原地逆序

使用異或操作

複製代碼

// 使用異或操作對字符串s進行逆序
char* Reverse(char* s)
{
    char* r = s ;

    //令p指向字符串最後一個字符
    char* p = s;
    while (*(p + 1) != '\0')
        ++p ;

    // 使用異或操作進行交換
    while (p > s)
    {
        *p = *p ^ *s ;
        *s = *p ^ *s ;
        *p = *p-- ^ *s++ ;
    }

    return r ;
}

複製代碼

按單詞逆序

給定一個字符串,按單詞將該字符串逆序,比如給定"This is a sentence",則輸出是"sentence a is This",爲了簡化問題,字符串中不包含標點符號。

分兩步

1 先按單詞逆序得到"sihT si a ecnetnes"

2 再整個句子逆序得到"sentence a is This"

對於步驟一,關鍵是如何確定單詞,這裏以空格爲單詞的分界。當找到一個單詞後,就可以使用上面講過的方法將這個單詞進行逆序,當所有的單詞都逆序以後,將整個句子看做一個整體(即一個大的包含空格的單詞)再逆序一次即可,如下圖所示,第一行是原始字符換,第二行是按單詞逆序後的字符串,最後一行是按整個句子逆序後的字符串。

代碼

複製代碼

// 對指針p和q之間的所有字符逆序
void ReverseWord(char* p, char* q)
{
    while(p < q)
    {
        char t = *p ;
        *p++ = *q ;
        *q-- = t ;
    }
}

// 將句子按單詞逆序
char* ReverseSentence(char* s)
{
    // 這兩個指針用來確定一個單詞的首尾邊界
    char* p = s ; // 指向單詞的首字符
    char* q = s ; // 指向空格或者 '\0'

    while(*q != '\0')
    {
        if (*q == '')
        {
            ReverseWord(p, q - 1) ;
            q++ ; // 指向下一個單詞首字符
            p = q ;
        }
        else
            q++ ;
    }

    ReverseWord(p, q - 1) ; // 對最後一個單詞逆序
    ReverseWord(s, q - 1) ; // 對整個句子逆序

    return s ;
}

複製代碼

逆序打印

還有一類題目是要求逆序輸出,而不要求真正的逆序存儲。這題很簡單,有下面幾種方法,有的方法效率不高,這裏僅是提供一個思路而已。

先求出字符串長度,然後反向遍歷即可。

void ReversePrint(const char* s)
{
    int len = strlen(s) ;
    for (int i = len - 1; i >= 0; --i)
        cout << s[i];
}

如果不想求字符串的長度,可以先遍歷到末尾,然後在遍歷回來,這要藉助字符串的結束符'\0

複製代碼

void ReversePrint(const char* s)
{
    const char* p = s ;

    while (*p)
        *p++ ;

    --p ; //while結束時,p指向'\0',這裏讓p指向最後一個字符

    while (p >= s)
    {
        cout <<*p ;
        --p ;
    }
}

複製代碼

對於上面第二種方法,也可以使用遞歸的方式完成。

void ReversePrint(const char* s)
{
    if(*(s +1) != '\0')
        ReversePrint(s + 1) ;
    cout << *s ;
}

關於數組的面試題,請看這裏

== THE END==

Happy coding!

 

此文章源自於【https://www.cnblogs.com/graphics/archive/2011/03/09/1977717.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章