hash

Hash

: 검색과 λ”λΆˆμ–΄ λ°μ΄ν„°μ˜ 좔가와 μ‚­μ œλ„ 효율적으둜 μˆ˜ν–‰ν•  수 μžˆλŠ” 방법 μš”μ†Œ 이동에 ν•„μš”ν•œ μ‹œκ°„ λ³΅μž‘λ„λŠ” O(n)이며, μ‚­μ œ 할떄도 같은 λΉ„μš©μœΌλ‘œ λ°œμƒ

F(key) -> Hashcode -> index -> value
  • κ²€μƒ‰ν•˜κ³ μž ν•˜λŠ” 킀값을 λ°›μ•„μ„œ ν•΄μ‰¬ν•¨μˆ˜λ‘œ λŒλ €μ„œ λ°˜ν™˜λ°›μ€ hashcodeλ₯Ό μ΄μš©ν•΄μ„œ λ°°μ—΄μ˜ indexλ₯Ό μ΄μš©ν•΄μ„œ λ°μ΄ν„°μ˜ μžλ£Œμ— μ ‘κ·Όν•˜λŠ” 방법 key 값은 숫자, λ¬Έμžμ—΄, 파일 등에 ν•΄λ‹Ήν•œλ‹€

  • 해쉬 ν…Œμ΄λΈ”μ€ hashcode % size둜 λ‚˜λˆ„μ–΄ 인덱슀둜 μ‚¬μš©ν•œλ‹€

  • λ“€μ–΄μ˜¬λ•Œλ§ˆλ‹€ linkedList둜 μ €μž₯ν•˜κ³  μ°ΎλŠ”λ‹€

  • 좩돌의 λ”°λΌμ„œ λΆ„λ₯˜(Collision)

  • O(1) -> O(N)κΉŒμ§€ λ³΅μž‘λ„κ°€ λŠ˜μ–΄λ‚œλ‹€

  • 체인법 : 같은 ν•΄μ‹œ 값을 κ°–λŠ” μš”μ†Œλ₯Ό μ—°κ²° 리슀트둜 관리

  • μ˜€ν”ˆ μ£Όμ†Œλ²• : 빈 버킷을 찾을 λ•ŒκΉŒμ§€ ν•΄μ‹œλ₯Ό 반볡

ν•΄μ‹œμ™€ ν•΄μ‹œ ν•¨μˆ˜μ— λŒ€ν•˜μ—¬

λ§Œμ•½ 좩돌이 μ „ν˜• λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ ν•΄μ‹œ ν•¨μˆ˜λ‘œ 인덱슀λ₯Ό μ°ΎλŠ” κ²ƒλ§ŒμœΌλ‘œ 검색, μΆ”κ°€, μ‚­μ œκ°€ 거의 μ™„λ£Œ λ˜λ―€λ‘œ μ‹œκ°„ λ³΅μž‘λ„λŠ” μ–΄λŠ κ²ƒμ΄λ‚˜ O(1)이 λœλ‹€ ν•΄μ‹œ ν…Œμ΄λΈ”μ„ 크게 ν•˜λ©΄ 좩돌 λ°œμƒμ„ μ–΅μ œ ν•  수 μžˆμ§€λ§Œ λ‹€λ₯Έ ν•œνŽΈμœΌλ‘œ λ©”λͺ¨λ¦¬λ₯Ό 쓸데없이 많이 μ°¨μ§€ν•œλ‹€ 즉, μ‹œκ°„κ³Ό κ³΅κ°„μ˜ 절좜(trade-Off)μ΄λΌλŠ” λ¬Έμ œκ°€ 항상 따라 λ‹€λ‹™λ‹ˆλ‹€. μΆ©λŒμ„ ν”Όν•˜κΈ° μœ„ν•΄ ν•΄μ‹œ ν•¨μˆ˜λŠ” ν•΄μ‹œ ν…Œμ΄λΈ” 크기 μ΄ν•˜μ˜ μ •μˆ˜λ₯Ό λ˜λ„λ‘ ν•œμͺ½μœΌλ‘œ μΉ˜μš°μΉ˜μ§€ μ•Šκ³  κ³ λ₯΄κ²Œ λ§Œλ“€μ–΄μ•Ό ν•œλ‹€ κ·Έλž˜μ„œ ν•΄μ‹œ ν…Œμ΄λΈ” ν¬κΈ°λŠ” μ†Œμˆ˜κ°€ μ’‹λ‹€κ³  μ•Œλ €μ Έμžˆλ‹€ ν‚€ 값이 μ •μˆ˜κ°€ μ•„λ‹Œ 경우 ν•΄μ‹œ 값을 ꡬ할 λ–„λŠ” μ’€ 더 신경을 써 λ°©λ²•μœΌ λͺ¨μƒ‰ν•΄μ•Ό ν•œλ‹€. μ˜ˆκ±΄λŒ€ μ‹€μˆ˜ ν‚€ 값에 λŒ€ν•΄ λΉ„νŠΈ μ—°μ‚°(bitwise operation)을 ν•˜λŠ” 방법, λ¬Έμžμ—΄ ν‚€ 값에 λŒ€ν•΄ 각 문자 μ½”λ“œμ— κ³±μ…ˆκ³Ό λ§μ…ˆμ„ ν•˜λŠ” 앙법이 μžˆλ‹€

μ˜€ν”ˆ μ£Όμ†Œλ²•μ— μ˜ν•œ ν•΄μ‹œ

public class OpenHash<K,V> {
    // λ²„ν‚·μ˜ μƒνƒœ
    enum Status {OCCUPIED, EMPTY, DELETED};        // {데이터 μ €μž₯, λΉ„μ–΄ 있음, μ‚­μ œ 마침}

    // 버킷
    static class Bucket<K,V> {
        private K key;                            // ν‚€ κ°’
        private V data;                            // 데이터
        private Status stat;                    // μƒνƒœ

        // μƒμ„±μž
        Bucket() {
            stat = Status.EMPTY;                // 버킷은 λΉ„μ–΄ 있음
        }

        // λͺ¨λ“  ν•„λ“œμ— 값을 μ„€μ •ν•©λ‹ˆλ‹€.
        void set(K key, V data, Status stat) {
            this.key  = key;                    // ν‚€ κ°’
            this.data = data;                    // 데이터
            this.stat = stat;                    // μƒνƒœ
        }

        // μƒνƒœλ₯Ό μ„€μ •ν•©λ‹ˆλ‹€.
        void setStat(Status stat) {
            this.stat = stat;
        }

        // ν‚€ 값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
        K getKey() {
            return key;
        }

        // 데이터λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
        V getValue() {
            return data;
        }

        // ν‚€μ˜ ν•΄μ‹œ 값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
        public int hashCode() {
            return key.hashCode();
        }
    }

    private int size;                        // ν•΄μ‹œ ν…Œμ΄λΈ”μ˜ 크기
    private Bucket<K,V>[] table;            // ν•΄μ‹œ ν…Œμ΄λΈ”

    // μƒμ„±μž
    public OpenHash(int size) {
        try {
            table = new Bucket[size];
            for (int i = 0; i < size; i++)
                table[i] = new Bucket<K,V>();
            this.size = size;
        } catch (OutOfMemoryError e) {        // ν…Œμ΄λΈ”μ„ 생성할 수 μ—†μŒ
            this.size = 0;
        }
    }

    // ν•΄μ‹œ 값을 ꡬ함
    public int hashValue(Object key) {
        return key.hashCode() % size;
    }

    // μž¬ν•΄μ‹œκ°’μ„ ꡬ함
    public int rehashValue(int hash) {
        return (hash + 1) % size;
    }

    // ν‚€ κ°’ keyλ₯Ό κ°–λŠ” λ²„ν‚·μ˜ 검색
    private Bucket<K,V> searchNode(K key) {
        int hash = hashValue(key);                // 검색할 λ°μ΄ν„°μ˜ ν•΄μ‹œκ°’
        Bucket<K,V> p = table[hash];            // 선택 버킷

        for (int i = 0; p.stat != Status.EMPTY && i < size; i++) {
            if (p.stat == Status.OCCUPIED && p.getKey().equals(key))
                return p;
            hash = rehashValue(hash);            // μž¬ν•΄μ‹œ
            p = table[hash];
        }
        return null;
    }

    // ν‚·κ°’ keyλ₯Ό κ°–λŠ” μš”μ†Œμ˜ 검색 (데이터λ₯Ό λ°˜ν™˜)
    public V search(K key) {
        Bucket<K,V> p = searchNode(key);
        if (p != null)
            return p.getValue();
        else
            return null;
    }

    // ν‚€ κ°’ key, 데이터 dataλ₯Ό κ°–λŠ” μš”μ†Œμ˜  μΆ”κ°€
    public int add(K key, V data) {
        if (search(key) != null)
            return 1;                            // 이 ν‚€ 값은 이미 등둝됨

        int hash = hashValue(key);                // μΆ”κ°€ν•  λ°μ΄ν„°μ˜ ν•΄μ‹œ κ°’
        Bucket<K,V> p = table[hash];            // 선택 버킷
        for (int i = 0; i < size; i++) {
            if (p.stat == Status.EMPTY || p.stat == Status.DELETED) {
                p.set(key, data, Status.OCCUPIED);
                return 0;
            }
            hash = rehashValue(hash);            // μž¬ν•΄μ‹œ
            p = table[hash];
        }
        return 2;                                // ν•΄μ‹œ ν…Œμ΄λΈ”μ΄ 가득 μ°Έ
    }

    // ν‚€ κ°’ keyλ₯Ό κ°–λŠ” μš”μ†Œμ˜ μ‚­μ œ
    public int remove(K key) {
        Bucket<K,V> p = searchNode(key);        // 선택 버킷
        if (p == null)
            return 1;                            // 이 ν‚€ 값은 λ“±λ‘λ˜μ§€ μ•ŠμŒ

        p.setStat(Status.DELETED);
        return 0;
    }

    // ν•΄μ‹œ ν…Œμ΄λΈ”μ„ 덀프
    public void dump() {
        for (int i = 0; i < size; i++) {
            System.out.printf("%02d ", i);
            switch (table[i].stat) {
             case OCCUPIED : 
                System.out.printf("%s (%s)\n", 
                                        table[i].getKey(), table[i].getValue());
                break;

             case EMPTY :
                 System.out.println("-- 미등둝 --");    break;

             case DELETED :
                 System.out.println("-- μ‚­μ œ 마침 --");    break;
            }
        }
    }
}

체인법

Last updated

Was this helpful?