JavaRush /Java блог /Random UA /Reverse string в Java: вчимося реверсувати рядки різними ...

Reverse string в Java: вчимося реверсувати рядки різними способами

Стаття з групи Random UA
Чи можна стати добрим програмістом без знання алгоритмів? Дуже спірно. Так, ви зможете знайти роботу в наших аутсорс-компаніях, адже в них на співбесідах ставлять питання переважно за технологіями. Reverse string в Java: вчимося реверсувати рядки різними способами - 1Але чи будете ви хорошим фахівцем, якщо ваші рішення будуть наповнені мабоцями? Якщо ви захочете перейти до більш серйозної зарубіжної компанії, зіткнетеся з співбесідами, орієнтованими насамперед на алгоритми. Так чи інакше, варто взяти на озброєння базові алгоритми, адже алгоритм — друг програміста . Сьогодні ми торкнемося однієї з таких тем і обговоримо способи реверсування рядка. Тут усе просто. Реверсування рядка- Це отримання рядка задом наперед. Наприклад: JavaRush forever -> reverof hsuRavaJ Отже, як можна перевернути рядок в Java?

1. StringBuilder/StringBuffer

Найбанальніший і найпростіший спосіб — використовувати StringBuilder/StringBuffer :
public static String reverseString(String str) {
  return new StringBuilder(str).reverse().toString();
}
Найкраще рішення = найпростіше. При питанні про те, як перевернути рядок (reverse string в Java) це перше, що має спадати на думку. Але ми раніше говорабо про алгоритми, чи не так? Давайте поглянемо на рішення не "з коробки".

2. Рішення з масивом

public static String reverseString(String str) {
  char[] array = str.toCharArray();
  String result = "";
  for (int i = array.length - 1; i >= 0; i--) {
     result = result + array[i];
  }
  return result;
}
Переводимо наш рядок у масив за допомогою методу toCharArray . Пробіжимося циклом for по даному масиву з його кінця, принагідно додаючи символи в результуючий рядок.

3. Рішення з charAt

public static String reverseString(String str) {
  String result = "";
  for (int i = 0; i < str.length(); i++) {
     result = str.charAt(i) + result;
  }
  return result;
}
В даному випадку нам навіть рядок на масив розбивати не доведеться, тому що кожен символ ми витягуємо за допомогою методу класу String - charAt (цикл for, знову ж таки, у нас зворотний, що дозволяє нам брати символи послідовно задом-наперед).

4. Рішення зі Stack

Клас Stack давно ніхто не використовує, і він вважається застарілим, але для ознайомлення буде корисно поглянути на рішення з його використанням:
public static String reverseString(String str) {
  Stack<Character> stack = new Stack<>();
  String result = "";
  for (Character character : str.toCharArray()) {
     stack.add(character);
  }
  while (!stack.isEmpty()) {
     result = result + stack.pop();
  }
  return result;
}
Тут знову ж таки використовуємо toCharArray для розбивки рядка на масив і кладемо все це в наш Stack з дженерик типом Character . Далі починаємо брати елементи з верхівки стека. Через особливості стека як структури LIFO - L ast I n F irst O ut (перший увійшов, останній вийшов), елементи будуть братися задом-наперед і слідство збережуться в результуючий рядок.

5. Рішення рекурсією

Майже кожне завдання на алгоритми можна вирішити за допомогою рекурсії. І тут ми теж не обійдемося без неї. Або навіть без них. Адже сьогодні ми розглянемо навіть не один спосіб розв'язання рекурсією, а кілька.
  • спосіб перший

    public static String reverseString(String str) {
      String rightStr;
      String leftStr;
      int length = str.length();
    
      if (length <= 1) {
         return str;
      }
    
      leftStr = str.substring(0, length / 2);
      rightStr = str.substring(length / 2, length);
    
      return reverseString(rightStr) + reverseString(leftStr);
    }

    Змінні rightStr і leftStr ми використовуємо для розбивки рядка, що прийшов, на дві рівні частини. Далі за допомогою такої розбивки ми дробимо рядок на найменші частини (1 символ). Після рекурсія починає згортатися, повертаючи символи в протилежному порядку (ті, що були праворуч - поставабо ліворуч; ті, що були ліворуч - праворуч)

    Не можна забувати, кожна рекурсія — це багаторазовий виклик методу, і як наслідок — чималі витрати ресурсів. Ну а якщо ми говоримо про рекурсію з недосяжною умовою виходу, то це шлях у нескінченність і до StackOverflowError.

  • спосіб другий

    Тут нам знадобиться додатковий аргумент у методі – index.

    При запуску цього методу він задається довжиною рядка -1:

    String str = "JavaRush forever";
    System.out.println(reverseString(str, str.length()-1));

    І сам метод:

    public static String reverseString(String str, int index) {
      if(index == 0){
         return str.charAt(0) + "";
      }
    
      char letter = str.charAt(index);
      return letter + reverseString(str, index-1);
    }

    Індекс у нас є індикатором того, який елемент рядка ми будемо використовувати зараз (а елементи ми будемо використовувати з кінця).

    Тому задаємо умови виходу при досягненні індексу першого елемента.

  • Складаємо значення отриманого за допомогою індексу letter із результатом попереднього виконання методу та повертаємо результат.

  • спосіб третій

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

    Даний спосіб по суті є найпростішим із рекурсивних. А як ми пам'ятаємо, просте = найкраще.

    Під час кожного запуску ми задаємо той самий рядок, але без першого елемента. При досягненні умови виходу (коли ми залишимося один символ) рекурсія починає згортатися, і до кожного наступного результату додаватиметься попередній невикористаний символ.

6. За допомогою XOR

XOR - логічна побітова операція. У разі двох змінних результат виконання операції дійсний тоді й лише тоді, коли один із аргументів дійсний, а інший — хибний.
A B Y
0 0 0
0 1 1
1 0 1
1 1 0
Докладніше про побітові операції можна почитати в цій статті . Наступне рішення спиратиметься на те, що:

(A XOR B) XOR B = A
(A XOR B) XOR A = B
Як буде виглядати метод:
public static String reverseString(String str) {
  char[] arr = str.toCharArray();
  int low = 0;
  int high = arr.length - 1;
  String result = "";
  while (low < high) {
     arr[low] = (char) (arr[low] ^ arr[high]);
     arr[high] = (char) (arr[low] ^ arr[high]);
     arr[low] = (char) (arr[low] ^ arr[high]);
     low++;
     high--;
  }
  for (int i = 0; i < arr.length; i++) {
     result = result + arr[i];
  }
  return result;
}
Давайте розбиратися з тим, що відбувається. Ми створюємо масив із вхідного рядка. Створюємо дві змінні, low і high , що зберігають індекси для проходу масивом. Відповідно, один рухатиметься з початку в кінець - йому задаємо значення 0, другий - з кінця в початок, йому задаємо arr.length - 1 . Заходимо в цикл, який відтворюватиметься, поки індекс high більше, ніж low . Тут починає відбуватися найцікавіше використання виключає АБО. Давайте розберемо з прикладу x і y . Припустимо, що arr[high] = 'x'; В цей час arr[high] = 'n'; Двійковий код — 1 1 0 1 1 1 0 Що ми матимемо в XOR операціях у циклі:
  1. arr [low] = (char) (arr [low] ^ arr [high]);

    
    arr[low] = 1 1 0 1 1 1 0
    arr[high] =1 1 1 1 0 0 0
    arr[low] = 0 0 1 0 1 1 0
  2. arr [high] = (char) (arr [low] ^ arr [high]);

    
    arr[low] =  0 0 1 0 1 1 0
    arr[high] = 1 1 1 1 0 0 0
    arr[high] = 1 1 0 1 1 1 0
  3. arr [low] = (char) (arr [low] ^ arr [high]);

    
    arr[low] =  0 0 1 0 1 1 0
    arr[high] = 1 1 0 1 1 1 0
    arr[low] =  1 1 1 1 0 0 0
У результаті завдяки цим операціям ми змінабо місцями значення двох осередків масиву. arr[high] віддалений від кінця масиву на стільки ж елементів, на скільки arr[low] віддалений від початку. Тому ми просто змінюємо елементи із цими індексами місцями. Наприклад, при першому виконанні у реченні "JavaRush forever" J і r поміняються місцями, при другому - a і e і т. д. Якщо у нас непарна кількість символів, то при досягненні елемента, що знаходиться посередині, нас викине з циклу (т .до середній елемент змінювати і не потрібно). Якщо парне – нас викине після обробки всіх елементів. Ну а потім ми заходимо у звичайний цикл і будуємо рядок із елементів масиву.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ