3 minute read

순차 탐색

  • 리스트 안에 있는 특정한 데이터를 찾기 위해 앞에서부터 데이터를 하나씩 차례대로 확인하는 방법으로, 말 그대로 ‘순차로 데이터를 탐색’한다는 의미이다.
  • 리스트 자료형에서 특정한 값을 가지는 원소의 개수를 세는 count() 메서드를 이용할 때도 내부에서는 순차 탐색이 수행된다.
  • 순차 탐색을 파이썬 코드로 작성하면 다음과 같다.
def seq_search(n, target, array):
    for i in range(n):
        if array[i] == target:
            return i + 1

print('생성할 원소 개수를 입력한 다음 한 칸 띄고 찾을 문자열을 입력하세요.')
input_data = input().split()
n = int(input_data[0]) # 원소의 개수
target = input_data[1] # 찾고자 하는 문자열

print('앞서 적은 원소 개수만큼 문자열을 입력하세요. 구분은 띄어쓰기 한 칸으로 합니다.')
array = input().split()

print(seq_search(n, target, array))

이진 탐색 : 반으로 쪼개면서 탐색하기

  • 우선, 이진 탐색은 데이터가 정렬되어 있어야만 사용할 수 있다.
  • 이진 탐색은 탐색하고자 하는 범위의 시작점, 끝점, 중간점을 정의한 뒤부터 시작된다.
  • 즉, 찾으려는 데이터와 중간점 위치에 있는 데이터를 반복적으로 비교해서 원하는 데이터를 찾는 게 이진 탐색 과정이다.

    아래의 코드를 보면서 이진탐색을 이해해보자

      # 이진 탐색 소스코드 구현(재귀함수)
      def binary_search(array, target, start, end):
          if start > end:
              return None
          mid = (start + end) // 2
          # 찾은 경우, 중간점 인덱스 반환
          if array[mid] == target:
              return mid
          # 중간점의 값보다 찾고자 하는 값이 적은 경우 왼쪽 확인
          elif array[mid] > target:
              return binary_search(array, target, start, mid - 1)
          # 중간점의 값보다 찾고자 하는 값이 큰 경우 오른쪽 확인
          elif array[mid] < target:
              return binary_search(array, target, mid + 1, end)
    
    
      # n(원소의 개수)과 target(찾고자 하는 문자열)을 입력받기
      n, target = list(map(int, input().split()))
      # 전체 원소 입력받기
      array = list(map(int, input().split()))
    
      # 이진 탐색 수행 결과 출력
      result = binary_search(array, target, 0, n - 1)
    
      if result == None:
          print('원소가 존재하지 않습니다')
      else:
          print(result + 1)
    
    • 이진 탐색은 코딩 테스트에서 단골로 나오는 문제이니, 여러번 연습해서 외우는 것이 좋다.
    • 또한, 이진 탐색의 원리는 다른 알고리즘에서도 폭넓게 적용되는 원리와 비슷하기도 하고, 다루어야 할 데이터의 개수가 많아 O(logN)의 시간 복잡도를 구현해야 하는 경우에 필수적으로 사용된다.

트리 자료구조

  • 트리 자료구조는 노드와 노드의 연결로 표현하며, 트리의 규칙은 아래와 같다.
  1. 트리는 부모 노드와 자식 노드의 관계로 표현된다
  2. 트리의 최상단 노드를 루트 노드라 한다
  3. 트리의 최하단 노드를 단말(leaf) 노드라 한다
  4. 트리에서 일부를 떼어내도 트리 구조이며, 떼어낸 트리를 ‘서브 트리’라고 한다
  5. 트리는 파일 시스템과 같이 계층적이고 정렬된 데이터를 다루기에 적합하다.
  • 따라서, 큰 데이터를 처리하기 위해서는 ‘트리 구조’로 설계하여 ‘이진 탐색’처럼 빠른 탐색 방식으로 탐색하는 것이 필요하다. 이러한 방식을 구현한 것이 ‘이진탐색트리’이다.

이진 탐색 트리

  • 이진 탐색 트리는 아래와 같은 규칙이 있다.
  1. 부모 노드보다 왼쪽 자식 노드가 작다
  2. 부모 노드보다 오른쪽 자식 노드가 크다
  • 이러한 규칙에 의해, 빠른 탐색이 가능하다.

빠르게 입력받기

  • 한 줄 입력받아 출력하는 소스코드

      import sys
      # 하나의 문자열 데이터 입력받기
      input_data = sys.stdin.readline().rstrip() 
      # rstrip은 right-strip으로, 엔터 키나 스페이스와 같이 불필요한 공백문자를 제거하기 위해 쓴다.
    
      print(input_data)
    

이진 탐색 사용하는 방법

다음 코드를 구현하라.

  • 입력
    1. 첫째 줄에 정수 N이 주어진다
    2. 둘째 줄에는 공백으로 구분해 N개의 정수가 주어진다.
    3. 셋째 줄에는 정수 M이 주어진다
    4. 넷째 줄에는 공백으로 구분해 M개의 정수가 주어진다.
  • 출력
    1. 첫째 줄에 공백으로 구분해, 넷째 줄에서 언급한 정수가 둘째 줄에 언급되었으면 yes, 아니면 no를 출력하라.
입력 예시
5
8 3 7 9 2
3
5 7 9
-----------------------
출력 예시
no yes yes

N이 엄청 클 경우를 대비해서 이진 탐색으로 구현해보자.

def binary_search(array, target, start, end):
    if start > end:
        return None
    mid = (start + end) // 2

    if array[mid] == target:
        return mid
    elif array[mid] > target:
        return binary_search(array, target, start, mid - 1)
    else:
        return binary_search(array, target, mid + 1, end)

n = int(input())
n_li = list(map(int, input().split()))

n_li.sort()

m = int(input())
m_li = list(map(int, input().split()))

for i in m_li:
    if binary_search(n_li, i, 0, n - 1) == None: print('no', end=' ')
    else: print('yes', end=' ')

  • 입력값이 적으면 ‘굳이 이렇게 짜야 하나?’라는 생각을 할 수도 있지만, 실제 코딩테스트에서는 어마어마한 입력값을 주기 때문에 이진 탐색을 구현하는 방법을 꼭 숙지해야한다.

Updated: