본문 바로가기

Java/알고리즘

[자바 알고리즘 인터뷰] 11장 해시 테이블 (1) 해시맵 디자인

https://leetcode.com/problems/design-hashmap/description/

 

- 다음과 같은 기능을 제공하는 해시맵을 디자인하라.

 

put(key, value) : 키, 값을 해시맵에 삽입한다. 만약 이미 존재하는 키라면 업데이트한다. 

get(key)  : 키에 해당하는 값을 조회한다. 만약 키가 존재하지 않는다면 -1을 업데이트한다. 

remove(key) : 키에 해당하는 키, 값을 해시맵에서 삭제한다. 

 

 

풀이 1) 개별 체이닝 방식을 이용한 해시 테이블 구현 

 

 

출처 : 자바 알고리즘 인터뷰 with 코틀린 저자:박상길 <책만>

 

- 키, 값을 보관하고 연결 리스트로 처리할 별도의 객체를 구현 

//노드 클래스 
static class Node {
    int key,val;
    Node next;

    Node(int key, int val)
    {
        this.key = key;
        this.val = val;
    }
}

 

 

1) 삽입 메소드 put()

 

- 편의상 모든 키를 정수형으로 지정했으므로 nodes의 크기만큼 모듈로(Modulo)연산을 한 나머지를 해시값으로 정하는 단순한 형태로 처리한다. 

- 해싱한 결과인 index는 해시 테이블의 인덱스가 될 것 이다. 

 

//삽입 
    public void put(int key, int value) {
       //해싱 결과를 인덱스로 지정
       int index = key%nodes.length;
       //해당 인덱스에 노드가 없다면 신규 생성 후 종료 
       if(nodes[index]==null)
       {
        nodes[index] = new Node(key,value);
        return;
       } 

       //인덱스에 노드가 존재한다면 연결 리스트로 처리 
       Node node = nodes[index];
       while(node!=null)
       {
            //동일한 키가 있다면 값을 업데이트하고 종료 
            if(node.key==key)
            {
                node.val = value;
                return;
            }

            //다음 노드가 없다면 종료 
            if(node.next == null)
                break;
            //다음 노드로 진행 
            node = node.next;
       }
       //마지막 노드 다음으로 연결
       node.next = new Node(key,value);
    }

 

- 만약 해당 인덱스에 아무것도 없다면 새로운 노드를 생성하고 종료한다. 

- 만약 인덱스가 일치하는 해시 충돌(Hash Collision)이 발생한다면 별도의 처리가 필요하다. 

 -> 개별 체이닝 방식으로 충돌 해결 

 

- 첫 번째 노드부터 탐색을 시작하는데, 종료 조건은 2가지이다 .

 

 1) 이미 키가 존재하는 경우 값을 업데이트하고 바로 빠져나가는 경우 

 2) 다음 노드가 없을 때 루프를 빠져나가는 경우 

 

 

2) 조회 메소드 get()

 

- 모듈로 연산으로 인덱스를 결정하고, 해당 인덱스에 아무것도 없다면 -1을 리턴한다. 

- 노드가 존재한다면 다음 노드를 탐색하면서 일치하는 키를 찾는다.

- 찾게 되는 값을 리턴하고, 찾지 못한다면 루프를 빠져나오면서 -1을 리턴한다. 

 

//조회 
public int get(int key) {
    //해싱 결과를 인덱스로 지정
    int index = key%nodes.length;
    //인덱스에 노드가 존재하지 않으면 -1
    if(nodes[index]==null)
        return -1;
    //인덱스에 노드가 존재한다면 일치하는 키 탐색
    Node node = nodes[index];
    while(node!=null)
    {
        //동일한 키가 있다면 값을 리턴
        if(node.key == key)
        {
            return node.val;
        }
        //다음 노드로 진행
        node = node.next;
    }
    //인덱스를 모두 순회해도 일치하는 키가 없다면 -1
    return -1; 
    }

 

 

3) 삭제 메소드 remove()

 

-  인덱스를 구한 다음 아무것도 없다면 그냥 리턴한다. 

 

첫 번째 노드일때의 삭제 처리 

- 다음 노드가 없으면 유일한 노드이므로 해당 인덱스는 null로 처리하고, 다음 노드가 있다면 다음 노드를 해당 이넫스의 루트로 지정한다. 

 

연결 리스트 노드일 때의 삭제 처리  

 - 이전 노드를 prev로 정한 다음, 계속 다음 노드를 탐색하다가 일치하는 노드를 찾게 되면, 이전 노드인 prev의 다음에 현재 노드의 다음을 연결한다. 

 

 

//삭제
    public void remove(int key) {
        //해싱 결과를 인덱스로 지정
        int index = key%nodes.length;
        //해당 인덱스에 노드가 없다면 종료 
        if(nodes[index]== null)
            return;
        //첫번째 노드일때의 삭제 처리 
        Node node = nodes[index];
        //일치하는 키가 있다면
        if(node.key == key) {
            //다음 노드가 없으면 해당 인덱스는 null 처리
            if(node.next == null)
                nodes[index] = null;
            //다음 노드가 있다면 다음 노드를 해당 인덱스로 지정
            else
                nodes[index] = node.next;
        }
        //연결 리스트 노드일 때의 삭제 처리
        Node prev = node;
        while(node!=null) 
        {
            //일치하는 키가 있다면
            if(node.key == key) {
                //이전 노드의 다음에 현재 노드의 다음을 연결하고 리턴
                prev.next = node.next;
                return;
            }
            //노드 한 칸씩 이동
            prev = node;
            node = node.next;
        }
    }

 

 

 

class MyHashMap {
    //노드 클래스 
    static class Node {
        int key,val;
        Node next;

        Node(int key, int val)
        {
            this.key = key;
            this.val = val;
        }
    }

    final Node[] nodes = new Node[1000000];
    
    //삽입 
    public void put(int key, int value) {
       //해싱 결과를 인덱스로 지정
       int index = key%nodes.length;
       //해당 인덱스에 노드가 없다면 신규 생성 후 종료 
       if(nodes[index]==null)
       {
        nodes[index] = new Node(key,value);
        return;
       } 

       //인덱스에 노드가 존재한다면 연결 리스트로 처리 
       Node node = nodes[index];
       while(node!=null)
       {
            //동일한 키가 있다면 값을 업데이트하고 종료 
            if(node.key==key)
            {
                node.val = value;
                return;
            }

            //다음 노드가 없다면 종료 
            if(node.next == null)
                break;
            //다음 노드로 진행 
            node = node.next;
       }
       //마지막 노드 다음으로 연결
       node.next = new Node(key,value);
    }
    
    //조회 
    public int get(int key) {
        //해싱 결과를 인덱스로 지정
        int index = key%nodes.length;
        //인덱스에 노드가 존재하지 않으면 -1
        if(nodes[index]==null)
            return -1;
        //인덱스에 노드가 존재한다면 일치하는 키 탐색
        Node node = nodes[index];
        while(node!=null)
        {
            //동일한 키가 있다면 값을 리턴
            if(node.key == key)
            {
                return node.val;
            }
            //다음 노드로 진행
            node = node.next;
        }
        //인덱스를 모두 순회해도 일치하는 키가 없다면 -1
        return -1;
    }
    
    //삭제
    public void remove(int key) {
        //해싱 결과를 인덱스로 지정
        int index = key%nodes.length;
        //해당 인덱스에 노드가 없다면 종료 
        if(nodes[index]== null)
            return;
        //첫번째 노드일때의 삭제 처리 
        Node node = nodes[index];
        //일치하는 키가 있다면
        if(node.key == key) {
            //다음 노드가 없으면 해당 인덱스는 null 처리
            if(node.next == null)
                nodes[index] = null;
            //다음 노드가 있다면 다음 노드를 해당 인덱스로 지정
            else
                nodes[index] = node.next;
        }
        //연결 리스트 노드일 때의 삭제 처리
        Node prev = node;
        while(node!=null) 
        {
            //일치하는 키가 있다면
            if(node.key == key) {
                //이전 노드의 다음에 현재 노드의 다음을 연결하고 리턴
                prev.next = node.next;
                return;
            }
            //노드 한 칸씩 이동
            prev = node;
            node = node.next;
        }
    }
}

/**
 * Your MyHashMap object will be instantiated and called as such:
 * MyHashMap obj = new MyHashMap();
 * obj.put(key,value);
 * int param_2 = obj.get(key);
 * obj.remove(key);
 */

19 밀리초