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?
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?
Câu trả lời:
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 - 1
thay 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.)
(Đâ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
và *q
là 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;
}
Ví dụ:
$ ./strrev Räksmörgås ░▒▓○◔◑◕●
░▒▓○◔◑◕● ●◕◑◔○▓▒░
Räksmörgås sågrömskäR
./strrev verrts/.
#include <algorithm>
std::reverse(str.begin(), str.end());
Đây là cách đơn giản nhất trong C ++.
Đọ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;
}
}
s
phả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
Đảo ngược một chuỗi tại chỗ (trực quan hóa):
Không ác C, giả sử trường hợp phổ biến trong đó chuỗi là char
mả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--;
}
}
int temp
, giải pháp này có vẻ tốt nhất. +1
!(*str)
mặc dù.
strchr()
tìm kiếm '\0'
. Tôi nghĩ về cả hai. Không cần một vòng lặp.
Bạn sử dụng std::reverse
thuật toán từ Thư viện chuẩn C ++.
Đã đượ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);
size_t
mặc dù.
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>
.
Lưu ý rằng vẻ đẹp của std :: Reverse là nó hoạt động với char *
chuỗi và std::wstring
s cũng như std::string
s
void strrev(char *str)
{
if (str == NULL)
return;
std::reverse(str, str + strlen(str));
}
*
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;
.
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'.
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.
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
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.
_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.
char* beg = s-1
có hành vi không xác định (ít nhất là nếu s
trỏ đế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 s
là một chuỗi rỗng.
s-1
đã xác định hành vi ngay cả khi s
trỏ đế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ợ.
#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
Trong trường hợp bạn đang sử dụng GLib, nó có hai chức năng đó là g_strreverse () và g_utf8_strreverse ()
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;
}
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;
}
char
lê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ề).
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--);
}
Nếu bạn đang sử dụng ATL / MFC CString
, chỉ cần gọi CString::MakeReverse()
.
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);
}
#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");
}
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;
}
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);
}
}
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");
}
Đâ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);
}
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;
}
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.
Đâ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 include
thêm bất cứ điều gì.
#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 ...