꿈돌이랜드

LowerBound, UpperBound 본문

Programming/Algorithm

LowerBound, UpperBound

loinsir 2024. 1. 12. 21:09
반응형

LowerBound와 UpperBound는 모두 이진탐색(BinarySearch)을 응용한 알고리즘이다.

일반적으로 이진탐색은 찾고자 하는 값이 정확히 없다면 탐색을 실패한다.

하지만 LowerBound와 UpperBound는 범위에 초점이 맞춰져 있어, 같은 원소가 여러개 있더라도 사용할 수 있다.

개념은 알지만 구현 시 자주 헷갈리므로 정리해보자.

LowerBound

동작방식

LowerBound는 찾고자 하는 값 이상의 값이 처음으로 나타나는 인덱스이다.

LowerBound의 동작 방식은 다음과 같다.

  1. 초기에 left는 배열의 시작 위치, right는 배열의 길이로 세팅한다
    • right가 배열의 끝 위치가 아니라, 길이로 처음에 셋팅한다는 점에 유의하자
  1. 중간 값(mid)를 가져온다.
  1. 중간 값과 검색 값을 반복적으로 비교한다.
    • 중간 값이 검색 값보다 작으면, left 값을 mid + 1로 셋팅한다
    • 중간 값이 검색 값보다 크거나 같으면, right 값을 mid 로 셋팅한다.
  1. left ≥ right가 되기 전 까지 2, 3번을 반복한다.

예를 들어 다음과 같은 배열이 있다고 하자.

arr = [1, 2, 2, 3, 3, 3, 5, 6, 8]

이 배열에서 검색 값을 3으로 하고, 3 이상의 값이 처음으로 나오는 lower bound를 찾는다고 해보자

  • 초기에 left = 0, right = 9가 된다.
    • mid = (left + right) / 2 = 4 가 된다.
  • 검색 값 3이 arr[mid] 값인 3과 같으므로, right = mid = 4 가 된다.
  • mid = (left + right) / 2 = (0 + 4) / 2 = 2 가 된다.
  • arr[mid] = 2 이므로 찾고자 하는 값 3보다 작다. 따라서 left = mid + 1이 되어, left = 3이 된다.
  • mid = (left + right) / 2 = (3 + 4) / 2 = 3 이 된다.
  • arr[mid] 값이 찾고자 하는 값 3과 같으므로 right = mid = 3이 된다
  • left값이 right값과 같기 때문에 루프가 종료되고, right값인 3이 lowerbound가 된다.

구현

이를 정리하면 다음과 같은 코드로 구현할 수 있다.

func lowerBound(_ arr: [Int], _ k: Int) -> Int {
  var left = 0, right = arr.count
  while left < right {
    let mid = (left + right) / 2
    if arr[mid] < k {
      left = mid + 1
    } else {
      right = mid
    }
  }
  return right
}

UpperBound

UpperBound는 찾고자 하는 값 초과의 값이 처음으로 나타나는 인덱스를 말한다.

동작방식

UpperBound의 동작 방식은 다음과 같다.

  1. 초기에 left는 배열의 시작 위치, right는 배열의 길이로 세팅한다
    • 이는 LowerBound와 마찬가지 이다.
  1. 중간 값(mid)를 가져온다.
  1. 중간 값과 검색 값을 반복적으로 비교한다.
    • 중간 값이 검색 값보다 작거나 같으면, left 값을 mid + 1로 셋팅한다
    • 중간 값이 검색 값보다 크면, right 값을 mid 로 셋팅한다.
  1. left ≥ right가 되기 전 까지 2, 3번을 반복한다.

lowerBound 예시와 마찬가지로 다음과 같은 배열이 있다고 하자.

arr = [1, 2, 2, 3, 3, 3, 5, 6, 8]

이 배열에서 검색 값을 3으로 하고, 3을 초과하는 값이 처음으로 나오는 upper bound를 찾는다고 해보자

  • left = 0, right = 9, mid = (left + right) / 2 = 4로 셋팅된다
  • 검색 값 3이 arr[mid]값과 같기 때문에 left = mid + 1 = 5가 된다
  • mid 값을 구하고 검색 값 3과 비교한다
    • mid = (left + right) / 2 = (5 + 9) / 2 = 7
  • 검색 값 3이 arr[mid] 값인 6보다 작으므로, right = mid = 7로 셋팅한다.
  • mid = (left + right) / 2 = (5 + 7) / 2 = 6이 되고 검색 값 3과 비교한다.
  • arr[mid] 값이 검색 값 3보다 크므로 right = mid = 6으로 셋팅한다.
  • mid = (left + right) / 2 = (5 + 6) / 2 = 5 가 되고, 검색 값 3과 비교한다.
  • arr[mid] 값이 검색 값 3과 같으므로, left = mid + 1 = 6 으로 셋팅된다.
  • left와 right값이 같아져서 반복이 종료되고, right값인 6이 upper bound가 된다.

구현

lower bound와 코드는 유사하나, 조건식에서의 차이가 있다.

lower bound는 현재 배열의 mid 인덱스 값이 검색 값보다 작을때 left = mid + 1, 크거나 같을 때 right = mid 로 셋팅하지만,

upper bound는 현재 배열의 mid 인덱스 값이 검색 값보다 작거나 같을 때 left = mid + 1, 클 때 right = mid로 셋팅한다

func upperBound(_ arr: [Int], _ k: Int) -> Int {
  var left = 0, right = arr.count
  while left < right {
    let mid = (left + right) / 2
    if arr[mid] <= k { // lower bound와 다른 부분은 이곳에 등호가 들어간다는 것
      left = mid + 1
    } else {
      right = mid
    }
  }
  return right
}

참조

[알고리즘] Lower bound, Upper bound
Lower bound선형구조의 부분탐색법인 이분탐색은 찾고자 하는 값이 없으면(맨 마지막 값) 탐색 실패! 하지만, lower bound는 찾고자 하는 값 이상이 처음 나타나는 위치입니다. lower bound의 경우에는 같은 원소가 여러개 있더라도 상관없습니다. 찾고자 하는 값 이상의 값이 처음 나타나는 위치를 찾아내기 위해, 이분탐색 방법에서의 조건을 조금 변경하면 됩니다. [문제] n개로 이루어진 정수 집합에서 원하는 수 k이상인 수가 처음으로 등장하는 위치를 찾으시오. 단, 입력되는 집합은 오름차순으로 정렬되어 있으며(이분 탐색 가능), 같은 수가 여러개 존재할 수 있다. 입력) 첫 줄에 한 정수 n이 입력된다. 둘째 줄에 n개의 정수가 공백으로 구분되어 입력된다. 셋째 줄에는 찾고자 하는 값 k..
https://12bme.tistory.com/120
Lower bound & Upper bound 개념 및 구현
목차 Lower bound & Upper bound 개념 및 구현 Lower bound와 Upper bound는 경곗값을 찾는 알고리즘입니다. Lower bound와 Upper bound는 이진 탐색을 기반으로 하기 때문에 데이터가 정렬되어 있어야 합니다. 이진 탐색을 기반으로 하기 때문에 두 알고리즘의 시간 복잡도는 O(log n) 입니다. (n: 배열 길이) Lower bound Lower bound는 특정 값의 시작 위치를 찾는 알고리즘입니다. 동작 방식 Lower bound의 동작 방식은 다음과 같습니다. 초기에 left는 배열의 시작 위치로 right는 배열의 길이로 셋팅합니다. 배열의 중간 값(mid)을 가져옵니다. 중간 값과 검색 값을 비교합니다. 중간 값이 검색 값보다 작다면 left 값을..
https://yoongrammer.tistory.com/105

Uploaded by N2T

반응형