해시 테이블

해시 테이블

해시 테이블(Hash Table)은 데이터를 효율적으로 저장하고 검색하기 위해 사용하는 자료구조임. 해시 테이블은 키(Key)와 값(Value)의 쌍으로 데이터를 저장하며, 키를 해시 함수(Hash Function)를 통해 해시값으로 변환하고, 이 해시값을 기반으로 데이터를 저장할 위치를 결정.

구성 요소

  • 해시 함수 (Hash Function)

    • 키를 입력으로 받아 해시값을 생성하는 함수.

    • 해시 함수는 입력된 키를 일정한 크기의 정수(인덱스)로 변환하여 해시 테이블의 특정 위치를 가리킴.

  • 버킷 (Bucket)

    • 해시 테이블에서 데이터를 저장하는 공간.

    • 각 버킷은 여러 개의 값을 저장할 수 있으며, 해시 함수에 의해 인덱싱된 위치에 데이터를 저장함.

  • 충돌 (Collision)

    • 서로 다른 키들이 동일한 해시값을 가질 때 발생하는 현상.

    • 충돌을 처리하기 위해 다양한 방법이 사용됨.

충돌 해결 방법

  • 체이닝 (Chaining)

    • 각 버킷에 연결 리스트(Linked List) 등을 사용해 여러 개의 값을 저장하여 충돌을 해결하는 방법.

    • 같은 인덱스에 여러 값이 저장될 수 있으며, 충돌이 발생하면 새로운 값을 리스트에 추가

  • 오픈 어드레싱 (Open Addressing)

    • 충돌이 발생하면 다른 빈 버킷을 찾아 값을 저장하는 방법.

    • 선형 탐사(Linear Probing), 이차 탐사(Quadratic Probing), 이중 해싱(Double Hashing) 등의 방식이 있음.

장점과 단점

  • 장점

    • 평균적으로 빠른 검색, 삽입, 삭제 성능 (O(1))

    • 효율적인 메모리 사용.

  • 단점

    • 해시 함수의 품질에 따라 성능이 크게 좌우될 수 있음.

    • 충돌 발생 시 성능이 저하될 수 있음.

    • 데이터의 순서를 유지하지 않음.

사용 예시

해시 테이블은 주로 데이터베이스, 캐싱 시스템, 키-값 저장소, 그리고 중복 검출과 같은 다양한 분야에서 널리 사용됨.

해시 테이블은 매우 효율적이지만 해시 함수의 선택과 충돌 처리 방법에 따라 성능이 크게 달라질 수 있으므로 상황에 맞게 설계하는 것이 중요함.

코드

class HashTable {
  constructor(size = 53) {
    this.table = new Array(size); // 해시 테이블의 크기를 설정
    this.size = size;
  }

  // 간단한 해시 함수: 문자열 키를 정수 인덱스로 변환
  _hash(key) {
    let total = 0;
    const WEIRD_PRIME = 31; // 소수를 사용하여 해시 충돌을 줄임
    for (let i = 0; i < Math.min(key.length, 100); i++) {
      const char = key[i];
      const value = char.charCodeAt(0) - 96; // 알파벳을 숫자로 변환
      total = (total * WEIRD_PRIME + value) % this.size;
    }
    return total;
  }

  // 데이터를 해시 테이블에 삽입
  set(key, value) {
    const index = this._hash(key); // 키를 해시값으로 변환하여 인덱스 찾기
    if (!this.table[index]) {
      this.table[index] = [];
    }
    // 기존 키-값 쌍이 있는지 확인하고 업데이트
    for (let i = 0; i < this.table[index].length; i++) {
      if (this.table[index][i][0] === key) {
        this.table[index][i][1] = value; // 값 업데이트
        return;
      }
    }
    this.table[index].push([key, value]); // 새로운 키-값 쌍 추가
  }

  // 데이터를 해시 테이블에서 검색
  get(key) {
    const index = this._hash(key);
    if (this.table[index]) {
      for (let pair of this.table[index]) {
        if (pair[0] === key) {
          return pair[1]; // 키와 일치하는 값을 반환
        }
      }
    }
    return undefined; // 키가 없으면 undefined 반환
  }

  // 해시 테이블에서 데이터 삭제
  remove(key) {
    const index = this._hash(key);
    if (this.table[index]) {
      this.table[index] = this.table[index].filter((pair) => pair[0] !== key); // 키에 해당하는 쌍을 제거
    }
  }
}

// 사용 예시
const ht = new HashTable();
ht.set("apple", 10);
ht.set("banana", 20);
console.log(ht.get("apple")); // 10
console.log(ht.get("banana")); // 20
ht.remove("apple");
console.log(ht.get("apple")); // undefined
// Set사용
class HashTable {
  constructor() {
    this.table = new Set(); // Set을 사용하여 데이터 저장
  }

  // 데이터를 삽입하는 메서드
  set(key, value) {
    // 동일한 키가 있으면 기존 쌍을 삭제하고 새로운 값으로 업데이트
    this.table.forEach((pair) => {
      if (pair[0] === key) {
        this.table.delete(pair);
      }
    });
    this.table.add([key, value]); // 새로운 키-값 쌍을 추가
  }

  // 데이터를 검색하는 메서드
  get(key) {
    for (let [k, v] of this.table) {
      if (k === key) {
        return v; // 키에 해당하는 값을 반환
      }
    }
    return undefined; // 키가 없으면 undefined 반환
  }

  // 데이터를 삭제하는 메서드
  remove(key) {
    for (let pair of this.table) {
      if (pair[0] === key) {
        this.table.delete(pair); // 키에 해당하는 쌍을 삭제
        return;
      }
    }
  }
}

// 사용 예시
const ht = new HashTable();
ht.set("apple", 10);
ht.set("banana", 20);
console.log(ht.get("apple")); // 10
console.log(ht.get("banana")); // 20
ht.remove("apple");
console.log(ht.get("apple")); // undefined

// Set의 고유한 특성을 활용한 간단한 해시 테이블 구현 방법이지만, Set은 본래 키-값 쌍의 해시 테이블보다는 Map에 가까운 데이터 구조임. Set을 활용하는 방식은 간단한 저장, 검색, 삭제 기능을 구현할 수 있지만, 실제 해시 테이블의 충돌 처리와 같은 고급 기능은 제공하지 않음.

Last updated