Все мы знаем, как работает двусвязный список и что он неэффективно использует память. Но что, если я скажу, что двусвязный список можно реализовать с той же пространственной сложностью, что и односвязный список, с помощью какой-нибудь простой побитовой операции? Да, вы не ослышались! Битовые манипуляции — это то, что пригодится, когда нам нужно что-то оптимизировать. Здесь вместо сохранения исходного адреса памяти мы сохраняем побитовое XOR предыдущего и следующего узла, тем самым уменьшая пространство одного адреса в каждом узле.
Что такое битовая манипуляция?
Битовые манипуляции – это метод манипулирования данными на уровне битов с помощью побитовых операторов. Поскольку компьютеры обычно хранят целые числа в форме дополнения до двух, работа напрямую с битами делает операцию быстрее, чем обычные арифметические операции. Как только вы освоите их, это можно будет применять к огромному количеству проблем, однако всегда следите за тем, чтобы не ставить под угрозу читабельность кода, применяя эти методы.
В этой статье мы рассмотрим основы побитовых операций и некоторые классные фрагменты кода с использованием битов, которые могут сделать наш код элегантнее и быстрее.
Побитовые операторы
Побитовые операторы позволяют вам манипулировать отдельными необработанными битами данных в структуре данных. Давайте посмотрим на различные типы побитовых операторов.
Побитовое И.Операция побитовое И (&) выполняет логическое И битов в каждой позиции числа в двоичной форме, то есть дает 1, только если оба биты равны 1.
Например: 101 и 111 равны 101, так как оба бита становятся единицей только в единицах и сотнях.
Побитовое ИЛИ: оператор побитового ИЛИ (|) работает так же, как побитовое И. Разница в том, что только один из битов должен быть равен 1, чтобы бит этой позиции на выходе был равен 1.
Например: 101 | 111 равно 111, так как оба бита никогда не становились равными нулю вместе.
Побитовое НЕ:в отличие от других побитовых операторов побитовое НЕ (~) принимает только один операнд и инвертирует все его биты, то есть дополнение числа до единицы.
Например: ~101 = 010.
Побитовое исключающее ИЛИ.оператор побитового исключающего ИЛИ или «оператор исключающего ИЛИ» (^) сравнивает биты двух чисел один за другим и дает 1, только если оба бита различны.
Например: 101 ^ 111 равно 010, так как оба бита отличаются только разрядом десятков.
Сдвиг влево: оператор сдвига влево (‹‹) — это бинарный оператор, который сдвигает биты числа влево и добавляет ноль в конце. Интересно, что это эквивалентно умножению числа на степень двойки.
Например: 110 ‹‹ 1 = 1100
То есть 6‹‹1 равно 12, умножение на 2.
Сдвиг вправо: Оператор сдвига вправо(››) — это двоичный оператор, который сдвигает биты числа вправо. Если внимательно присмотреться, это эквивалентно делению числа на степени двойки.
Например: 110 ›› 1 = 11
То есть 6 ›› 1 равно 3, деление на 2.
Трюки с битами
Давайте посмотрим на несколько замечательных битовых хаков, использующих эти концепции.
1. Чтобы получить K-й бит справа
int getBit(int num, int k) { return num & (1<< (i-1)); }
2. УСТАНОВИТЕ K-й бит справа
void setBit(int &n, int k) { n = n|(1<< (k-1)); }
3. Переключение K-го бита справа в числе
void toggle(int &n, int k) { n = n ^ (1 << k - 1); }
4. Проверить, равны ли два числа
bool equal(int a, int b) { if (a ^ b == 0) return True; return False; }
5. Проверить, является ли число степенью двойки
bool isPowerof2(int number) { if (number & (number-1) ==0) return True; return False; }
6. Проверить, является ли число четным или нечетным
bool isEven (int number) { if(number & 1 == 0) return True; return False; }
7. Найти наибольший общий делитель двух чисел
int gcd(int a, int b){ while(b){ b^=a^=b^=a%=b; } return a; }
8. Поменять местами два целых числа a и b
void swap(int &a, int &b){ a^=b; b^=a; a^=b; }
9. Минимум x и y
int minimum(int x, int y){ int min = (y ^ (x ^ y) & - (x < y)); return min; }
10. Максимум x и y
int maximum(int x, int y){ int max= (x ^ (x ^ y) & - (x < y)); return max; }
Практикуйте проблемные ссылки
Поменять местами все нечетные и четные биты
Максимум последовательных единиц III
Следующий больший элемент III
Минимальное количество переворотов, чтобы сделать a OR b равным c
Преобразование вещественное число (от 0 до 1) в двоичную строку
Ресурсы
Связанный список XOR — двусвязный список с эффективным использованием памяти | Набор 1
Хаки с битами
Трюк XOR
Побитовые операции 2 — popcount & bitsets
Спасибо за чтение этой статьи!