私はC++ For Everyoneという本を読んでいて、関数string reverse(string str)
を書くと言われている演習の1つです。ここで、戻り値はstr
の逆です。
誰かが基本的なコードを書いて私に説明してもらえますか?私は昨日からこの質問を見つめていましたが、理解できません。私が得た最も遠いのは、関数にstr
の最初の文字を返すようにすることです(これはまだどのように起こったのかわかりません)
これは私が得た限りです(この質問を投稿してから1時間後):
string reverse(string str)
{
string Word = "";
if (str.length() <= 1)
{
return str;
}
else
{
string str_copy = str;
int n = str_copy.length() - 1;
string last_letter = str_copy.substr(n, 1);
str_copy = str_copy.substr(0, n);
Word += reverse(str_copy);
return str_copy;
}
return Word;
}
「Wolf」と入力すると、Wolが返されます。誰かがここで私を助けてくれますreturn Word
の代わりにreturn str_copy
の場合はw
を取得しますreturn last_letter
の場合はl
を取得します
代わりに、再帰的アルゴリズム自体について説明します。 「tupni」を生成するはずの「input」の例を見てください。次の方法で文字列を再帰的に反転できます。
これを試してください
string reverse(string &s)
{
if( s.length() == 0 ) // end condtion to stop recursion
return "";
string last(1,s[s.length()-1]); // create string with last character
string reversed = reverse(s.substr(0,s.length()-1));
return last+reversed; // Make he last character first
}
再帰関数には次のプロパティが必要です
この再帰関数は基本的に最後の文字の文字列を作成し、最後の文字を除く文字列の残りの部分で自分自身を再度呼び出します。実際の切り替えは、last + reversedが返される最後の行で行われます。それが逆の場合、何も起こりません。
それは非常に非効率的ですが、概念を示すために機能します。
再帰を処理するためのより良い方法を提案するためだけに:
C++で再帰を使用した文字列の反転:
#include <iostream>
#include <string>
using namespace std;
string reverseStringRecursively(string str){
if (str.length() == 1) {
return str;
}else{
return reverseStringRecursively(str.substr(1,str.length())) + str.at(0);
}
}
int main()
{
string str;
cout<<"Enter the string to reverse : ";
cin>>str;
cout<<"The reversed string is : "<<reverseStringRecursively(str);
return 0;
}
本格的なアルゴリズムは作成しませんが、ヒントは次のとおりです。
最も外側の2つの文字を入れ替えて、中央の文字に同じものを適用するのはどうですか?
ああ、その本が本当にこれに適切な関数シグネチャとしてstring reverse(string str)
を提案した場合は、それを捨てて、代わりに 良い本 を購入してください。
これは、入力文字列を逆にする再帰関数の私のバージョンです。
void reverse(char *s, size_t len)
{
if ( len <= 1 || !s )
{
return;
}
std::swap(s[0], s[len-1]);// swap first and last simbols
s++; // move pointer to the following char
reverse(s, len-2); // shorten len of string
}
最短で最も簡単
class Solution {
public:
string reverseString(string s) {
string str;
if(s != "\0"){
str = reverseString(s.substr(1, s.length()));
str += s.substr(0,1);
}
return str;
}
};
1行の再帰的ソリューション:
string RecursiveReverse(string str, string prev = "") {
return (str.length() == 0 ? prev : RecursiveReverse(str.substr(0, str.length()-1), prev += str[str.length()-1]));
}
あなたはそれをこのように呼びます:
cout << RecursiveReverse("String to Reverse");
私は解決策を与えるべきではないことを知っていますが、誰もこの簡単な解決策について言及していないので、私はそれを共有する必要があります。コードは文字通りアルゴリズムなので、擬似コードは必要ないと思います。
void c_plusplus_recursive_swap_reverse(std::string::iterator start,
std::string::iterator end)
{
if(start >= end) {
return;
}
std::iter_swap(start, end);
c_plusplus_recursive_swap_reverse(++start, --end);
}
それを使用するには:
c_plusplus_recursive_swap_reverse(temp.begin(), temp.end());
std :: reverseと同様に独自のリバースを実装できます。
template <typename BidirIt>
void reverse(BidirIt first, BidirIt last)
{
if((first == last) || (first == --last))
return;
std::iter_swap(first, last);
reverse(++first, last);
}
文字列はその場で逆にすることができます。可能な限り小さい文字列、つまり1文字の文字列から開始する場合は、何もする必要はありません。これは、再帰呼び出しを停止または復帰する場所であり、基本ケースになります。
次に、最小の文字列、つまり2文字以上を交換する一般的な方法を考える必要があります。最も単純なロジックは、現在の文字を交換することですstr[current_index]
反対側に文字がありますstr[str_length-1 - current_index]
。
最後に、次のインデックスのために逆関数を再度呼び出します。
#include <iostream>
using namespace std;
void reverse_string(std::string& str, int index, int length) {
// Base case: if its a single element, no need to swap
// stop swapping as soon as we reach the mid, hence index*2
// otherwise we will reverse the already reversed string
if( (length - index*2) <= 1 ) {
return;
}
// Reverse logic and recursion:
// swap current and opposite index
std::swap(str[index], str[length-1 - index]);
// do the same for next character (index+1)
reverse_string(str, index+1, length);
}
int main() {
std::string s = "World";
reverse_string(s, 0, s.length());
std::cout << s << endl;
}
私はこのようなことをしました、それはその場で逆転をしました。私は、弦を2つの極端な端から弦の中心まで横断する、2つの変数を取りました。それらが互いに重なるか等しい場合、反転は終了します。
例を見てみましょう:string str = "abcd"
を入力し、関数を次のように呼び出します
ReverseString(str,0,str.length()-1);
変数ポインタを再帰的にインクリメント/デクリメントします。最初にポインタが'a'
と'd'
を指し、それらを交換し、次に'b'
と'c'
を指し、それらを交換します。最終的にはi >= j
であり、基本ケースがtrueである必要があるため、再帰は終了します。この質問の主なポイントは、入力文字列を参照として渡すことです。
string ReverseString(string& str,int i,int j){
if(str.length() < 1 || str == "" || i >= j){
return "";
}
else{
char temp = str[i];
str[i] = str[j];
str[j] = temp;
ReverseString(str,i+1,j-1);
}
return str;
}
既存のすべてのソリューションには、実際には何も実行しないコードが多すぎるため、これが私の見解です。
_#include <iostream>
#include <string>
std::string
r(std::string s)
{
if (s.empty())
return s;
return r(s.substr(1)) + s[0];
}
int
main()
{
std::cout << r("testing") << std::endl;
}
_
P.S.私はこの質問に出くわし、Cの_std::string
_の_s+1
_が何であるか_char *
_のC++の方法を見つけようとしました。 s.substr(1, s.length()-1)
のルート全体に行かずに、見た目が醜い。結局、_std::string::npos
_があります。これは、文字列の終わりまでを意味し、すでに2番目の引数のデフォルト値であるため、s.substr(1)
で十分です(さらに、looksより効率的で、C)の単純な_s + 1
_と同等です。
ただし、コンパイラが末尾再帰の最適化と呼ばれる処理を実行できない限り、一般に、入力が大きくなるにつれて再帰はスケーリングされないことに注意してください。 (命令型言語では再帰に依存することはめったにありません。)
ただし、末尾再帰の最適化をアクティブ化するには、通常、(0)再帰はreturn
ステートメント内でのみ発生し、(1)それ以上の操作は実行されないことが必要です。親関数での再帰呼び出しの結果。
たとえば、上記の場合、_+ s[0]
_は、子呼び出しが完了した後に親によって論理的に実行されます(そして、より醜いs[s.length()-1] +
ルートに移動しても、おそらくそうなるでしょう)。また、ほとんどのコンパイラが末尾再帰の最適化を実行できなくなる可能性があるため、大きな入力で関数が非常に非効率になります(ヒープの枯渇により完全に壊れていない場合)。
(価値があるので、より末尾再帰に適したソリューションを書いてみました(関数自体への引数を介して戻り結果を増やすようにしてください)が、結果のバイナリの分解は、それがより複雑であることを示唆しているようですC++のような命令型言語では、 gcc:C++でstd :: stringを返すと末尾再帰はありませんか? を参照してください。)