우기의 알 블로그 저자 한승욱이라고 합니다.
스스로 알을 깨고 나오는 새처럼
언젠가 알을 깨고 온전한 나 자신이 되었을 때, 그때를 기다리며 제 속에서 솟아 나오는 것을 글로써 표현하고자 합니다.
'개발 기술블로그'를 위주로 저 한승욱의 다양한 관심사, 생각, 철학 등을 포스팅합니다.
해시 함수를 사용하여 색인(index)을 버킷(bucket), 슬롯(slot)의 배열로 계산
데이터의 저장과 검색이 아주 빠르게 진행된다.
파이썬에서는 딕셔너리 = 해쉬 테이블
딕셔너리
키를 통해 바로 데이터를 받아올 수 있으므로 속도가 매우 빠름
찾는 데이터에 대해 배열을 전부 다 둘러보지 않고도 바로 값을 조회할 수 있는 자료구조
딕셔너리는 내부적으로 배열을 사용
classDict:def__init__(self):
self.items =[None]*8
이와 같은 구조에서 해쉬 함수를 이용하여 저장과 검색을 진행
해쉬 함수
classDict:def__init__(self):
self.items =[None]*8defput(self, key, value):
index =hash(key)%len(self.items)
self.items[index]= value
defget(self, key):
index =hash(key)%len(self.items)return self.items[index]
my_dict =Dict()
my_dict.put("test",3)print(my_dict.get("test"))
랜덤의 값을 생성하고 이를 배열의 크기만큼 % 연산 함으로써 0 ~ 배열 크기 -1 만큼의 인덱싱을 해줄 수 있다.
하지만 여기서 문제점은 인덱스 값이 동일해질 때가 있다는 것이다. 이를 충돌이라고 한다.
충돌
똑같은 위치에 value를 넣기에 이전 값이 덮어 씌워져버리는 현상이 ㅂ
충돌 해결법
링크드 리스트: 링크드 리스트를 만들어서 추가해서 노드로써 이어주게
만듬. 인덱스가 충돌이 나더라도 데이터 손실에 대한 걱정이 없음. 키도 같이 저장해주어야 함.
classLinkedTuple:def__init__(self):self.items =list()defadd(self, key, value):self.items.append((key, value))defget(self, key):for k, v inself.items:if key ==k:return v
classLinkedDict:def__init__(self):self.items =[]for i inrange(8):self.items.append(LinkedTuple())defput(self, key, value):
index =hash(key)%len(self.items)self.items[index].add(key, value)defget(self, key):
index =hash(key)%len(self.items)returnself.items[index].get(key)
이와 같은 기법을 체이닝(chaining) 기법이라고 함
각 배열에 링크드 리스트를 연결함으로써 동일한 키로 저장되려고 한다면 기존 아이템의 뒤에 아이템으로 만들어 저장
키가 도달하게 되면 키에 대한 링크드 리스트를 조회해서 키가 가진 값을 리턴
배열에 다음 공간에 넣기
💡
특정한 값의 해쉬 값이 3이 나와서 items[3] 에 들어갔다는 가정.
다음값 의 해쉬 값이 동일하게 3이 나왔다면 그 다음 items[4]를 확인하고 비었다면
그곳에 넣고 아니라면 계속 다음 빈 주소를 찾아서 넣는 방식.
개방 주소법(Open Addressing) 이라고 함
정리
💡
해쉬 테이블(Hash Table)은 "키" 와 "데이터"를 저장함으로써 즉각적으로 데이터를 받아오고
업데이트하고 싶을 때 사용하는 자료구조입니다.
해쉬 함수(Hash Function)는 임의의 길이를 갖는 메시지를 입력하여 고정된 길이의 해쉬값을 출력하는 함수입니다.
해쉬 테이블의 내부 구현은 키를 해쉬 함수를 통해 임의의 값으로 변경한 뒤 배열의 인덱스로 변환하여 해당하는 값에 데이터를 저장합니다.
그렇기 때문에 즉각적으로 데이터를 찾고, 추가할 수 있는 것 입니다.
만약, 해쉬 값 혹은 인덱스가 중복되어 충돌이 일어난다면?
체이닝과 개방 주소법 방법으로 해결할 수 있습니다!
# 출처: 스파르타코딩클럽
문제
❓
Q. 오늘 수업에 많은 학생들이 참여했습니다. 단 한 명의 학생을 제외하고는 모든 학생이 출석했습니다.
모든 학생의 이름이 담긴 배열과 출석한 학생들의 배열이 주어질 때, 출석하지 않은 학생의 이름을 반환하시오.
# 코드 스니펫
all_students =["나연","정연","모모","사나","지효","미나","다현","채영","쯔위"]
present_students =["정연","모모","채영","쯔위","사나","나연","미나","다현"]defget_absent_student(all_array, present_array):# 구현해보세요!returnprint(get_absent_student(all_students, present_students))
여기서 이중 for문을 사용한다면 O(N^2)의 매우 비효율적인 구조가 되기에 해쉬를 사용
all_students =["나연","정연","모모","사나","지효","미나","다현","채영","쯔위"]
present_students =["정연","모모","채영","쯔위","사나","나연","미나","다현"]defget_absent_student(all_array, present_array):
student_dict ={}for key in all_array:
student_dict[key]=Truefor key in present_array:del student_dict[key]for key in student_dict.keys():return key
print(get_absent_student(all_students, present_students))
알고리즘 3주차 - 해쉬
해쉬
해쉬 테이블이란?
딕셔너리
내부적으로 배열을 사용
class Dict: def __init__(self): self.items = [None] * 8
해쉬 함수
class Dict: def __init__(self): self.items = [None] * 8 def put(self, key, value): index = hash(key) % len(self.items) self.items[index] = value def get(self, key): index = hash(key) % len(self.items) return self.items[index] my_dict = Dict() my_dict.put("test", 3) print(my_dict.get("test"))
충돌
충돌 해결법
링크드 리스트: 링크드 리스트를 만들어서 추가해서 노드로써 이어주게 만듬. 인덱스가 충돌이 나더라도 데이터 손실에 대한 걱정이 없음. 키도 같이 저장해주어야 함.
class LinkedTuple: def __init__(self): self.items = list() def add(self, key, value): self.items.append((key, value)) def get(self, key): for k, v in self.items: if key == k: return v class LinkedDict: def __init__(self): self.items = [] for i in range(8): self.items.append(LinkedTuple()) def put(self, key, value): index = hash(key) % len(self.items) self.items[index].add(key, value) def get(self, key): index = hash(key) % len(self.items) return self.items[index].get(key)
체이닝(chaining)
기법이라고 함배열에 다음 공간에 넣기
정리
문제
# 코드 스니펫 all_students = ["나연", "정연", "모모", "사나", "지효", "미나", "다현", "채영", "쯔위"] present_students = ["정연", "모모", "채영", "쯔위", "사나", "나연", "미나", "다현"] def get_absent_student(all_array, present_array): # 구현해보세요! return print(get_absent_student(all_students, present_students))
all_students = ["나연", "정연", "모모", "사나", "지효", "미나", "다현", "채영", "쯔위"] present_students = ["정연", "모모", "채영", "쯔위", "사나", "나연", "미나", "다현"] def get_absent_student(all_array, present_array): student_dict = {} for key in all_array: student_dict[key] = True for key in present_array: del student_dict[key] for key in student_dict.keys(): return key print(get_absent_student(all_students, present_students))
해쉬는 시간을 아낄수 있지만 반대로 공간을 사용하는 자료구조 라는 것을 알 수 있음
'기술개발 > Algorithm' 카테고리의 다른 글