面试题 42(*),翻转单词顺序,字符串的左旋转 (字符串操作类经典题目:两次Reverse字符串的妙用,可以做到不需要额外空间的情况下按要求变换顺序)

看了书里的提示才想到:两次翻转字符串。

这是非常经典的题目,解法也非常经典。

先把字符串整个逆序一遍,然后再将里面被空格分开的子字符串挨个逆序一遍,就得到要求的字符串了。

自己实现的代码:

#include <string.h>
#include <stdio.h>

void ReverseStr(char* str, char* end){
    for(; str < end; str++, end--){
        char temp = *str;
        *str = *end;
        *end = temp;
    }
}

void ReverseWord(char* str){
    if(str == NULL || *str == '\0') return;
    char* p = str;
    while(*p != '\0') p++;
    ReverseStr(str, p-1);
    p = str;
    char* start = str;
    while(*p != '\0'){
        for(; *p != ' ' && *p != '\0'; p++);
        ReverseStr(start, p - 1);
        if(*p != '\0'){
            p++;
            start = p;
        }
    }
}

// ====================测试代码====================
void Test(char* testName, char* input, char* expectedResult)
{
    if(testName != NULL)
        printf("%s begins: ", testName);

    ReverseWord(input);

    if((input == NULL && expectedResult == NULL)
        || (input != NULL && strcmp(input, expectedResult) == 0))
        printf("Passed.\n\n");
    else
        printf("Failed.\n\n");
}

// 功能测试,句子中有多个单词
void Test1()
{
    char input[] = "I am a student.";
    char expected[] = "student. a am I";

    Test("Test1", input, expected);
}

// 功能测试,句子中只有一个单词
void Test2()
{
    char input[] = "Wonderful";
    char expected[] = "Wonderful";

    Test("Test2", input, expected);
}

// 鲁棒性测试
void Test3()
{
    Test("Test3", NULL, NULL);
}

// 边界值测试,测试空字符串
void Test4()
{
    Test("Test4", "", "");
}

// 边界值测试,字符串中只有空格
void Test5()
{
    char input[] = "   ";
    char expected[] = "   ";
    Test("Test5", input, expected);
}

int main()
{
    Test1();
    Test2();
    Test3();
    Test4();
    Test5();

    return 0;
}

书上的代码:

char* ReverseSentence(char *pData)
{
    if(pData == NULL)
        return NULL;

    char *pBegin = pData;

    char *pEnd = pData;
    while(*pEnd != '\0')
        pEnd ++;
    pEnd--;

    // 翻转整个句子
    Reverse(pBegin, pEnd);

    // 翻转句子中的每个单词
    pBegin = pEnd = pData;
    while(*pBegin != '\0')
    {
        if(*pBegin == ' ')
        {
            pBegin ++;
            pEnd ++;
        }
        else if(*pEnd == ' ' || *pEnd == '\0')
        {
            Reverse(pBegin, --pEnd);
            pBegin = ++pEnd;
        }
        else
        {
            pEnd ++;
        }
    }

    return pData;
}

 

接着,另一道题:

这道题其实是上一题的变种,其实也就是将字符串的各部分换个位置。只不过划分部分的标志不再是空格,而是人为制定的字符个数。

 

我们依然将整个字符串先逆序,然后要求左旋转N位,就将逆序后的后N位逆序,前半部分也逆序,结束。

这里代码就不提供了,直接上书上的代码:

#include "stdafx.h"
#include "..\Utilities\StringUtil.h"
#include <string.h>

char* LeftRotateString(char* pStr, int n)
{
    if(pStr != NULL)
    {
        int nLength = static_cast<int>(strlen(pStr));
        if(nLength > 0 && n > 0 && n < nLength)
        {
            char* pFirstStart = pStr;
            char* pFirstEnd = pStr + n - 1;
            char* pSecondStart = pStr + n;
            char* pSecondEnd = pStr + nLength - 1;

            // 翻转字符串的前面n个字符
            Reverse(pFirstStart, pFirstEnd);
            // 翻转字符串的后面部分
            Reverse(pSecondStart, pSecondEnd);
            // 翻转整个字符串
            Reverse(pFirstStart, pSecondEnd);
        }
    }

    return pStr;
}

// ====================测试代码====================
void Test(char* testName, char* input, int num, char* expectedResult)
{
    if(testName != NULL)
        printf("%s begins: ", testName);

    char* result = LeftRotateString(input, num);

    if((input == NULL && expectedResult == NULL)
        || (input != NULL && strcmp(result, expectedResult) == 0))
        printf("Passed.\n\n");
    else
        printf("Failed.\n\n");
}

// 功能测试
void Test1()
{
    char input[] = "abcdefg";
    char expected[] = "cdefgab";

    Test("Test1", input, 2, expected);
}

// 边界值测试
void Test2()
{
    char input[] = "abcdefg";
    char expected[] = "bcdefga";

    Test("Test2", input, 1, expected);
}

// 边界值测试
void Test3()
{
    char input[] = "abcdefg";
    char expected[] = "gabcdef";

    Test("Test3", input, 6, expected);
}

// 鲁棒性测试
void Test4()
{
    Test("Test4", NULL, 6, NULL);
}

// 鲁棒性测试
void Test5()
{
    char input[] = "abcdefg";
    char expected[] = "abcdefg";

    Test("Test5", input, 0, expected);
}

// 鲁棒性测试
void Test6()
{
    char input[] = "abcdefg";
    char expected[] = "abcdefg";

    Test("Test6", input, 7, expected);
}

int _tmain(int argc, _TCHAR* argv[])
{
    Test1();
    Test2();
    Test3();
    Test4();
    Test5();
    Test6();

    return 0;
}

 

总结:遇到这种要求字符串内部顺序变换的题目,尽量多从Reverse考虑,因为(1) 这种顺序的变换往往可以由Reverse的组合实现 (2) Reverse是唯一不需要额外空间的做法,大部分不需要额外空间的最优解,都是顺着这个思路下去的。

posted on 2014-03-03 04:48  Felix Fang  阅读(260)  评论(0)    收藏  举报

导航