C

[57] C 백준 10815번 숫자 카드 문제 (이진탐색, 이분탐색, 정렬)

엽동이 2022. 8. 31. 18:11

문제

숫자 카드는 정수 하나가 적혀져 있는 카드이다. 상근이는 숫자 카드 N개를 가지고 있다. 정수 M개가 주어졌을 때, 이 수가 적혀있는 숫자 카드를 상근이가 가지고 있는지 아닌지를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다. 두 숫자 카드에 같은 수가 적혀있는 경우는 없다.

셋째 줄에는 M(1 ≤ M ≤ 500,000)이 주어진다. 넷째 줄에는 상근이가 가지고 있는 숫자 카드인지 아닌지를 구해야 할 M개의 정수가 주어지며, 이 수는 공백으로 구분되어져 있다. 이 수도 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다

출력

첫째 줄에 입력으로 주어진 M개의 수에 대해서, 각 수가 적힌 숫자 카드를 상근이가 가지고 있으면 1을, 아니면 0을 공백으로 구분해 출력한다.

예제 입력 1 

5
6 3 2 10 -10
8
10 9 -5 2 3 4 5 -10

예제 출력 1 

1 0 0 1 1 0 0 1

 

 

 

<풀이>

이 문제를 처음부터 하나하나 비교해 가는 순차 검색 알고리즘을 통해 풀려고 하였지만 시간초과가 발생할 것 같아 다른 탐색법을 찾아보았다.

이분탐색 혹은 이진탐색이라는 탐색법을 알게 되었는데 이진탐색이란 이렇다.

https://m42-orion.tistory.com/69

 

[C++ Algorithm] 이분 탐색(Binary Search)

⭐️ 이분 탐색(Binary search)이란? - 정렬된 리스트(배열)에서 원하는 값(target)의 존재 여부(존재 위치)를 찾는 알고리즘. - 반드시 리스트(배열)를 정렬해서 사용해야 한다는 단점이 있다. - 탐색할

m42-orion.tistory.com

위 블로그 참고

 

이진탐색

이진탐색은 정렬되어있는 리스트에서 탐색범위를 절반씩 줄여가면서 원하는 수를 찾는 방법이다.

 

출처:&nbsp;https://dev-overload.tistory.com/12

이진탐색의 기본 조건은 배열이 무조건 오름차순으로 정렬되어 있어야 한다는 것이다.

 

1(start) 4 7 10 11 13 14(end)의 수열이 있고 타겟값이 7이라고 가정해보자

 

왼쪽부터 순서대로 arr[0], arr[1], arr[2] ... arr[6]이다.

 

start(low)의 위치는 맨 처음 arr[0]이고 end(high)는 맨 뒤 arr[6]로 시작한다.

 

mid = (0 + 6) / 2 이므로 3

 

1 4 7 10 11 13 14

 

3번째 값인 10이 mid의 위치가 된다.

타겟값인 7(2번)이 mid의 값보다 작으므로 end = mid - 1로  재설정 된다.

 

mid = (0 + 2) / 2 = 1 

 

1 7 10 11 13 14

 

타겟값인 7(2번)이 mid의 값보다 크므로 start = mid + 1로 재설정 된다. 

 

mid = (2 + 2) / 2 = 2

 

1 4 7 10 11 13 14

 

타겟값인 7(2번)과 mid의 값이 같으므로 타겟값을 찾아내게 되었다.

 

따라서 arr[mid] > target인 경우 end가 mid - 1이 되고,

arr[mid] < target인 경우 start가 mid + 1이 된다.

 

이 문제에서는 (상근이가 가지고 있는지 확인해야 하는 카드)의 배열의 수를 하나씩 (상근이가 가지고 있는 카드)의 오름차순 정렬된 배열에 이진탐색 시켜 카드가 있다면 1, 없다면 0을 출력하게 만들면 된다.

 

<제출한 코드>

#include <stdio.h>
#include <stdlib.h>

int compare(const void *a, const void *b)    // 오름차순 비교 함수 구현(qsort)
{
    int num1 = *(int *)a;    // void 포인터를 int 포인터로 변환한 뒤 역참조하여 값을 가져옴
    int num2 = *(int *)b;    // void 포인터를 int 포인터로 변환한 뒤 역참조하여 값을 가져옴

    if (num1 < num2)    // a가 b보다 작을 때는
        return -1;      // -1 반환
    
    if (num1 > num2)    // a가 b보다 클 때는
        return 1;       // 1 반환
    
    return 0;    // a와 b가 같을 때는 0 반환
}

int binarySearch(int arr[], int size, int key){ // 이진탐색 함수 (배열, 배열 크기, 타겟값)
    int high, low, mid;
    high = size - 1;
    low = 0;

    while (high >= low){ // high가 low보다 작아지면 값이 없다.
        mid = (high + low) / 2;
        if (key < arr[mid]) high = mid - 1; //key값이 mid값보다 작으면 high = mid - 1이 되어야함.
        else if (key > arr[mid]) low = mid + 1; // key값이 mid값보다 크면 low = mid +1이 되어야함.
        else if (key == arr[mid]) return 1; // key == mid값이면 그 수가 찾던 수이다.
    }
    return 0; // 만족하는 값이 없을 때
}

int main(void){
    int N, M, result;
    int sanggeun[500000];
    int cardCheck[500000];

    scanf("%d", &N);
    for (int i = 0; i < N; i++){
        scanf("%d", &sanggeun[i]);
    }
    scanf("%d", &M);
    for (int j = 0; j < M; j++){
        scanf("%d", &cardCheck[j]);
    }

    qsort(sanggeun, N, sizeof(int), compare); // 이진탐색을 위한 오름차순 정렬

    for (int i = 0; i < M; i++){
        result = binarySearch(sanggeun, N, cardCheck[i]);// 이진탐색 함수
        printf("%d ", result);
    }

    return 0;
}

오름차순 정렬 방법은 <stdlib.h>에 있는 qsort()퀵 정렬 함수를 사용하였다.

퀵 정렬에 대한 내용은 https://dojang.io/mod/page/view.php?id=638

 

C 언어 코딩 도장: 73.2 퀵 정렬 함수 사용하기

이번에는 퀵 정렬 함수를 사용해보겠습니다. 퀵 정렬 함수에는 정렬할 배열 또는 메모리의 주소, 요소 개수, 요소 크기, 비교 함수를 넣어줍니다(stdlib.h 헤더 파일에 선언되어 있습니다). qsort(정

dojang.io