От автора: многие алгоритмы требуют обмена значениями между двумя переменными. Во время собеседования по кодированию вас могут спросить: «Как обменять значения между 2 переменными без временной переменной?».
Полезно знать несколько способов обмена значениями между переменными. В этом посте вы прочитаете о 4 способах обмена (2 их которые используют дополнительную память и 2 не используют).
1. Деструктурирующее присваивание
Деструктурирующее присваивание (функция ES2015 ) позволяет извлекать элементы массива в переменные. Например, следующий код деструктурирует массив:
let a; let b; [a, b] = [1, 2, 3]; a; // => 1 b; // => 2
[a, b] = [1, 2, 3] является деструктурирующим присваиванием, которое деструктурирует массив [1, 2, 3]. Переменной a присваивается первый элемент 1 из [1, 2, 3], соответственно, b присваивается второй элемент 2.
Зная, как деструктурировать массив, мы можем легко использовать это для обмена переменных. Давайте поменяем местами значения переменных a и b, используя деструктурирующее присваивание:
let a = 1; let b = 2; [a, b] = [b, a]; a; // => 2 b; // => 1
[a, b] = является деструктурирующим присваиванием, которое меняет местами значения переменных a и b. На первом этапе, справа от деструктурирования, создается временный массив (который оценивается, как [2, 1]).
После этого происходит деструктурирование временного массива: [a, b] = [2, 1]. Переменной a присваивается 2, и b присваивается 1. Обмен значений a и b был выполнен.
Мне нравится подход деструктурирования, потому что он короткий и выразительный: обмен значениями выполняется всего за один оператор. Работает с любым типом данных: числа, строки, логические значения, объекты.
Я рекомендую менять значения между переменными с использованием присваивания в большинстве случаев.
2. Временная переменная
Обмен значениями между переменными с использованием временной переменной является классическим. Как следует из названия, этот подход требует дополнительной временной переменной. Давайте поменяем значения между переменными a и b, используя временную переменную temp:
let a = 1; let b = 2; let temp; temp = a;a = b;b = temp; a; // => 2 b; // => 1
Temp — это временная переменная. На первом шаге temp присваивается значение a. Затем переменной a присваивается значение b. Наконец, переменной b присваивается значение temp (имеющей начальное значение a).
Обмен значениями с использованием временной переменной работает с любыми типами значений, такими как числа, строки, логические значения, объекты.
Недостатком этого подхода является необходимость в специальной временной переменной, плюс замена происходит в 3 оператора.
3. Сложение и вычитание
Вы можете обменять значения между переменными без использования дополнительной памяти (например, временного массива или переменной).
В следующем примере переменные a и b меняются значениями, используются арифметические операторы сложения + и вычитания -:
let a = 1; let b = 2; a = a + b;b = a - b;a = a - b; a; // => 2 b; // => 1
Изначально a равно 1, а b равно 2. Давайте посмотрим, как выполняется обмен:
a = a + b присваиваем a значение 1 + 2.
b = a – b присваивает b значение 1 + 2 — 2 = 1 (b сейчас равно 1).
a = a – b присваивает a значение 1 + 2 — 1 = 2 (a сейчас равно 2).
В конце у нас a равно 2, а b равно 1. Обмен между a и b выполнен. Хотя этот подход не использует временные переменные, он имеет значительные ограничения.
Во-первых, вы можете поменять местами только целые числа. Во-вторых, помните о лимите при выполнении сложения на первом шаге a = a + b (сумма должна быть меньше, чем Number.MAX_SAFE_INTEGER).
4. Побитовый оператор XOR
Оператор XOR оценивается как true, если операнды отличаются. Как напоминание, вот таблица значений для XOR:
┌─────┬─────┬───────┐ │ a │ b │ a ^ b │ ├─────┼─────┼───────┤ │ 0 │ 0 │ 0 │ │ 1 │ 1 │ 0 │ │ 0 │ 1 │ 1 │ │ 1 │ 0 │ 1 │ └─────┴─────┴───────┘
В JavaScript побитовый оператор XOR n1 ^ n2 выполняет операцию XOR для каждого бита чисел n1 и n2. Например, 5 ^ 7 оценивается 2:
1 0 1 (5 in binary) 1 1 1 (7 in binary) ----- 0 1 0 (5 ^ 7 = 2 in binary)
Побитовый оператор XOR имеет 2 интересных свойства:
n ^ n = 0: XOR, выполняемый для того же числа, дает 0
n ^ 0 = n: XOR, выполняемый для числа и ноля, дает то же число
Эти свойства XOR можно использовать для обмена значений между переменными. Давайте посмотрим, как обменять значения a и b:
let a = 1; let b = 2; a = a ^ b;b = a ^ b;a = a ^ b; a; // => 2 b; // => 1
Вот объяснение, почему работает обмен:
a = a ^ b
b = a ^ b, на основании 1. a заменяется на a ^ b. Таким образом b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a. Помним, что b сейчас равно a.
a = a ^ b, на основании 1. a замещено a ^ b, и на основе 2. b замещено a. Таким образом a = (a ^ b) ^ a = b ^ (a ^ a) = b ^ 0 = b. Переменная a становится b.
Если вы находите это объяснение сложным, можете его пропустить. Свойства побитового оператора XOR ( n ^ n = 0 и n ^ 0 = n), составленные в 3 присваиваниях, позволяют менять a и b.
Обмен значений между переменными с использованием побитового оператора XOR имеет ограничения: вы можете менять только целые числа.
Заключение
JavaScript предлагает множество способов обмена значениями между переменными с использованием дополнительной памяти и без.
Первый способ, который я рекомендую для ежедневного использования, — это замена переменных с помощью деструктурирующего присваивания [a, b] = . Это короткий и выразительный подход.
Второй способ — использовать временную переменную. Это хорошая альтернатива подходу деструктурирования.
Третий способ, использующий сложение и вычитание, не предполагает использования дополнительных переменных или памяти. Однако этот подход ограничен только целыми числами.
Точно так же, четвертый подход с использованием побитового оператора XOR не использует дополнительную память. Но, опять же, вы ограничены только целыми числами. Какой ваш предпочтительный способ обмена значений между переменными?
Автор: Dmitri Pavlutin
Источник: https://dmitripavlutin.com
Редакция: Команда webformyself.