6

通过我在网上找到的一些编程面试挑战,我不得不编写一个算法来反转一个 const char * 并返回一个指向新 char * 的指针。我想我有它,但为了让它正常工作,我不得不做一些奇怪的事情——基本上必须自己考虑空终止字符。不知何故,我觉得这是错误的,但我很难过,我想知道是否有人可以帮助我:

char * reverse(const char * str)
{
  int length = strlen(str);
  char * reversed_string = new char[length+1];

  for(int i = 0; i < length; ++i)
  {
    reversed_string[i] = str[(length-1) - i];
  }
  //need to null terminate the string
  reversed_string[length] = '\0';

  return reversed_string;

}

int main(int argc, char * argv[])
{

  char * rev_str = reverse("Testing");

  cout << "Your string reversed is this: " << rev_str << endl;

  delete rev_str;
  rev_str = 0;

  return 0;
}
4

20 回答 20

16

std::reverse来自<algorithm>字符串和char数组的作品:

string str = "Hello";
char chx[] = "Hello";

reverse(str.begin(), str.end());
reverse(chx, chx + strlen(chx));

cout << str << endl;
cout << chx << endl;

/EDIT:这当然会修改原始字符串。但是 STL 来救援。下面创建一个新的反转字符串。char不幸的是(?),如果不创建额外的(隐式)副本,这不能直接在 C 数组上工作:

string reverse_string(string const& old) {
    return string(old.rbegin(), old.rend());
}

cout << reverse_string("Hello") << endl;
于 2008-10-20T18:48:28.810 回答
14

我曾经有过这个问题。这是我想到的第一个答案,但接下来是,“现在不分配任何内存就这样做”。

int length = strlen(string);
for(int i = 0; i < length/2; i++) {
  char c = string[i];
  string[i] = string[length - i];
  string[length - i] = c;
}

编辑:有些人对不使用指针表示不屑。这有点可读性,尽管不是完全最佳的。其他人已经进入了指针解决方案,这里不再赘述。

一位评论者质疑说,如果没有(基于堆栈的)用于交换的保持单元,它应该是可行的。这样做的机制是按位异或。将循环内部替换为

string[i] = string[i] ^ string[length - i];
string[length - i] = string[i] ^ string[length - i];
string[i] = string[i] ^ string[length - i];

但一般来说,现代编译器可以优化掉一个简单交换的局部变量。有关详细信息,请参阅维基百科

于 2008-10-20T18:41:10.633 回答
7
if( string[0] )
{
    char *end = string + strlen(string)-1;
    while( start < end )
    {
        char temp = *string;
        *string++ = *end;
        *end-- = temp;
    }
}
于 2008-10-20T18:47:08.110 回答
3

我知道这是非常不可移植的,但是 x86 汇编指令bswap让您可以通过一条指令交换四个字节,这可能是提升代码的好方法。

这是一个如何让它与 GCC 一起工作的例子。

/* 
 * reverse.c
 *
 * $20081020 23:33 fernando DOT miguelez AT gmail DOT com$
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_CHARS 10 * 1024 * 1024

/*
 * Borrowed from http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2007-03/msg00004.html
 * GNU Compiler syntax
 */
inline uint32_t bswap(uint32_t val)
{
    __asm__("bswap %0" : "=r" (val) : "0" (val));
    return val;
}

char * reverseAsm(const char * str)
{
    int i;
    int length = strlen(str);
    int dwordLength = length/4;

    if(length % 4 != 0)
    {
        printf("Error: Input string length must be multiple of 4: %d\n", length);       
        return NULL;
    }

    char * reversed_string = (char *) malloc(length+1);
    for(i = 0; i < dwordLength; i++)
    {
        *(((uint32_t *) reversed_string) + dwordLength - i - 1) = bswap(*(((uint32_t *) str) + i));
    }

    reversed_string[length] = '\0';

    return reversed_string;
}

char * reverse(const char * str)
{
    int i;
    int length = strlen(str);
    char * reversed_string = (char *) malloc(length+1);

    for(i = 0; i < length; ++i)
    {
        reversed_string[i] = str[(length-1) - i];
    }

        //need to null terminate the string

    reversed_string[length] = '\0';

    return reversed_string;
}

int main(void)
{
    int i;
    char *reversed_str, *reversed_str2;
    clock_t start, total;
    char *str = (char *) malloc(MAX_CHARS+1);

    str[MAX_CHARS] = '\0';

    srand(time(0));

    for(i = 0; i < MAX_CHARS; i++)
    {
        str[i] = 'A' + rand() % 26;     
    }

    start = clock();
    reversed_str = reverse(str);
    total = clock() - start;
    if(reversed_str != NULL)
    {
        printf("Total clock ticks to reverse %d chars with pure C method: %d\n", MAX_CHARS, total); 
        free(reversed_str);
    }
    start = clock();
    reversed_str2 = reverseAsm(str);
    total = clock() - start;
    if(reversed_str2 != NULL)
    {
        printf("Total clock ticks to reverse %d chars with ASM+C method: %d\n", MAX_CHARS, total); 
        free(reversed_str2);
    }

    free(str);

    return 0;
}

在我的旧电脑上 Cygwin 下的结果:

fer@fernando /cygdrive/c/tmp$ ./reverse.exe
Total clock ticks to reverse 10485760 chars with pure C method: 221
Total clock ticks to reverse 10485760 chars with ASM+C method: 140
于 2008-10-20T21:39:58.683 回答
3

呃?没有人用指针做过吗?

char *reverse(const char *s) {
    size_t n = strlen(s);
    char *dest = new char[n + 1];
    char *d = (dest + n - 1);

    dest[n] = 0;
    while (*s) {
        *d-- = *s++
    }

    return dest;
}

希望多年的 Java 没有毁了我的 C ;-)

编辑:用额外的 var 替换所有那些 strlen 调用。这些天 strlen 返回了什么?(感谢底座)。

于 2008-10-20T19:47:38.813 回答
3

您的代码直截了当且不足为奇。一些东西:

  1. 使用 size_t 而不是 int 作为循环索引
  2. 虽然您的编译器很可能足够聪明地确定 (length-1) 是不变的,但它可能不够聪明地确定 (length-1)-i 最好用在每次传递中递减的不同循环变量替换
  3. 我会使用指针而不是数组语法——拥有 *dst-- = *src++; 对我来说会更干净。在循环。

换句话说:

char *dst = reversed_string + length;
*dst-- = '\0';
while (*src) {
   *dst-- = *src++;
}
于 2008-10-20T19:56:12.027 回答
2

你不能(不应该)这样做:

string[i] ^= string[length - i] ^= string[i] ^= string[length - i];

来自:http ://en.wikipedia.org/wiki/XOR_swap_algorithm#Code_example

  • *“此代码具有未定义的行为,因为它修改了左值x 两次而没有中间的序列点。
于 2009-10-08T21:59:22.343 回答
2

@Konrad Rudolph:(对不起,我没有发表评论的“经验”)

我想指出 STL 提供了一种reverse_copy()算法,类似于reverse()。您不需要像以前那样引入临时的,只需分配一个大小合适的新 char * 即可。

于 2008-10-20T20:55:58.490 回答
1

实际上,考虑到原始字符串保持不变的约束,我认为问题中给出的原始方法是最好的。人们发布的所有这些就地反转的奇特方法都很棒,但是一旦考虑到复制给定的字符串,它们都比简单地向后复制字符串效率低。

于 2008-10-20T18:57:40.833 回答
1

我们之前使用过这个问题——令人惊讶的结果是发现很多人做不到(即使有丰富的 C/C++ 经验!)。我更喜欢就地变体,因为它节省了一些开销,并且只需要迭代 strlen(s)/2 个字符。

您在面试中的解决方案会很好。使用指针而不是数组语法的(正确的!)解决方案的评分会更高一些,因为它显示了对 C/C++ 编程中至关重要的指针的更高舒适度。

次要的批评是指出 strlen 返回 size_t 而不是 int,您应该在 rev_str 上使用 delete []。

于 2008-10-20T19:01:27.380 回答
1

WRT:“现在在没有临时持有变量的情况下这样做”......可能是这样的(并且暂时保持数组索引):

int length = strlen(string);
for(int i = 0; i < length/2; i++) {
  string[i] ^= string[length - i];
  string[length - i] ^= string[i];
  string[i] ^= string[length - i];
}
于 2008-10-20T21:09:29.757 回答
0

.

char * reverse(const char * str)
{
  if (!str)
    return NULL;

  int length = strlen(str);
  char * reversed_string = new char[length+1];

  for(int i = 0; i < length/2; ++i)
  {
    reversed_string[i] = str[(length-1) - i];
    reversed_string[(length-1) - i] = str[i];
  }
  //need to null terminate the string
  reversed_string[length] = '\0';

  return reversed_string;

}

一半的时间,但复杂性相同(注意可能会因一个错误而关闭)

于 2008-10-22T05:08:37.980 回答
0

上面的 for 循环有错字。检查循环变量 i 应该是 <= 而不是 <,否则对于奇数个元素将失败。for(int i = 0; i <= 长度/2; ++i)

于 2009-03-29T18:03:14.653 回答
0

这很好用:

#include <algorithm>
#include <iostream>
#include <cstring>

void reverse_string(char *str) {    
    char *end = str + strlen(str) - 1;
    while (str < end) {
        std::iter_swap(str++, end--);
    }
}

int main() {
    char s[] = "this is a test";
    reverse_string(s);
    std::cout << "[" << s << "]" << std::endl;
}
于 2008-10-20T18:46:39.140 回答
0

我会像这样解决它(虽然我的 c 有点生锈,请原谅我)

char *reverse( const char *source ) {
  int len = strlen( source );
  char *dest = new char[ len + 1 ];
  int i = 0;
  int j = len;
  while( j > 0 ) {
    dest[j--] = src[i++];
  }
  dest[i] = \0;
  return dest;
}
于 2008-10-20T18:55:30.213 回答
0

字符串反转到位,没有临时变量。

static inline void
byteswap (char *a, char *b)
{
  *a = *a^*b;
  *b = *a^*b;
  *a = *a^*b;
}

void
reverse (char *string)
{
  char *end = string + strlen(string) - 1;

  while (string < end) {
    byteswap(string++, end--);
  }
}
于 2008-10-20T21:55:59.570 回答
0

不需要临时变量的方法

int length = strlen(string);
for(int i = 0; i < length/2; i++) {
  string[i] ^= string[length - i] ^= string[i] ^= string[length - i];
}
于 2008-10-20T22:10:59.930 回答
0

它不会更有效率,但是您可以通过将每个字母推入堆栈,然后将它们弹出到新分配的缓冲区中来展示数据结构的知识。

这将需要两次通过和一个临时堆栈,但我可能会更相信自己第一次就能做到这一点,然后不会像上面那样犯一个错误。

char* stringReverse(const char* sInput)
{
    std::size_t nLen = strlen(sInput);
    std::stack<char> charStack;
    for(std::size_t i = 0; i < nLen; ++i)
    {
        charStack.push(sInput[i]);
    }
    char * result = new char[nLen + 1];
    std::size_t counter = 0;
    while (!charStack.empty())
    {
        result[counter++] = charStack.top();
        charStack.pop();
    }
    result[counter] = '\0';
    return result;
}
于 2008-10-20T19:25:54.023 回答
0

当作为面试官问这个问题时,我正在寻找一个干净、易于理解的解决方案,并且可能会问如何使最初的解决方案更有效率。我对“智能”解决方案不感兴趣。

我在想这样的事情;候选人是否在他们的循环中因一个错误而使旧的,他们是否预先分配了足够的内存,他们是否检查了错误的输入,他们是否使用了足够有效的类型。

不幸的是,正如已经指出的那样,太多的人甚至无法做到这一点。

于 2008-10-20T20:50:39.937 回答
0

如果我在做面试,我会对解决方案的质量更加挑剔,因为它的健壮性,而不仅仅是它的性能。

如果传递了一个空指针,到目前为止提交的所有答案都将失败——它们中的大多数会立即调用strlen()一个可能的空指针——这可能会导致你的进程出现段错误。

许多答案都对性能着迷,以至于他们错过了问题的关键问题之一: reverse a const char *,即您需要制作反向副本,而不是就地反向。如果需要副本,您会发现很难将迭代次数减半!

这是一个面试问题,所以我们想看看算法的细节,但在现实世界中,这只是强调尽可能使用标准库的价值。

于 2008-10-21T02:56:44.503 回答