Làm thế nào để bạn đảo ngược một chuỗi tại chỗ trong C hoặc C ++?


Câu trả lời:


123

Thuật toán tiêu chuẩn là sử dụng các con trỏ đến điểm bắt đầu / kết thúc và đưa chúng vào trong cho đến khi chúng gặp nhau hoặc giao nhau ở giữa. Trao đổi khi bạn đi.


Đảo ngược chuỗi ASCII, tức là một mảng kết thúc 0 trong đó mọi ký tự khớp với 1 char. (Hoặc các bộ ký tự không đa nhân khác).

void strrev(char *head)
{
  if (!head) return;
  char *tail = head;
  while(*tail) ++tail;    // find the 0 terminator, like head+strlen
  --tail;               // tail points to the last real char
                        // head still points to the first
  for( ; head < tail; ++head, --tail) {
      // walk pointers inwards until they meet or cross in the middle
      char h = *head, t = *tail;
      *head = t;           // swapping as we go
      *tail = h;
  }
}

// test program that reverses its args
#include <stdio.h>

int main(int argc, char **argv)
{
  do {
    printf("%s ",  argv[argc-1]);
    strrev(argv[argc-1]);
    printf("%s\n", argv[argc-1]);
  } while(--argc);

  return 0;
}

Thuật toán tương tự hoạt động cho các mảng số nguyên có độ dài đã biết, chỉ cần sử dụng tail = start + length - 1thay vì vòng tìm kiếm kết thúc.

(Lưu ý của biên tập viên: câu trả lời này ban đầu cũng sử dụng XOR-exchange cho phiên bản đơn giản này. Đã sửa lỗi vì lợi ích của những người đọc tương lai của câu hỏi phổ biến này. XOR-exchange rất không được khuyến khích ; khó đọc và làm cho mã của bạn biên dịch kém hiệu quả. có thể thấy trên trình khám phá trình biên dịch Godbolt, phần thân vòng lặp asm phức tạp hơn bao nhiêu khi xor-exchange được biên dịch cho x86-64 với gcc -O3.)


Được rồi, chúng ta hãy sửa các ký tự UTF-8 ...

(Đây là thứ trao đổi XOR. Hãy lưu ý rằng bạn phải tránh tự hoán đổi, vì nếu *p*qlà cùng một vị trí, bạn sẽ chọn nó với ^ a == 0. XOR-hoán đổi phụ thuộc vào việc có hai vị trí riêng biệt, sử dụng chúng như là lưu trữ tạm thời.)

Lưu ý của biên tập viên: bạn có thể thay thế SWP bằng chức năng nội tuyến an toàn bằng biến tmp.

#include <bits/types.h>
#include <stdio.h>

#define SWP(x,y) (x^=y, y^=x, x^=y)

void strrev(char *p)
{
  char *q = p;
  while(q && *q) ++q; /* find eos */
  for(--q; p < q; ++p, --q) SWP(*p, *q);
}

void strrev_utf8(char *p)
{
  char *q = p;
  strrev(p); /* call base case */

  /* Ok, now fix bass-ackwards UTF chars. */
  while(q && *q) ++q; /* find eos */
  while(p < --q)
    switch( (*q & 0xF0) >> 4 ) {
    case 0xF: /* U+010000-U+10FFFF: four bytes. */
      SWP(*(q-0), *(q-3));
      SWP(*(q-1), *(q-2));
      q -= 3;
      break;
    case 0xE: /* U+000800-U+00FFFF: three bytes. */
      SWP(*(q-0), *(q-2));
      q -= 2;
      break;
    case 0xC: /* fall-through */
    case 0xD: /* U+000080-U+0007FF: two bytes. */
      SWP(*(q-0), *(q-1));
      q--;
      break;
    }
}

int main(int argc, char **argv)
{
  do {
    printf("%s ",  argv[argc-1]);
    strrev_utf8(argv[argc-1]);
    printf("%s\n", argv[argc-1]);
  } while(--argc);

  return 0;
}
  • Tại sao, vâng, nếu đầu vào bị hỏng, điều này sẽ vui vẻ trao đổi bên ngoài địa điểm.
  • Liên kết hữu ích khi phá hoại trong UNICODE: http://www.macchiato.com/unicode/chart/
  • Ngoài ra, UTF-8 trên 0x10000 chưa được kiểm tra (vì dường như tôi không có bất kỳ phông chữ nào cho nó, cũng như không có sự kiên nhẫn để sử dụng một hexeditor)

Ví dụ:

$ ./strrev Räksmörgås ░▒▓○◔◑◕●

░▒▓○◔◑◕● ●◕◑◔○▓▒░

Räksmörgås sågrömskäR

./strrev verrts/.

162
Không có lý do chính đáng để sử dụng trao đổi XOR bên ngoài một cuộc thi mã bị xáo trộn.
Chris Conway

28
Bạn nghĩ rằng "tại chỗ" có nghĩa là "không có bộ nhớ thêm", thậm chí không có bộ nhớ O (1) cho tạm thời? Còn không gian trên stack cho str và địa chỉ trả về thì sao?
Chris Conway

55
@Bill, đó không phải là định nghĩa phổ biến của trên mạng tại chỗ của ý nghĩa. Các thuật toán tại chỗ có thể sử dụng bộ nhớ bổ sung. Tuy nhiên, số lượng bộ nhớ bổ sung này không được phụ thuộc vào đầu vào - tức là nó phải không đổi. Do đó, việc hoán đổi các giá trị sử dụng bộ nhớ bổ sung là hoàn toàn tại chỗ.
Konrad Rudolph

19
Không nâng cao điều này cho đến khi hoán đổi xor biến mất.
Adam Rosenfield

34
Trao đổi XOR chậm hơn so với trao đổi thông qua đăng ký trên các bộ xử lý không theo thứ tự hiện đại.
Patrick Schlüter

465
#include <algorithm>
std::reverse(str.begin(), str.end());

Đây là cách đơn giản nhất trong C ++.


6
Trong C ++, một chuỗi được biểu diễn bằng lớp chuỗi. Anh ấy đã không hỏi "char star" hay "char ngoặc". Giữ đẳng cấp, C.
jokoon

10
@fredsbend, phiên bản "dài một cách lố bịch" của câu trả lời được chọn xử lý một trường hợp mà câu trả lời đơn giản này không có - đầu vào UTF-8. Nó cho thấy tầm quan trọng của việc chỉ định đầy đủ vấn đề. Bên cạnh câu hỏi là về mã sẽ hoạt động trong C là tốt.
Đánh dấu tiền chuộc

5
Câu trả lời này xử lý trường hợp nếu bạn sử dụng lớp chuỗi nhận biết UTF-8 (hoặc có thể là lớp ký tự utf-8 với std :: basic_ chuỗi). Bên cạnh đó, câu hỏi cho biết "C hoặc C ++", không phải "C và C ++". Chỉ C ++ là "C hoặc C ++".
Taywee

161

Đọc Kernighan và Ritchie

#include <string.h>

void reverse(char s[])
{
    int length = strlen(s) ;
    int c, i, j;

    for (i = 0, j = length - 1; i < j; i++, j--)
    {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}

8
Đã thử nghiệm trên iphone của tôi, điều này chậm hơn so với việc sử dụng địa chỉ con trỏ thô khoảng 15%
jjxtra

3
Không nên biến "c" là char thay vì int?
Bài học

17
Điều quan trọng cần lưu ý trong ví dụ này là chuỗi sphải được khai báo ở dạng mảng. Nói cách khác, char s[] = "this is ok"thay char *s="cannot do this"vì bởi vì điều này dẫn đến hằng số chuỗi không thể sửa đổi
user1527227

3
Với lời xin lỗi đến "Bố già" .... "Bỏ súng, mang theo K & R". Là một người khổng lồ C, tôi sẽ sử dụng các con trỏ, vì chúng đơn giản và dễ hiểu hơn cho vấn đề này, nhưng mã sẽ ít được chuyển sang C #, Java, v.v.

2
@Eric Điều này không chạy trong thời gian O (log (n)). Nó chạy trong O (n) nếu bạn đề cập đến số lượng ký tự hoán đổi mã thực hiện, với n độ dài chuỗi, sau đó n hoán đổi được thực hiện. Nếu bạn đang nói về số lượng vòng lặp được thực hiện thì nó vẫn là O (n) - mặc dù O (n / 2) nhưng bạn thả các hằng số trong ký hiệu Big O.
Stephen Fox

62

Đảo ngược một chuỗi tại chỗ (trực quan hóa):

Đảo ngược một chuỗi tại chỗ


Mát mẻ! Bạn đã sử dụng cái gì để tạo ra cái này? Tôi nghĩ rằng câu trả lời sẽ được cải thiện bằng cách bao gồm một liên kết đến nó.
Pryftan

Việc thực hiện thuật toán này thực sự nằm trong câu trả lời này .
karlphillip

Bạn có thể giải thích điều này một chút để nếu liên kết hình ảnh chết thì câu trả lời sẽ không vô dụng?
SS Anne

41

Không ác C, giả sử trường hợp phổ biến trong đó chuỗi là charmảng kết thúc null :

#include <stddef.h>
#include <string.h>

/* PRE: str must be either NULL or a pointer to a 
 * (possibly empty) null-terminated string. */
void strrev(char *str) {
  char temp, *end_ptr;

  /* If str is NULL or empty, do nothing */
  if( str == NULL || !(*str) )
    return;

  end_ptr = str + strlen(str) - 1;

  /* Swap the chars */
  while( end_ptr > str ) {
    temp = *str;
    *str = *end_ptr;
    *end_ptr = temp;
    str++;
    end_ptr--;
  }
}

Thay vì sử dụng vòng lặp while để tìm con trỏ kết thúc, bạn không thể sử dụng cái gì đó như end_ptr = str + strlen (str); Tôi biết rằng thực tế sẽ làm điều tương tự, nhưng tôi thấy nó rõ ràng hơn.
Peter Kühne

Đủ công bằng. Tôi đã cố gắng (và không thành công) để tránh lỗi sai trong câu trả lời của @ uvote.
Chris Conway

Bên cạnh một cải tiến hiệu suất tiềm năng với có thể int temp, giải pháp này có vẻ tốt nhất. +1
chux - Tái lập Monica

@ chux-RebstateMonica Có. Nó thật đẹp. Cá nhân tôi sẽ loại bỏ () s khỏi !(*str)mặc dù.
Pryftan

@ PeterKühne Có hoặc strchr()tìm kiếm '\0'. Tôi nghĩ về cả hai. Không cần một vòng lặp.
Pryftan

35

Bạn sử dụng std::reversethuật toán từ Thư viện chuẩn C ++.


14
Thư viện mẫu tiêu chuẩn là một thuật ngữ chuẩn. Tiêu chuẩn C ++ không đề cập đến nó và các thành phần STL trước đây nằm trong Thư viện chuẩn C ++.
Nemanja Trifunovic

16
đúng. tôi luôn tự hỏi tại sao rất nhiều người vẫn gọi nó là "STL" mặc dù nó chỉ nhằm mục đích gây nhầm lẫn. sẽ đẹp hơn nếu có nhiều người giống bạn và gọi nó chỉ là "Thư viện chuẩn C ++" hoặc STL và nói "Thư viện tiêu chuẩn" :)
Johannes Schaub - litb

27

Đã được một thời gian và tôi không nhớ cuốn sách nào đã dạy tôi thuật toán này, nhưng tôi nghĩ nó khá khéo léo và dễ hiểu:

char input[] = "moc.wolfrevokcats";

int length = strlen(input);
int last_pos = length-1;
for(int i = 0; i < length/2; i++)
{
    char tmp = input[i];
    input[i] = input[last_pos - i];
    input[last_pos - i] = tmp;
}

printf("%s\n", input);

Đó là một biến thể thú vị có. Về mặt kỹ thuật nên là một size_tmặc dù.
Pryftan

24

Sử dụng phương pháp std :: Reverse từ STL :

std::reverse(str.begin(), str.end());

Bạn sẽ phải bao gồm thư viện "thuật toán" , #include<algorithm>.


1
Thư viện nào cần được đưa vào?
Don Cruickshank

#include <Thuật toán> Tôi quên viết nó và sau đó chỉnh sửa câu trả lời nhưng nó không được lưu, đó là lý do tại sao tên bị thiếu ..
user2628229

Mặc dù đây cũng là câu trả lời của tôi, nhưng chỉ tò mò thôi, vì OP đã hỏi "không có bộ đệm để giữ chuỗi đảo ngược" (về cơ bản, hoán đổi tại chỗ), làm thế nào để chứng minh rằng điều này đang làm như vậy? Có ai chỉ giải thích rằng bên trong nó sử dụng std :: iter_swap <>, lặp lại đến điểm giữa của bộ đệm từ cả hai điểm cuối không? Ngoài ra, OP đã yêu cầu cả C và C ++, điều này chỉ dành cho C ++ (chúng ta có thể hy vọng phương thức <string.h> có strrev () không?)
HidekiAI

21

Lưu ý rằng vẻ đẹp của std :: Reverse là nó hoạt động với char *chuỗi và std::wstrings cũng như std::strings

void strrev(char *str)
{
    if (str == NULL)
        return;
    std::reverse(str, str + strlen(str));
}

1
Một mặt tôi muốn nôn vì bạn đã sử dụng C ++ nhưng mặt khác, đó là một điều tuyệt đẹp, đẹp đẽ, tuyệt đẹp khi thấy ai đó sử dụng C ++, người không đặt *theo loại và đặt tên theo tên. Tuyệt vời. Tôi thừa nhận tôi là một người theo chủ nghĩa thuần túy nhưng tôi co rúm mỗi khi thấy mã như thế nào char* str;.
Pryftan

11

Nếu bạn đang tìm cách đảo ngược bộ đệm kết thúc NULL, hầu hết các giải pháp được đăng ở đây đều ổn. Nhưng, như Tim Farley đã chỉ ra, các thuật toán này sẽ chỉ hoạt động nếu nó hợp lệ khi cho rằng một chuỗi về mặt ngữ nghĩa là một mảng byte (tức là các chuỗi đơn byte), đó là một giả định sai, tôi nghĩ vậy.

Lấy ví dụ, chuỗi "año" (năm bằng tiếng Tây Ban Nha).

Các điểm mã Unicode là 0x61, 0xf1, 0x6f.

Hãy xem xét một số mã hóa được sử dụng nhiều nhất:

Latin1 / iso-8859-1 (mã hóa byte đơn, 1 ký tự là 1 byte và ngược lại):

Nguyên:

0x61, 0xf1, 0x6f, 0x00

Đảo ngược:

0x6f, 0xf1, 0x61, 0x00

Kết quả là ổn

UTF-8:

Nguyên:

0x61, 0xc3, 0xb1, 0x6f, 0x00

Đảo ngược:

0x6f, 0xb1, 0xc3, 0x61, 0x00

Kết quả là vô nghĩa và một chuỗi UTF-8 bất hợp pháp

UTF-16 Big Endian:

Nguyên:

0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00

Byte đầu tiên sẽ được coi là bộ kết thúc NUL. Không đảo ngược sẽ diễn ra.

UTF-16 Little Endian:

Nguyên:

0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00

Byte thứ hai sẽ được coi là bộ kết thúc NUL. Kết quả sẽ là 0x61, 0x00, một chuỗi chứa ký tự 'a'.


std :: Reverse sẽ hoạt động cho các loại unicode hai byte, miễn là bạn đang sử dụng chuỗi.
Nhật thực

Tôi không quen thuộc lắm với C ++, nhưng tôi đoán là bất kỳ hàm thư viện tiêu chuẩn đáng kính nào xử lý các chuỗi sẽ có thể xử lý các mã hóa khác nhau, vì vậy tôi đồng ý với bạn. Theo "các thuật toán" này, tôi có nghĩa là các hàm đảo ngược ad-hoc được đăng ở đây.
Juan Pablo Califano

Thật không may, không có thứ gọi là "hàm đáng kính xử lý chuỗi" trong C ++ tiêu chuẩn.
Jem

@Eclipse Nếu nó đảo ngược một cặp thay thế, kết quả sẽ không còn đúng nữa. Unicode thực sự không phải là một bộ ký tự có chiều rộng cố định
phuclv

Điều tôi thích về câu trả lời này là nó cho thấy thứ tự thực tế của byte (mặc dù endian có thể là một cái gì đó - à, tôi thấy bạn thực sự đã xem xét nó) và làm thế nào nó đúng hoặc không chính xác. Nhưng tôi nghĩ rằng câu trả lời sẽ được cải thiện nếu bạn kết hợp một số mã chứng minh điều này.
Pryftan

11

Vì lợi ích của tính đầy đủ, cần chỉ ra rằng có các biểu diễn chuỗi trên các nền tảng khác nhau trong đó số byte trên mỗi ký tự thay đổi tùy theo ký tự. Các lập trình viên trường học cũ sẽ gọi nó là DBCS (Bộ ký tự nhân đôi) . Các lập trình viên hiện đại thường gặp phải điều này trong UTF-8 (cũng như UTF-16 và các chương trình khác). Có những mã hóa khác như vậy là tốt.

Trong bất kỳ sơ đồ mã hóa có chiều rộng thay đổi nào, các thuật toán đơn giản được đăng ở đây ( xấu , không xấu hoặc khác ) sẽ không hoạt động chính xác! Trên thực tế, chúng thậm chí có thể khiến chuỗi trở nên không thể đọc được hoặc thậm chí là chuỗi bất hợp pháp trong sơ đồ mã hóa đó. Xem câu trả lời của Juan Pablo Califano cho một số ví dụ hay.

std :: Reverse () có khả năng vẫn sẽ hoạt động trong trường hợp này, miễn là việc triển khai Thư viện C ++ tiêu chuẩn của bạn (đặc biệt là các trình vòng lặp chuỗi) đã tính đến điều này.


6
std :: đảo ngược KHÔNG đưa điều này vào tài khoản. Nó đảo ngược value_type. Trong trường hợp chuỗi std ::, nó đảo ngược char. Không phải nhân vật.
MSalters

Nói đúng hơn là các lập trình viên cũ của chúng tôi biết về DBCS nhưng cũng biết về UTF-8: vì tất cả chúng ta đều biết rằng các lập trình viên cũng giống như những người nghiện khi họ nói 'một dòng nữa và tôi sẽ bỏ!' Tôi chắc chắn rằng một số lập trình viên cuối cùng đã bỏ việc nhưng lập trình thẳng thắn thực sự giống như một cơn nghiện đối với tôi; Tôi nhận được rút tiền từ không lập trình. Đó là một điểm tốt mà bạn thêm vào đây. Tôi không thích C ++ (Tôi đã rất cố gắng để thích nó thậm chí viết khá nhiều về nó nhưng tôi vẫn không hài lòng về mặt thẩm mỹ để nói) vì vậy tôi không thể nhận xét ở đó nhưng dù sao bạn cũng đưa ra quan điểm tốt có +1.
Pryftan

5

Một cách khác của C ++ (mặc dù tôi có thể sẽ sử dụng std :: Reverse () bản thân mình :) để biểu cảm hơn và nhanh hơn)

str = std::string(str.rbegin(), str.rend());

Cách C (nhiều hơn hoặc ít hơn :)) và xin vui lòng, hãy cẩn thận về thủ thuật XOR để hoán đổi, trình biên dịch đôi khi không thể tối ưu hóa điều đó.

Trong trường hợp như vậy thường là chậm hơn nhiều.

char* reverse(char* s)
{
    char* beg = s, *end = s, tmp;
    while (*end) end++;
    while (end-- > beg)
    { 
        tmp  = *beg; 
        *beg++ = *end;  
        *end =  tmp;
    }
    return s;
} // fixed: check history for details, as those are interesting ones

Tôi sẽ sử dụng strlenđể tìm phần cuối của chuỗi, nếu nó có khả năng dài. Việc triển khai thư viện tốt sẽ sử dụng các vectơ SIMD để tìm kiếm nhanh hơn 1 byte mỗi lần lặp. Nhưng đối với các chuỗi rất ngắn, while (*++end);sẽ được thực hiện trước khi một lệnh gọi hàm thư viện bắt đầu tìm kiếm.
Peter Cordes

@PeterCordes tốt, đồng ý, strlen nên được sử dụng dù sao để dễ đọc. Đối với chuỗi dài hơn, bạn nên luôn luôn giữ độ dài trong một varialbe. strlen trên SIMD thường được đưa ra làm ví dụ với tuyên bố từ chối trách nhiệm này, đó không phải là ứng dụng thực tế hoặc ít nhất là 5 năm trước, khi mã được viết. ;)
pprzemek

1
Nếu bạn muốn điều này chạy nhanh trên CPU thực, bạn sẽ sử dụng xáo trộn SIMD để thực hiện đảo ngược trong khối 16B. : P ví dụ trên x86, _mm_shuffle_epi8(PSHUFB) có thể đảo ngược thứ tự của vectơ 16B, được đưa ra vectơ điều khiển xáo trộn bên phải. Nó có thể có thể chạy ở tốc độ gần như memcpy với một số tối ưu hóa cẩn thận, đặc biệt là với AVX2.
Peter Cordes

char* beg = s-1có hành vi không xác định (ít nhất là nếu strỏ đến phần tử đầu tiên của mảng, đó là trường hợp phổ biến nhất). while (*++end);có hành vi không xác định nếu slà một chuỗi rỗng.
melpomene

@pprzemek Chà, bạn đang đưa ra yêu cầu s-1đã xác định hành vi ngay cả khi strỏ đến phần tử đầu tiên của một mảng, vì vậy, chính bạn là người có thể trích dẫn tiêu chuẩn hỗ trợ.
melpomene

4
#include <cstdio>
#include <cstdlib>
#include <string>

void strrev(char *str)
{
        if( str == NULL )
                return;

        char *end_ptr = &str[strlen(str) - 1];
        char temp;
        while( end_ptr > str )
        {
                temp = *str;
                *str++ = *end_ptr;
                *end_ptr-- = temp;
        }
}

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

        strcpy(buffer, "testing");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "a");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "abc");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "");
        strrev(buffer);
        printf("%s\n", buffer);

        strrev(NULL);

        return 0;
}

Mã này tạo ra đầu ra này:

gnitset
a
cba

2
@uvote, Đừng dùng strcpy. Không bao giờ. Nếu bạn phải sử dụng một cái gì đó như strcpy, hãy sử dụng strncpy. strcpy là nguy hiểm. Nhân tiện, C và C ++ là hai ngôn ngữ riêng biệt với các phương tiện riêng biệt. Tôi nghĩ rằng bạn đang sử dụng các tệp tiêu đề chỉ có sẵn trong C ++, vậy bạn có thực sự cần câu trả lời trong C không?
Onorio Catenacci

6
strcpy hoàn toàn an toàn nếu lập trình viên có thể theo dõi kích thước của mảng, nhiều người sẽ cho rằng strncpy kém an toàn hơn vì nó không đảm bảo chuỗi kết quả bị hủy. Trong mọi trường hợp, không có gì sai khi sử dụng strcpy của uvote ở đây.
Robert Gamble

1
@Onorio Catenacci, strcpy không nguy hiểm nếu bạn biết rằng chuỗi nguồn sẽ nằm gọn trong bộ đệm đích, như trong các trường hợp được đưa ra trong đoạn mã trên. Ngoài ra, strncpy zero - điền vào số lượng ký tự được chỉ định trong tham số kích thước nếu có phòng trống, có thể không mong muốn.
Chris Young

3
Bất cứ ai không thể sử dụng strcpy đúng cách đều không nên lập trình trong C.
Robert Gamble

2
@Robert Đánh bạc, tôi đồng ý. Tuy nhiên, vì tôi không biết cách nào để ngăn mọi người lập trình trong C bất kể năng lực của họ là gì, tôi thường khuyên bạn nên chống lại điều này.
Onorio Catenacci


3

Tôi thích câu trả lời K & R của Evgeny. Tuy nhiên, thật tuyệt khi thấy một phiên bản sử dụng con trỏ. Mặt khác, về cơ bản là giống nhau:

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

char *reverse(char *str) {
    if( str == NULL || !(*str) ) return NULL;
    int i, j = strlen(str)-1;
    char *sallocd;
    sallocd = malloc(sizeof(char) * (j+1));
    for(i=0; j>=0; i++, j--) {
        *(sallocd+i) = *(str+j);
    }
    return sallocd;
}

int main(void) {
    char *s = "a man a plan a canal panama";
    char *sret = reverse(s);
    printf("%s\n", reverse(sret));
    free(sret);
    return 0;
}

3

Hàm đệ quy để đảo ngược một chuỗi tại chỗ (không có bộ đệm phụ, malloc).

Mã ngắn, gợi cảm. Xấu, sử dụng stack xấu.

#include <stdio.h>

/* Store the each value and move to next char going down
 * the stack. Assign value to start ptr and increment 
 * when coming back up the stack (return).
 * Neat code, horrible stack usage.
 *
 * val - value of current pointer.
 * s - start pointer
 * n - next char pointer in string.
 */
char *reverse_r(char val, char *s, char *n)
{
    if (*n)
        s = reverse_r(*n, s, n+1);
   *s = val;
   return s+1;
}

/*
 * expect the string to be passed as argv[1]
 */
int main(int argc, char *argv[])
{
    char *aString;

    if (argc < 2)
    {
        printf("Usage: RSIP <string>\n");
        return 0;
    }

    aString = argv[1];
    printf("String to reverse: %s\n", aString );

    reverse_r(*aString, aString, aString+1); 
    printf("Reversed String:   %s\n", aString );

    return 0;
}

1
Đó là một giải pháp khá thú vị, bạn nên thêm một số dấu vết như printf ("% * s> [% d] Reverse_r ('% c',% p = \"% s \ ",% p = \"% s \ ") \ n ", độ sâu," ", độ sâu, val, s, (s? s:" null "), n, (n? n:" null ")); ở đầu và <ở cuối
Benoît

Đẩy từng cái charlên ngăn xếp không được tính là "tại chỗ". Đặc biệt là không khi bạn thực sự đẩy 4 * 8B mỗi ký tự (trên máy 64 bit: 3 args + địa chỉ trả về).
Peter Cordes

Câu hỏi ban đầu là "Làm thế nào để bạn đảo ngược một chuỗi trong C hoặc C ++ mà không yêu cầu một bộ đệm riêng để giữ chuỗi đảo ngược?" - không có yêu cầu trao đổi 'tại chỗ'. Ngoài ra, giải pháp này đề cập đến việc sử dụng ngăn xếp xấu ngay từ đầu. Tôi có bị bỏ phiếu vì khả năng đọc hiểu của người khác không?
Simon Peverett

1
Tôi sẽ không gọi nó là "gợi cảm" nhưng tôi sẽ nói rằng đó là biểu tình và hướng dẫn. Nếu đệ quy được sử dụng đúng cách nó có thể rất có giá trị. Tuy nhiên, cần phải chỉ ra rằng - lần cuối tôi biết - C thậm chí không yêu cầu một ngăn xếp; tuy nhiên nó không đệ quy. Dù bằng cách nào, đây là một ví dụ về đệ quy mà nếu được sử dụng đúng cách có thể rất hữu ích và có giá trị. Tôi không nghĩ rằng tôi đã từng thấy nó được sử dụng để đảo ngược một chuỗi.
Pryftan


1

Chia sẻ mã của tôi. Là một người học C ++, như một tùy chọn để sử dụng trao đổi (), tôi khiêm tốn xin ý kiến.

void reverse(char* str) {
    int length = strlen(str);
    char* str_head = str;
    char* str_tail = &str[length-1];
    while (str_head < str_tail) 
        swap(*str_head++, *str_tail--);
}


0

Còn nữa:

#include <stdio.h>
#include <strings.h>

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

  char *reverse = argv[argc-1];
  char *left = reverse;
  int length = strlen(reverse);
  char *right = reverse+length-1;
  char temp;

  while(right-left>=1){

    temp=*left;
    *left=*right;
    *right=temp;
    ++left;
    --right;

  }

  printf("%s\n", reverse);

}

Điều này thể hiện số học con trỏ, giống như câu trả lời của tôi, nhưng kết hợp nó với Hoán đổi. Tôi tin rằng câu trả lời này thêm rất nhiều, thực sự. Bạn sẽ có thể hiểu loại mã này trước khi thêm một tỷ thư viện làm phụ thuộc chỉ để nhận một hộp văn bản đơn giản (một cái gì đó tôi thấy quá thường xuyên trong các ứng dụng hiện đại mà tôi có để làm việc)
Stephen J

0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

unsigned char * utf8_reverse(const unsigned char *, int);
void assert_true(bool);

int main(void)
{
    unsigned char str[] = "mañana mañana";
    unsigned char *ret = utf8_reverse(str,  strlen((const char *) str) + 1);

    printf("%s\n", ret);
    assert_true(0 == strncmp((const char *) ret, "anãnam anañam", strlen("anãnam anañam") + 1));

    free(ret);

    return EXIT_SUCCESS;
}

unsigned char * utf8_reverse(const unsigned char *str, int size)
{
    unsigned char *ret = calloc(size, sizeof(unsigned char*));
    int ret_size = 0;
    int pos = size - 2;
    int char_size = 0;

    if (str ==  NULL) {
        fprintf(stderr, "failed to allocate memory.\n");
        exit(EXIT_FAILURE);
    }

    while (pos > -1) {

        if (str[pos] < 0x80) {
            char_size = 1;
        } else if (pos > 0 && str[pos - 1] > 0xC1 && str[pos - 1] < 0xE0) {
            char_size = 2;
        } else if (pos > 1 && str[pos - 2] > 0xDF && str[pos - 2] < 0xF0) {
            char_size = 3;
        } else if (pos > 2 && str[pos - 3] > 0xEF && str[pos - 3] < 0xF5) {
            char_size = 4;
        } else {
            char_size = 1;
        }

        pos -= char_size;
        memcpy(ret + ret_size, str + pos + 1, char_size);
        ret_size += char_size;
    }    

    ret[ret_size] = '\0';

    return ret;
}

void assert_true(bool boolean)
{
    puts(boolean == true ? "true" : "false");
}

0

Với lambda C ++:

 auto reverse = [](std::string& s) -> std::string {
        size_t start = 0, end = s.length() -1;
        char temp;

        while (start < end) {
          temp = s[start];
          s[start++] = s[end];
          s[end--] = temp;
        } 

        return s;
   };

0

Câu trả lời của tôi sẽ giống với hầu hết trong số họ, nhưng vui lòng tìm mã của tôi ở đây.

//Method signature to reverse string
string reverseString(string str);

int main(void){
    string str;
    getline(cin, str);
    str =  reverseString(str);
    cout << "The reveresed string is : " << str;
    return 0;
}

/// <summary>
///     Reverses the input string.
/// </summary>
/// <param name="str">
///    This is the input string which needs to be reversed.
/// </param>
/// <return datatype = string>
///     This method would return the reversed string
/// </return datatype>

string reverseString(string str){
    int length = str.size()-1;
    char temp;
    for( int i=0 ;i<(length/2);i++)
    {
        temp = str[i];
        str[i] = str[length-i];
        str[length-i] = temp;
    }
    return str;
}

0

Tôi nghĩ rằng có một cách khác để đảo ngược chuỗi. lấy đầu vào từ người dùng và đảo ngược nó.

void Rev() {
    char ch;
    cin.get(ch);
    if(ch != '\n') {
        Rev();
        cout.put(ch);
    }
}

Đúng nhưng điều này chỉ in ra thứ tự đảo ngược, phải không? (Tôi không sử dụng C ++ nên có lẽ .put () không làm những gì tôi nghĩ).
Pryftan

-1

Nếu bạn không cần lưu trữ nó, bạn có thể giảm thời gian như thế này:

void showReverse(char s[], int length)
{
    printf("Reversed String without storing is ");
    //could use another variable to test for length, keeping length whole.
    //assumes contiguous memory
    for (; length > 0; length--)
    {
        printf("%c", *(s+ length-1) );
    }
    printf("\n");
}

Tôi dường như là câu trả lời duy nhất không có bộ đệm hoặc biến tạm thời. Tôi sử dụng độ dài của chuỗi, nhưng những người khác làm điều này thêm một chuỗi khác cho đầu (so với đuôi). Tôi giả sử func ngược tiêu chuẩn lưu trữ biến, thông qua trao đổi hoặc một cái gì đó. Vì vậy, tôi có ấn tượng, giả sử toán học con trỏ và loại UTF xếp hàng, rằng đây có thể là câu trả lời duy nhất thực sự trả lời câu hỏi. Các printf () đã thêm có thể được lấy ra, tôi chỉ cần làm nó để trông đẹp hơn cho kết quả. Tôi đã viết điều này cho tốc độ. Không phân bổ thêm hoặc vars. Có lẽ là thuật toán nhanh nhất để hiển thị str ()
Stephen J

-3

Đây là sự đảm nhận của tôi trong C. Đã thực hành và cố gắng ngắn gọn nhất có thể! Bạn nhập một chuỗi thông qua dòng lệnh, tức là ./program_name "nhập chuỗi ở đây"

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

void reverse(int s,int e,int len,char t,char* arg) {
   for(;s<len/2;t=arg[s],arg[s++]=arg[e],arg[e--]=t);
}

int main(int argc,char* argv[]) {
  int s=0,len=strlen(argv[1]),e=len-1; char t,*arg=argv[1];
  reverse(s,e,len,t,arg);
  for(s=0,e=0;e<=len;arg[e]==' '||arg[e]=='\0'?reverse(s,e-1,e+s,t,arg),s=++e:e++);
  printf("%s\n",arg);
}

1
Đó là một mớ hỗn độn không thể đọc được, nhưng rõ ràng bạn sẽ không tìm mã ngắn nhất có thể ( mã golf ) vì một số tên biến có nhiều hơn một ký tự.
Peter Cordes

-4

Nhưng tôi nghĩ thuật toán hoán đổi XOR là tốt nhất ...

char str[]= {"I am doing reverse string"};
char* pStr = str;

for(int i = 0; i != ((int)strlen(str)-1)/2; i++)
{
    char b = *(pStr+i);
    *(pStr+i) = *(pStr+strlen(str)-1-i);
    *(pStr+strlen(str)-1-i) = b;
}

Một cuộc gọi đến strlen()trong điều kiện vòng lặp và trong thân vòng lặp có thể không tối ưu hóa đi.
Peter Cordes

-5

Đây là cách sạch nhất, an toàn nhất, dễ nhất để đảo ngược chuỗi trong C ++ (theo ý kiến ​​của tôi):

#include <string>

void swap(std::string& str, int index1, int index2) {

    char temp = str[index1];
    str[index1] = str[index2];
    str[index2] = temp;

}

void reverse(std::string& str) {

    for (int i = 0; i < str.size() / 2; i++)
        swap(str, i, str.size() - i - 1);

}

Một cách khác là sử dụng std::swap, nhưng tôi thích xác định các chức năng của riêng mình - đó là một bài tập thú vị và bạn không cần includethêm bất cứ điều gì.


-5
#include<stdio.h>
#include<conio.h>

int main()
{
    char *my_string = "THIS_IS_MY_STRING";
    char *rev_my_string = my_string;

    while (*++rev_my_string != '\0')
        ;

    while (rev_my_string-- != (my_string-1))
    {
        printf("%c", *rev_my_string);
    }

    getchar();
    return 0;
}

Đây là mã được tối ưu hóa trong ngôn ngữ C để đảo ngược chuỗi ... Và nó rất đơn giản; chỉ cần sử dụng một con trỏ đơn giản để thực hiện công việc ...


Điều này in chuỗi một char tại một thời điểm. Nó không đảo ngược nó tại chỗ. Nó cũng chờ đầu vào của người dùng mà không có lý do.
Peter Cordes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.