테이블 자료구조
- 데이터가 key-value(data) 구조로 저장된 구조
- key가 데이터의 저장 및 탐색의 도구이기 때문에 key를 이용해 원하는 데이터를 찾을 수 있음
- key는 중복될 수 없고 데이터를 근거로 만들어지기 때문에 의미있는 값을 가짐
- 테이블 자료구조의 탐색 연산은 O(1)의 시간 복잡도를 보이는 장점이 있지만 메모리의 효율성이 낮고 및 적용 범위가 제한점이라는 단점이 있음
- 사전 구조 혹은 map이라고도 함
- 간단한 key-value 구조
typedef struct _empInfo {
int empNum; // 사번(key)
int age; // 나이(value)
} EmpInfo;
int main()
{
EmpInfo empArr[1000];
EmpInfo em;
em.empNum = 100;
em.age = 20;
empArr[em.empNum] = em; // 키를 근거로 데이터 저장
em = empInfo[100]; // 키를 근거로 데이터 접근
}
해쉬 함수와 충돌 문제
- 위와 같이 배열의 크기를 1000으로 설정하면 유효한 key(사번) 0~999를 가지게 되면 의미를 갖는 사번(2020005)와 같은 사번을 제대로 등록할 수 없음
- 혹은 위와 같은 사번을 사용하기 위해 배열의 크기를 늘리는 것도 비효율적임
- 유효한 key를 0~999 중 하나의 인덱스 값(해쉬)으로 매칭을 시켜주는 해쉬 알고리즘(함수)가 필요

- 간단한 해쉬 함수는 아래와 같이 구성할 수 있음
int GetHashValue(int nEmpNum)
{
int nResult = nEmpNum % 1000;
return nResult;
}
- 하지만 위와 같은 방식은 충돌을 일으킬 수 있음
- 각각 다른 사번을 해쉬 함수를 통해 인덱스 값을 얻었지만 같은 값이 추출되어 충돌하는 경우가 있음
- 테이블에서는 이러한 충돌을 처리하는 것이 매우 중요

슬롯과 테이블
- key와 value를 저장하는 구조체(공간)

typedef int KEY;
typedef Info* VALUE;
typedef int HashFunc(KEY key);
enum SlotState = {EMPTY, DELETED, INUSE};
typedef struct _Info {
int nEmpNum;
int nAge;
} Info;
typedef struct _Slot {
KEY key;
VALUE val;
enum SlotState enState;
} Slot;
typedef struct _table {
Slot tbl[MAX_TBL];
HashFunc* hf; // 등록할 수 있는 해쉬함수
} Table;
- 슬롯의 상태를 비어있는(EMPTY), 이전에 사용되었다가 비어진(DELETED), 사용중인(INUSE)로 구분
- DELETE 상태는 충돌 문제를 해결하기 위한 상태
- 테이블 관련 함수
-> 초기화 : 모든 슬롯의 상태를 EMPTY로 설정하고 해쉬 함수 등록
void TBLInit(Table * pt, HashFunc * f)
{
int i;
for(i=0; i<MAX_TBL; i++) {
(pt->tbl[i]).status = EMPTY;
}
pt->hf = f;
}
-> 삽입 : 테이블에 키와 값을 저장
void TBLInsert(Table * pt, Key k, Value v)
{
int hv = pt->hf(k); // 전달받은 유효한 키를 받아 해쉬를 돌려 인덱스 값을 얻어옴
pt->tbl[hv].val = v;
pt->tbl[hv].key = k; // 해당 슬롯에 key와 value를 저장
pt->tbl[hv].enState = INUSE; // 해당 슬롯의 상태를 사용중으로 설정
}
-> 삭제 : 테이블의 인덱스에 해당하는 슬롯의 상태를 DELETED 상태로 바꾸고 값을 반환
Value TBLDelete(Table * pt, Key k)
{
int hv = pt->hf(k);
if((pt->tbl[hv]).enState != INUSE) {
return NULL;
}
else {
(pt->tbl[hv]).enState = DELETED;
Value nValue = (pt->tbl[hv]).val;
return nValue;
}
}
-> 탐색 : 유효한 키를 이용해서 해당 인덱스의 슬롯의 값을 반환
Value TBLSearch(Table * pt, Key k)
{
int hv = pt->hf(k);
if((pt->tbl[hv]).enState != INUSE) {
return NULL;
}
else {
Value nValue = (pt->tbl[hv]).val;
return nValue;
}
}
좋은 해쉬 함수의 조건
- 좋은 해쉬 함수를 사용한 결과는 데이터의 저장 위치가 몰려있지 않고 적당히 분산되어 있음
- 좋은 해쉬 함수를 만들기 위해서는 유효한 키의 일부분을 참조하지 않고 전체를 참조하여 해쉬 값을 만들어 내는것이 중요

'Programming > Data Structure' 카테고리의 다른 글
열혈 자료구조 - 14.1 그래프의 이해와 종류 (0) | 2021.01.24 |
---|---|
열혈 자료구조 - 13.2 충돌 문제의 해결책 (0) | 2021.01.21 |
열혈 자료구조 - 12.2 균형 잡힌 이진 트리 : AVL 트리의 구현 (0) | 2021.01.17 |
열혈 자료구조 - 12.1 균형 잡힌 이진 트리 : AVL 트리의 이해 (0) | 2021.01.17 |
열혈 자료구조 - 11.2 이진 탐색 트리 (0) | 2021.01.17 |