[자바 기본 지식] 몇 가지 일반적인 비트 작업

1. 홀수와 짝수 결정

숫자 n이 이진수로 표현되면 마지막 이진수가 1인지 0인지 확인하기 만하면됩니다. 1이면 홀수를 나타내고, 그렇지 않으면 짝수입니다. 코드 쇼 :

if(n & 1 == 1){
    // n是奇数
}

2. 두 개의 숫자를 교환

x = x ^ y; // (1)
y = x ^ y; // (2)
x = x ^ y; // (3)

우리 모두는 두 개의 동일한 숫자의 배타적 OR의 결과가 0, 즉 n ^ n = 0이고 0으로 XOR 처리 된 후, 즉 n ^ 0 = n이 자신과 동일하다는 것을 모두 알고 있습니다.

따라서 (1)의 x를 (2)의 x로 대체하면 다음과 같습니다 .y = x ^ y = (x ^ y) ^ y = x ^ (y ^ y) = x ^ 0 = x 값은 다음과 같이 할당됩니다. 와이.

(3)의 경우 파생은 다음과 같습니다. x = x ^ y = (x ^ y) ^ x = (x ^ x) ^ y = 0 ^ y = y, 따라서 y의 값은 x에 할당됩니다.

배타적 OR 연산은 교환 및 연관 연산 법칙을 지원합니다.

3. 반복되지 않는 숫자 찾기

정수 데이터 세트를 제공합니다.이 데이터 중에서 숫자 중 하나는 한 번만 나타나고 다른 숫자는 두 번 나타납니다. 숫자를 찾을 수 있습니다.

많은 사람들이이 질문을 저장하기 위해 해시 테이블을 사용할 수 있는데, 저장 될 때마다 특정 수의 발생 횟수가 기록되고 마지막으로 해시 테이블을 탐색하여 한 번만 발생하는 발생 횟수를 찾습니다. 이 방법의 시간 복잡도는 O (n)이고 공간 복잡도도 O (n)입니다.

사실,이 문제는 비트 산술 일 수도 있습니다. 모든 정수를 XOR 할 수 있습니다. 두 개의 동일한 숫자의 XOR 결과가 0이므로 0 인 숫자의 XOR 결과 자체가됩니다. OR는 한 ​​번만 나타나는 숫자입니다. 예를 들어,이 데이터 세트는 1, 2, 3, 4, 5, 1, 2, 3, 4입니다. 다섯 개는 한 번만 나타나고 나머지는 두 번 나타납니다. 모두 XOR. 결과는 다음과 같습니다.

1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 1 ^ 2 ^ 3 ^ 4 = (1 ^ 1) ^ (2 ^ 2) ^ (3 ^ 3) ^ (4 ^ 4) ^ 5 = 0 ^ 0 ^ 0 ^ 0 ^ 5 = 5

코드 쇼 :

int find(int[] nums){
    int tmp = nums[0];
    for(int i = 1;i < nums.length; i++)
        tmp ^= arr[i];
    
    return tmp;
}

4, 2의 n 제곱

2의 n 제곱까지 풀고 시스템과 함께 제공되는 pow 함수를 사용할 수 없습니다.

이 질문을 보는 많은 사람들은 n 2s를 곱하는 것으로 충분하다고 생각할 수 있습니다. 이렇게하면 시간 복잡도는 O (n)입니다. 그렇다면 비트 연산으로 어떻게할까요?

예를 들어, n = 13이고 n의 이진수는 1101이고 2의 13 제곱은 2 ^ 1101 = 2 ^ 0001 * 2 ^ 0100 * 2 ^ 1000으로 나눌 수 있습니다. & 1과 >> 1을 통해 1101 비트를 비트 단위로 읽을 수 있습니다. 1이면이 비트가 나타내는 승수가 최종 결과에 곱해집니다. 최종 코드는 다음과 같습니다.

int pow(int n) {
    int sum = 1;
    int tmp = 2;
    while(n != 0) {
        if(n & 1 == 1)
            sum *= tmp;
        
        temp *= temp;
        n >>= 1;
    }
    return sum;
}

5. N보다 크지 않은 2 지수의 최대 거듭 제곱 찾기

예를 들어 N = 19이면 이진수로의 변환은 00010011입니다 (편의상 8 비트 이진수를 사용하여 나타냄). 그런 다음 우리가 찾고있는 숫자는 바이너리에서 가장 왼쪽에있는 1 을 유지 하고 이후의 모든 1이 0이되는 것 입니다. 즉, 목표 번호는 00010000입니다. 이 번호를 얻는 방법은 무엇입니까? 해당 솔루션은 다음과 같습니다.

1. 가장 왼쪽에있는 1을 찾아 오른쪽에있는 모든 0을 1로 변경합니다.

2. 얻은 값에 1을 더하면 00100000, 즉 000111111 + 1 = 00100000을 얻을 수 있습니다.

3. 획득 한 00100000을 오른쪽으로 한 자리 이동하여 00010000, 즉 00100000 >> 1 = 00010000을 얻습니다.

그래서 질문은, 첫 번째 단계에서 가장 왼쪽에있는 1 다음에 0을 어떻게 얻을 수 있습니까?

코드 쇼 :

n |= n >> 1;
n |= n >> 2;
n |= n >> 4;

n을 오른쪽으로 이동하고 OR 연산을 수행하여 얻을 수 있습니다. 설명하겠습니다. 가장 왼쪽 1이 이진수의 k 번째 위치 (왼쪽에서 오른쪽으로 계산)에 있다고 가정하고 n을 오른쪽으로 한 자리 이동 한 후 얻은 결과도 1이어야하며 n의 결과와 오른쪽 시프트에 대해 OR 연산을 수행 한 다음 얻은 결과의 k 번째 및 k + 1 자리는 1이어야합니다. 같은 방식으로 n을 오른쪽으로 다시 시프트 2 비트로 얻은 결과의 k 번째 자리 +2와 k + 3 자리는 1이어야하며 다시 OR 연산을 수행하면 kth, k + 1, k + 2, k + 3을 얻을 수 있습니다. 모두 1, 등등 ...

최종 코드는 다음과 같습니다.

int findN(int n){
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8 // 整型一般是 32 位,上面我是假设 8 位。
    return (n + 1) >> 1;
}

이 접근 방식의 시간 복잡도는 대략 O (1)입니다.

추천

출처blog.csdn.net/qq_41893274/article/details/112759540