2010-03-13 3 views
32

Каков наиболее эффективный способ изменить строку на Java? Должен ли я использовать какой-то оператор xor? Легким способом было бы поместить все символы в стек и снова вернуть их в строку, но я сомневаюсь, что это очень эффективный способ сделать это.Каков наиболее эффективный алгоритм для обращения к String в Java?

И, пожалуйста, не говорите мне использовать встроенную функцию в Java. Мне интересно узнать, как сделать это, чтобы не использовать эффективную функцию, но не зная, почему она эффективна или как она создана.

+7

Такого рода вопросы были забиты камнями до смерти для C/C++. В частности, искусство компьютерного программирования Д. Кнута подробно описано. – NomeN

+0

«Забитый камнями», хахаха, мне это нравится. – Zaki

+2

Из любопытства: кто-нибудь знает о реальном использовании для этого? Я имею в виду место, где есть необходимость в * эффективном * алгоритме реверсирования строки *? –

ответ

48

Вы говорите, что вы хотите знать наиболее эффективный способ, и вы не хотите знать некоторые стандартные встроенный способ сделать это. Тогда я говорю вам: RTSL (читайте источник, luke):

Проверьте исходный код AbstractStringBuilder#reverse, который вызывается StringBuilder # reverse. Бьюсь об заклад, это некоторые вещи, которые вы бы не рассмотрели для надежной обратной операции.

+0

Кстати ... когда я говорю «делаю ставку на то, что ты бы подумал» ... Я говорил о суррогатных парах :-). Вы можете увидеть это в коде. – Tom

+2

+1 для ссылки на источник. Один из лучших способов узнать, как реализовать что-то, - это посмотреть, как это делается в реальном мире. –

+0

+1 для прямого ответа на вопрос, являющийся одним из немногих, что не касается касательной –

20

Вы сказали, что вы не хотите, чтобы сделать это легкий путь, но для тех, кто погуглить вы должны использовать StringBuilder.reverse:

String reversed = new StringBuilder(s).reverse().toString(); 

Если вам нужно реализовать его самостоятельно, а затем перебрать символы в обратном порядке и добавьте их в StringBuilder. Вы должны быть осторожны, если есть (или могут быть) суррогатные пары, поскольку они не должны быть отменены. Вышеуказанный метод делает это автоматически, поэтому вы должны использовать его, если это возможно.

+5

Предпочитаете 'StringBuilder'' 'StringBuffer', если вам не нужна безопасность потоков. – Tom

+0

-1 за неуважение «И, пожалуйста, не говорите мне, чтобы я использовал встроенную функцию в Java». Извините за это;) Я думаю, Халтнер хотел бы знать фонов/механиков. Однако, я думаю, вопрос не должен быть помечен java. – Tedil

+0

@Tedil: Вопрос, скорее всего, будет помечен домашним заданием. –

34

Следующие не имеют суррогатных пар UTF-16.

public static String reverse(String orig) 
{ 
    char[] s = orig.toCharArray(); 
    int n = s.length; 
    int halfLength = n/2; 
    for (int i=0; i<halfLength; i++) 
    { 
     char temp = s[i]; 
     s[i] = s[n-1-i]; 
     s[n-1-i] = temp; 
    } 
    return new String(s); 
} 
+1

haha ​​Я закодировал ТОЧНУЮ ту же самую вещь, прежде чем искать в Интернете :) +1 – Eugene

+0

Это просто и точно, не знаю, почему другие люди отвечают на него по-разному. – Hafiz

+0

Hi Simon. Не возражаете ли вы объяснить мне это решение? – theGreenCabbage

5

Самый быстрый способ будет использовать метод reverse() на StringBuilder или StringBuffer классов :)

Если вы хотите реализовать его самостоятельно, вы можете получить массив символов, выделить второй массив символов и переместить символы , в псевдокоде это будет примерно так:

String reverse(String str) { 
    char[] c = str.getCharArray 
    char[] r = new char[c.length]; 
    int end = c.length - 1 

    for (int n = 0; n <= end; n++) { 
     r[n] = c[end - n]; 
    } 

    return new String(r); 
} 

Вы также можете запустить половину длины массива и поменять местами символы, проверки, связанные медленные вещи вниз, вероятно.

+0

Класс 'String' не имеет метода' reverse() '. Кроме того, ваш псевдокод ошибочен, так как ваши массивы иногда основаны на 0, а иногда и на основе 1. –

+0

У меня плохое, StringBUilder и StringBuffer есть обратное, я это изменю. Массивы Java основаны на 0, и вопрос помечен как Java. – rsp

+0

Нет, все еще неправильно. Теперь он произведет цепочку длины len-1. Вы хотите: 'for (int n = 0; n

1

Если вы не хотите использовать какую-либо встроенную функцию, вам нужно вернуться со строкой к ее составным частям: массив символов.

Теперь вопрос становится тем, что является наиболее эффективным способом обращения вспять массива? Ответ на этот вопрос на практике также зависит от использования памяти (для очень больших строк), но теоретически эффективность в этих случаях измеряется в доступе к массиву.

Самый простой способ - создать новый массив и заполнить его значениями, которые вы встречаете, при обратном итерации по исходному массиву и возврату нового массива. (Хотя с временной переменной вы также можете сделать это без дополнительного массива, как в ответе Саймона Никерсона).

Таким образом вы получаете доступ к каждому элементу ровно один раз для массива с n элементами. Таким образом, получается эффективность O (n).

3

Я не совсем уверен, что вы имеете в виду, когда говорите, что вам нужен эффективный алгоритм.

Способы реверсирования строки, я могу думать, являются (все они уже упоминалось в других ответах):

  1. использовать стек (ваша идея).

  2. Создайте новую обратную строку, добавив символы поочередно в обратном порядке от исходной строки к пустой строке String/StringBuilder/char [].

  3. Обмен всеми символами в первой половине строки со своей соответствующей позицией в последней половине (то есть i-й символ заменяется символом (длиной-i-1) -го символа).

Дело в том, что все они имеют один и тот же выполнения сложности: O (N). Таким образом, на самом деле нельзя утверждать, что любой из них значительно лучше других для очень больших значений N (т.е. очень больших строк).

У третьего метода есть что-то для этого, другие два требуют O (N) дополнительного пространства (для стека или новой строки), в то время как он может выполнять свопы на месте. Но Строки неизменны в Java, поэтому вам нужно выполнить свопы на вновь созданной StringBuilder/char [], и в итоге вам понадобится дополнительное пространство O (N).

3
public class ReverseInPlace { 

    static char[] str=null; 

    public static void main(String s[]) { 
     if(s.length==0) 
     System.exit(-1); 

     str=s[0].toCharArray(); 

     int begin=0; 
     int end=str.length-1; 

     System.out.print("Original string="); 
     for(int i=0; i<str.length; i++){ 
     System.out.print(str[i]); 
     } 

     while(begin<end){ 
      str[begin]= (char) (str[begin]^str[end]); 
      str[end]= (char) (str[begin]^str[end]); 
      str[begin]= (char) (str[end]^str[begin]); 

      begin++; 
      end--;  
     } 

     System.out.print("\n" + "Reversed string="); 
     for(int i=0; i<str.length; i++){ 
     System.out.print(str[i]); 
     } 

    } 
} 
+1

Хотя я благодарю вас за ваш ответ, прошло уже полгода с тех пор, как я написал этот вопрос, и проблема с тех пор давно решена, но спасибо за ваш ответ. – Hultner

+0

как насчет использования чека, чтобы отметить ответы как правильные по другим вопросам! –

8

старый пост & вопрос, однако до сих пор не видел ответы, относящиеся к рекурсии. Рекурсивный метод обратной заданной строки с, без ретрансляции на встроенных функций JDK

public static String reverse(String s) { 
    if (s.length() <= 1) { 
     return s; 
    } 
    return reverse(s.substring(1)) + s.charAt(0); 
} 

`

0

Использование Строка:

String abc = "abcd"; 
int a= abc.length(); 

String reverse=""; 

for (int i=a-1;i>=0 ;i--) 
{ 
    reverse= reverse + abc.charAt(i); 
} 
System.out.println("Reverse of String abcd using invert array is :"+reverse); 

Использование StringBuilder:

String abc = "abcd"; 
    int a= abc.length(); 
    StringBuilder sb1 = new StringBuilder(); 

    for (int i=a-1;i>=0 ;i--) 
    { 
     sb1= sb1.append(abc.charAt(i)); 
    } 
    System.out.println("Reverse of String abcd using StringBuilder is :"+sb1); 
+2

Этот вопрос более 3 лет, и так принято. Вопрос состоял в том, как эффективно * быстро изменить строку *. Код эффективен, но * далеко не эффективен. –

+0

@heuster: Хотя это на самом деле означает, что это идеальный источник понимания, по крайней мере, если вы объяснили, почему он неэффективен. – omilke

+2

@Olli достаточно справедливо. Строки в Java являются * неизменяемыми *, поэтому строка 'reverse + abc.charAt (i)' создает новую строку длины 'i + 1' (вместо добавления символа в существующую строку). Поэтому цикл создает строки 'a' в целом, длины' 0, 1, ..., a-1'. В общем случае для этого подхода требуется дополнительная память O (n^2). Временная сложность никогда не может быть лучше, чем сложность пространства, поэтому время работы равно «O (n^2)». Как видно из ответа MAK, «эффективный» алгоритм должен иметь «O (n)» временную сложность. –

0

Один из вариантов может быть , заменяя элементы.

int n = length - 1; 
char []strArray = str.toCharArray(); 
for (int j = 0; j < n; j++) { 
    char temp = strArray[j]; 
    char temp2 = strArray[n]; 
    strArray[j] = temp2; 
    strArray[n] = temp; 
    n--; 
    } 
0
public static void main(String[] args){ 
    String string ="abcdefghijklmnopqrstuvwxyz"; 
    StringBuilder sb = new StringBuilder(string); 
    sb.reverse(); 

    System.out.println(sb); 
} 
0
public static String Reverse(String word){ 
     String temp = ""; 
     char[] arr = word.toCharArray(); 
     for(int i = arr.length-1;i>=0;i--){ 
      temp = temp+arr[i]; 
     } 
     return temp; 
    } 
+0

Подумайте о том, как добавить объяснение в соответствие с кодом. – krsteeve

+0

Добавление к строке обычно является медленным процессом. – Teepeemm

2

Я думаю, что если вы действительно не имеете проблем с производительностью, вы должны просто идти с наиболее читаемым раствором, который является:

StringUtils.reverse("Hello World"); 
+2

Это означало больше как научный вопрос, а затем что-то для фактического производственного кода – Hultner

1

Я бы просто сделать это таким без использования какой-либо одной функции утилиты. Достаточно класс String.

public class MyStringUtil { 

    public static void main(String[] args) { 
     String reversedString = reverse("StringToReverse"); 
     System.out.println("Reversed String : " + reversedString); 
    } 

    /** 
    * Reverses the given string and returns reversed string 
    * 
    * @param s Input String 
    * @returns reversed string 
    */ 
    private static String reverse(String s) { 
     char[] charArray = s.toCharArray(); // Returns the String's internal character array copy 
     int j = charArray.length - 1; 
     for (int i = 0; charArray.length > 0 && i < j; i++, j--) { 
      char ch = charArray[i]; 
      charArray[i] = charArray[j]; 
      charArray[j] = ch; 
     } 
     return charArray.toString(); 
    } 
} 

Проверьте его. Ура !!

1
char* rev(char* str) 
    { 
    int end= strlen(str)-1; 
    int start = 0; 

    while(start<end) 
    { 
    str[start] ^= str[end]; 
    str[end] ^= str[start]; 
    str[start]^= str[end]; 

    ++start; 
    --end; 
    } 

    return str; 
} 

=========================

Хотите знать, как это работает?

Первая операция:

x1 = x1 XOR x2 

x1: 1 0 0 
x2: 1 1 1 
New x1: 0 1 1 

Вторая операция

x2 = x2 XOR x1 

x1: 0 1 1 
x2: 1 1 1 
New x2: 1 0 0 
//Notice that X2 has become X1 now 

Третья операция:

x1 = x1 XOR x2 
x1: 0 1 1 
x2: 1 0 0 
New x1: 1 1 1 
//Notice that X1 became X2 
+0

Этот вопрос конкретно касается Java, а не C++, поэтому 'char *' должен быть 'char []' или 'String'. Но более принципиально, используя трюк XOR на самом деле быстрее, чем просто использовать переменную temp для переключения двух переменных? – Teepeemm

+0

Извините, что он будет опубликован на C++. XOR - фактически самая быстрая операция, которую может сделать процессор, и хорошая часть - это поддержка всех ЦП. Причина этого довольно проста: ворота XOR могут быть созданы только с 4 NAND-воротами или 5 NOR-воротами, что означает, что его легко создать с использованием ткани вашего кремния. Неудивительно, что все процессоры, о которых я знаю, могут выполнить вашу операцию XOR за 1 такт (или даже меньше). (Обратите внимание, что это то, что я прочитал несколько дней назад на одной из сообщений и записал для справки. У меня больше нет этой ссылки.) –

+0

Какую модель эффективности вы предлагаете и как результаты теста для этого подхода сравним с предыдущими, особенно с abiolaaye? – greybeard

0
public static string getReverse(string str) 
{ 
    char[] ch = str.ToCharArray(); 
    string reverse = ""; 
    for (int i = str.Length - 1; i > -1; i--) 
    { 
     reverse += ch[i]; 
    } 
    return reverse; 
} 

//using in-built method reverse of Array 
public static string getReverseUsingBulidingFunction(string str) 
{ 
    char[] s = str.ToCharArray(); 
    Array.Reverse(s); 
    return new string(s); 
} 


public static void Main(string[] args) 
{ 
    string str = "123"; 
    Console.WriteLine("The reverse string of '{0}' is: {1}",str,getReverse(str)); 
    Console.WriteLine("The reverse string of '{0}' is: {1}", str, getReverseUsingBulidingFunction(str)); 
    Console.ReadLine(); 
} 
-2
public static String reverseString(String str) 
{ 
    StringBuilder sb = new StringBuilder(); 

    for (int i = str.length() - 1; i >= 0; i--) 
    { 
     sb.append(str[i]); 
    } 

    return sb.toString(); 
} 
+1

Действительно ли это Java? – Pang

+0

Это действительно, но не эффективный способ сделать это !!! –

1
private static String reverse(String str) { 
    int i = 0; 
    int j = str.length()-1; 
    char []c = str.toCharArray(); 
    while(i <= j){ 
     char t = str.charAt(i); 
     c[i] = str.charAt(j); 
     c[j]=t; 
     i++; 
     j--; 
    } 
    return new String(c); 
} 
0

Использование нескольких потоков для замены элементов:

final char[] strArray = str.toCharArray(); 

    IntStream.range(0, str.length()/2).parallel().forEach(e -> { 
     final char tmp = strArray[e]; 
     strArray[e] = strArray[str.length() - e - 1]; 
     strArray[str.length() - e - 1] = tmp; 
    }); 
    return new String(strArray); 
Смежные вопросы