今日は再帰をいじっています。多くの場合、十分に使用されていないプログラミング手法です。
文字列を再帰的に逆にすることにしました。ここに私が思いついたものがあります:
//A method to reverse a string using recursion
public String reverseString(String s){
char c = s.charAt(s.length()-1);
if(s.length() == 1) return Character.toString(c);
return c + reverseString(s.substring(0,s.length()-1));
}
私の質問:Javaにはもっと良い方法がありますか?
最良の方法は、再帰を使用しないことです。これらは通常、実際のベストプラクティスではなく、再帰の概念を生徒に教えるために使用されます。そのため、あなたのやり方は大丈夫です。 Javaで再帰を使用しないでください;)
PS。さっき言ったことはさておき、""
私の再帰関数のベースケースとして:
public String reverseString(String s){
if (s.length() == 0)
return s;
return reverseString(s.substring(1)) + s.charAt(0);
}
これを行う場合、文字列は不変であり、そのようにすると文字列をあちこちにコピーするため、文字配列を操作する必要があります。
これはテストされておらず、完全に意識の流れです。おそらくどこかにOB1があります。そして、非常にJavaではありません。
public String reverseString(String s)
{
char[] cstr = s.getChars();
reverseCStr(cstr, 0, s.length - 1);
return new String(cstr);
}
/**
* Reverse a character array in place.
*/
private void reverseCStr(char[] a, int s, int e)
{
// This is the middle of the array; we're done.
if (e - s <= 0)
return;
char t = a[s];
a[s] = a[e];
a[e] = t;
reverseCStr(a, s + 1, e - 1);
}
深く入れ子にしたくありません。分割統治は進むべき道です。また、一時文字列の合計サイズを削減し、並列化に対応します。
public static String reverseString(String str) {
int len = str.length();
return len<=1 ? str : (
reverseString(str.substring(len/2))+
reverseString(str.substring(0, len/2))
);
}
(テストされていません-これはstackoverflowです。)
String.concat
の代わりに +
は、明確さを犠牲にしてパフォーマンスを改善します。
編集:楽しみのために、単純なアルゴリズムの末尾再帰に優しいバージョンです。
public static String reverseString(String str) {
return reverseString("", str);
}
private static String reverseString(String reversed, String forward) {
return forward.equals("") ? reversed : (
reverseString(reversed+forward.charAt(0), forward.substring(1))
);
}
サロゲートペアの正しい処理は、関心のある読者に任されています。
ここにうまく機能している私の再帰的な逆関数があります
public static String rev(String instr){
if(instr.length()<=1){
return instr;
} else {
return (instr.charAt(instr.length()-1)+rev(instr.substring(0,instr.length()-1)) );
}
}
ちょっとしたことのために、StringBuilder
(String
sの操作よりも一般的に推奨される)を使用した末尾再帰メソッドを示します。
public String reverseString(String s_) {
StringBuilder r = new StringBuilder();
StringBuilder s = new StringBuilder(s_);
r = reverseStringHelper(r, s);
return r.toString();
}
private StringBuilder reverseStringHelper(StringBuilder r, StringBuilder s) {
if (s.length() == 0)
return r;
else
return reverseStringHelper(r.append(s.charAt(0)), s.deleteCharAt(0));
}
テストされていないので、私は長年Javaを扱っていませんでしたが、これはほぼ正しいはずです。
実際のコードを記述している場合(再帰を学習していない場合)、StringBuilderのreverse()メソッドを使用します。 Javaチュートリアル は次の例を示します。
String palindrome = "Dot saw I was Tod";
StringBuilder sb = new StringBuilder(palindrome);
sb.reverse(); // reverse it
System.out.println(sb);
Javaでは、ストリングは不変であるため、ストリングの連結は見た目よりも複雑になります。
連結ごとに、元の文字列の内容をコピーする新しい文字列を作成し、線形の複雑さをもたらしますO(n)ここでnは文字列の長さです。このような操作はO(m * n)であり、2次の複雑さであると言えますO(n ^ 2)。
追加ごとにO(1)複雑さを持つStringBuilderを使用できます。以下は、StringBuilderを使用した再帰プログラムです。これはn/2スタックフレームのみを使用するため、s.charAt(s.length-1) + reverse(s.subString(0, s.length-2);
のような通常の再帰呼び出しよりもスペースの複雑さは少なくなります。
public class StringReverseRecursive {
public static void main(String[] args) {
String s = "lasrever gnirts fo noitatnemelpmi evisrucer a si sihT";
StringBuilder sb = new StringBuilder(s);
reverse(s, sb, 0, sb.length() - 1);
System.out.println(sb.toString());
}
public static void reverse(String s, StringBuilder sb, int low, int high) {
if (low > high)
return;
sb.setCharAt(low, s.charAt(high));
sb.setCharAt(high, s.charAt(low));
reverse(s, sb, ++low, --high);
}
}
あなたが「より良い」と定義するものに依存します。 :-)真剣に、しかし。ソリューションでは、本質的に再帰の最大深度を使用します。スタックサイズが "より良い"の定義に懸念がある場合は、次のようなものを使用することをお勧めします。
public String reverseString(String s) {
if (s.length() == 1) return s;
return reverseString(s.substring(s.length() / 2, s.length() -1) + reverseString(0, s.length() / 2);
}
これが動作し、再帰を使用することがわかったものです。 str.length()
をstrLength引数として渡すことができます
private static String reverse(String str, int strLength) {
String result = "";
if(strLength > 0)
result = str.charAt(strLength - 1) + reverse(str, strLength - 1);
return result;
}
関数呼び出し:
//str:string to be reversed,i=0,j=str.length-1
public void reverseString(String str,int i,int j)
{
if(i==j)
{
System.out.println(str);
return;
}
char x=str.charAt(i);
str=str.replace(str.charAt(i),str.charAt(j));
str=str.replace(str.charAt(j),x);
i++;j--;
reverseString(str,i,j);
}
この方法も機能します。
String rev="";
public String reverseString(String s){
if (s.length()==0) return "";
return rev+s.substring(s.length()-1,s.length())+reverseString(s.substring(0, s.length()-1));
}
少ないコードが良いと思うなら....
static String reverse(String str){
return str.length()>=2 ? str.charAt(str.length()-1) + reverse(str.substring(0,str.length()-1)) : str ;
}
すでに約20の回答がありますが、再帰アルゴリズムも同様にスローします。少し冗長かもしれませんが、少なくとも読みやすいです。
public static String reverseString(String str) {
return reverseString("", str);
}
private static String reverseString(String result, String original) {
if (original.length() == 0) {
return result;
} else {
int length = original.length();
String lastLetter = original.substring(length - 1, length);
original = original.substring(0, length - 1);
return reverseString(result + lastLetter, original);
}
}
コードは基本的に再帰的に文字列の末尾を取得し、先頭に移動します。たとえば、逆にしたい文字列が「jam」の場合、ヘルパーメソッドが呼び出されるたびに、結果と元の文字列は次のようになります。
// result: original:
// "" jam
// m ja
// ma j
// maj ""
public static String rev(String name){
if(name.length()>=1){
System.out.print(name.charAt(name.length()-1));
return rev(name.substring(0,name.length()-1));
}
else{
return ""+name.substring(0);
}
}
外部変数を試して、1 x 1のすべての文字を追加できます。
public static String back="";
public static String reverseString(String str){
if(str.length()==0){
return back;
}else {
back+=str.charAt(str.length()-1);
lees(str.substring(0,str.length()-1));
return back;
}
}
以下を試してください:
public class reverse2
{
public static void main(String name)
{
String revname=new StringBuffer(name).reverse().toString();
System.out.println("original string " + name + " and the reverse of it is " + revname);
}
}
再帰的なメソッド呼び出しを使用して文字列を反転します。
サンプルコード
public static String reverseString(String s) {
if (s.length() == 0) {
return s;
}
else {
return s.charAt(s.length() - 1) + reverseString(s.substring(0, s.length() - 1));
}
}
このコンテキストでは、これはまったく必要ありませんが、独自のスタックを作成すれば、再帰をシミュレートして再帰の深さの問題を回避できます。
再帰を反復的に実装できます。再帰は、本質的に再帰的なアルゴリズムを持っている場合に必要になる可能性がありますが、大きな問題サイズに対しても実行する必要があります。
String recIterReverse (String Word){
Stack <String> stack = new Stack <String> ();
stack.Push(Word);
String result = "";
while (!stack.isEmpty()){
String temp = stack.pop();
result = temp.charAt(0) + result;
if (temp.length() > 1){
stack.Push(temp.substring(1));
}
}
return result;
}
これは私の解決策であり、上記の多くの解決策で文字列の長さを取得していますが、理想的には必要ありません。 Zenは再帰を使用し、文字列の最初の文字を切り取り、残りを再帰メソッドに渡します。わーい!!私たちは解決策を得ました。
private static void printReverse(String str) {
if (!str.isEmpty()) {
String firstChar = str.substring(0, 1); //Get first char of String
String newstr = str.substring(0, 0) + str.substring(1); // Get remaining string
printReverse(newstr); // Recursion magic
System.out.print(firstChar); //Output
}
}
それは間違いなく私が再帰的に文字列を逆にする方法です(あなたの状態で空の文字列の場合にそれを拡張するのはいいかもしれませんが)。根本的に良い方法はないと思います。
編集:文字列を作成するよりも、ドリフトを取得する場合は、文字配列を操作し、再帰のチェーンに「カットオフ」長さを渡す方が効率的です。ただし、そもそもひじょうに効率的な手法ではないため、これを実際に見直す価値はありません。
ここ は不変バージョンです:
String reverse(String str) {
if(str.length()<2) return str;
return reverse(str.substring(1)) +str.charAt(0);
}
および末尾再帰バージョン:
String reverseTail(String str) {
if(str.length()<2) return str;
return str.charAt(str.length()-1)+ reverseTail(str.substring(0,str.length()-1));
Mehrdad のように、再帰を使用しないのが最善です。ただし、使用する場合は、各呼び出しの最初と最後の両方の文字を保持して、再帰呼び出しの数を半分にすることもできます。あれは、
public String reverseString(String s){
int len = s.length();
if (len <= 1) {
return s;
}
char fst = s.charAt(0);
char lst = s.charAt(len - 1);
return lst + reverseString(s.substring(1, len - 2)) + fst;
}
これは、空の文字列のケースも処理します。おそらく、適切な容量のStringBuilderを渡すと、さらに高速になりますが、それは読者の課題として残されています;)
public class StringUtility {
public static void main(String[] args) {
StringUtility stringUtility = new StringUtility();
String input = "santosh123";
int middle = input.length() / 2;
middle = middle - 1;
System.out.println(stringUtility.stringReverse(input, middle));
}
public String stringReverse(String input, int middle) {
if (middle == -1) {
System.out.println("if");
return input;
} else {
System.out.println("else");
input = swapChar(input, middle);
middle = middle - 1;
return stringReverse(input, middle);
}
}
private String swapChar(String input, int middle) {
StringBuilder str = new StringBuilder(input);
char begin = str.charAt(middle);
int endIndex = input.length() - middle - 1;
char end = str.charAt(endIndex);
str.setCharAt(middle, end);
str.setCharAt(endIndex, begin);
System.out.println(str + " " + middle + " " + endIndex);
return str.toString();
}
}
public String reverseString (String s) {
if (s != null && s.length () > 0 ) {
rev = rev + s.substring (s.length () - 1);
reverseString (s.substring (0, s.length () - 1));
}
return rev;
}
基本的なアイデアをキャプチャしますが、最後の文字を抽出しても明確さは向上しません。私は以下を好むが、他は好まないかもしれない:
public class Foo
{
public static void main(String[] argv) throws Exception
{
System.out.println(reverse("a"));
System.out.println(reverse("ab"));
System.out.println(reverse("abc"));
}
public final static String reverse(String s)
{
// oft-repeated call, so reduce clutter with var
int length = s.length();
if (length <= 1)
return s;
else
return s.substring(length - 1) + reverse(s.substring(0, length - 1));
}
}