#해시

해시 테이블과 집합으로 문자열·검색 문제를 빠르게 해결하는 방법을 정리한다. 현재 9개의 풀이가 있다.

해시 테이블 학습 가이드

필요한 주제만 펼쳐서 해시 테이블의 구조와 빠른 조회 패턴을 익힌다.

해시 테이블 기본 개념

해시 테이블은 키를 해시 함수로 변환해 저장 위치를 정하고, 나중에 같은 키로 해당 위치를 빠르게 찾아가는 자료구조다.

apple과 book 키가 해시 함수를 거쳐 서로 다른 버킷에 매핑되는 그림
키를 버킷 위치로 변환하면 전체 자료를 순회하지 않고 필요한 값을 찾을 수 있다.
  • 키: 값을 찾기 위한 식별자
  • 값: 키에 연결해 저장하는 데이터
  • 해시 함수: 키를 배열의 인덱스로 변환하는 함수
  • 버킷: 변환된 인덱스에 해당하는 저장 공간

Python에서는 `dict`가 키와 값을 저장하고, `set`이 원소의 존재 여부를 빠르게 확인하는 대표적인 해시 기반 자료구조다.

충돌과 키 비교

서로 다른 키가 같은 버킷 인덱스로 변환되는 것을 충돌이라고 한다. 충돌이 발생해도 실제 키를 다시 비교해 원하는 항목을 구분해야 한다.

cat과 act가 같은 버킷에 저장된 뒤 실제 키를 비교해 값을 구분하는 그림
같은 버킷에 여러 키가 들어갈 수 있으므로 해시 값만 같다고 같은 키로 처리하면 안 된다.

Python의 `dict`와 `set`은 충돌 처리를 내부에서 담당한다. 문제 풀이에서는 해시 함수 자체를 구현하기보다, 서로 다른 상태가 같은 키가 되지 않도록 키를 설계하는 것이 중요하다.

  • 문자열 빈도는 정렬된 문자열이나 빈도 튜플로 표현
  • 여러 값을 묶을 때는 충돌하지 않는 튜플을 키로 사용
  • 리스트처럼 변경 가능한 객체는 직접 키로 사용하지 않음
자주 사용하는 해시 패턴
해시 기반 set의 존재 확인, dict의 빈도 계산, 키와 값 매핑 세 가지 패턴을 보여주는 그림
문제의 요구사항에 따라 존재 여부, 빈도, 키와 값의 관계를 해시에 저장한다.
문제 조건자료구조저장할 상태
이미 본 값인지 확인`set`값의 존재 여부
값별 등장 횟수 계산`dict``값 -> 개수`
두 값 사이의 관계 저장`dict``키 -> 연결된 값`
빠른 보완값 조회`dict``필요한 값 -> 인덱스`
같은 특징을 가진 그룹화`dict``특징 키 -> 목록`
시간 복잡도와 사용 기준

해시 테이블의 삽입, 조회, 삭제는 평균적으로 `O(1)`이다. 다만 충돌이 과도하거나 해시 키를 만드는 비용이 크면 실제 성능이 달라질 수 있다.

  • 순서가 필요하지 않고 빠른 존재 확인이 필요하면 `set`
  • 키로 값을 찾아야 하면 `dict`
  • 입력 순서와 중복을 보존해야 하면 값을 리스트에 누적
  • 키는 문제의 상태를 빠짐없이 구분하는 불변 값으로 설계

정렬이 필요한 문제나 값의 순서 관계를 계속 비교해야 하는 문제에서는 해시만으로 해결되지 않을 수 있으므로 정렬, 힙, 트리와 비교한다.

자주 하는 실수
  • 리스트처럼 변경 가능한 객체를 딕셔너리 키로 사용하는 경우
  • 빈도 키를 구분자 없이 문자열로 이어 붙여 서로 다른 상태가 충돌하는 경우
  • 중복 원소를 제거하면 안 되는 문제에서 `set`만 사용하는 경우
  • 조회 전에 키가 존재하는지 확인하지 않아 예외가 발생하는 경우
  • 평균 `O(1)`이라는 이유만으로 정렬이나 순서 조건이 필요한 문제에 해시를 적용하는 경우

전체 문제 풀이 보기