Skip to content

[자료구조] #256

@blossun

Description

@blossun

해쉬(Hash)

  • 해쉬함수 : 임의의 길이의 데이터를 고정된 길이의 데이터로 대응시키는 함수
  • 충돌 : 해쉬함수에서 두 입력값에 대해 출력값이 동일한 것
  • 충돌 회피(Collision Resolution)
    • Open Addressing : 충돌이 발생했을 때 원소를 저장하는 인덱스를 바꾸는 충돌 회피 방법
    • Chaining : 해쉬 테이블에서 각 인덱스에 원소 1개만을 담는 것이 아니라 Linked List 구조로 여러 원소를 담고 있는 방식
  • 시간복잡도 : 삽입, 삭제, 검색 모두 (이론적으로는) O(1)
    • 하지만, 충돌이 빈번히 발생할수록 실제 시간복잡도는 나빠진다.
    • 테이블의 크기를 크게 한다거나, 적절한 해쉬 함수를 사용한다거나 하는 방식으로 충돌이 최대한 일어나지 않도록 하는 것이 중요

이진검색 트리

  • 시간복잡도 : 삽입, 삭제, 검색 모두 O(lgN)
    • 하지만, 트리가 편향됨에 따라 실제 시간복잡도는 O(N)이 될 수도 있다. 이를 해결하기 위해 자가 균형 트리가 존재

힙(Heap)

  • 최댓값 혹은 최솟값을 빠르게 찾아내기 위한 이진 트리. (이진 검색 트리와 다른다.)
  • 최대힙 : 부모는 자식보다 커야한다.
  • 최소힙 : 부모는 자식보다 작아야한다.
  • 루트가 최솟값 혹은 최댓값이 된다.
  • 시간복잡도 : 삽입, 최솟(최댓)값 삭제 : O(lg N), 최솟(최댓)값 확인 : O(1)

"힙에서 할 수 있는건 어차피 균형 이진 트리에서도 할 수 있지 않나요? 그러면 균형 이진 트리가 더 제공해주는 기능이 많은데 힙을 쓸 이유가 있나요?"

힙에서 할 수 있는걸 균형 이진 트리에서 할 수 있는 것이 맞고, 자가 균형 트리라는 가정하에 시간복잡도가 O(lg N)으로 동일한 것도 맞습니다. 그러나 힙은 균형 이진 트리보다 수행 속도도 빠르고, 구현도 쉽고, 공간도 적게 차지하기에 최소 혹은 최댓값의 확인/삭제만 필요할 때에는 힙을 쓰는 것이 더 좋습니다.

Java - PriorityQueue(우선순위 큐)

        // 우선순위큐 (우선순위 큐는 기본적으로 최대힙)
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        // 최소힙 : 내림차순 정렬
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(Collections.reverseOrder());

TreeMap

  • 이진트리를 기반으로 한 Map 컬렉션
  • TreeSet과의 차이점
    • TreeSet : 값만 저장
    • TreeMap : 키와 값이 저장된 Map, Etnry를 저장
  • TreeMap에 객체를 저장하면 자동으로 정렬
    • 키 : 저장과 동시에 자동 오름차순으로 정렬
      • 숫자 타입일 경우에는 값으로, 문자열 타입일 경우에는 유니코드로 정렬
    • 정렬 순서 : 기본적으로 부모 키값과 비교해서 키 값이 낮은 것은 왼쪽 자식 노드에 키값이 높은 것은 오른쪽 자식 노드에 Map.Etnry 객체를 저장
  • TreeMap은 일반적으로 Map으로써의 성능이 HashMap보다 떨어진다.
    • TreeMap은 데이터를 저장할 때 즉시 정렬하기에 추가나 삭제가 HashMap보다 오래 걸립니다.
  • 하지만 정렬된 상태로 Map을 유지해야 하거나 정렬된 데이터를 조회해야 하는 범위 검색이 필요한 경우 TreeMap을 사용하는 것이 효율성면에서 좋다.
TreeMap 전체 값 출력 코드

EntrySet, KeySet() 사용

TreeMap<Integer,String> map = new TreeMap<Integer,String>(){{//초기값 설정
    put(1, "사과");//값 추가
    put(2, "복숭아");
    put(3, "수박");
}};

//entrySet() 활용
for (Entry<Integer, String> entry : map.entrySet()) {
    System.out.println("[Key]:" + entry.getKey() + " [Value]:" + entry.getValue());
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:복숭아
//[Key]:3 [Value]:수박

//KeySet() 활용
for(Integer i : map.keySet()){ //저장된 key값 확인
    System.out.println("[Key]:" + i + " [Value]:" + map.get(i));
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:복숭아
//[Key]:3 [Value]:수박

TreeMap의 전체요소를 출력하려면 HashMap과 마찬가지로 entrySet()이나 keySet()메소드를 활용하여 Map의 객체를 반환받은 후 출력하면 됩니다. entrySet()은 key와 value 모두가 필요할 경우 사용하며 keySet()은 key 값만 필요할 경우 사용하는데 key값만 받아서 get(key)를 활용하여 value도 출력할 수도 있기에 어떤 메소드를 선택하든지 간에 큰 상관이 없어 대부분 코드가 간단한 keySet을 활용하시던데 key값을 이용해서 value를 찾는 과정에서 시간이 많이 소모되므로 많은 양의 데이터를 가져와야 한다면 entrySet()이 좋습니다.(약 20%~200% 성능 저하가 있음)

Iterator 사용

TreeMap<Integer,String> map = new TreeMap<Integer,String>(){{//초기값 설정
    put(1, "사과");//값 추가
    put(2, "복숭아");
    put(3, "수박");
}};
		
//entrySet().iterator()
Iterator<Entry<Integer, String>> entries = map.entrySet().iterator();
while(entries.hasNext()){
    Map.Entry<Integer, String> entry = entries.next();
    System.out.println("[Key]:" + entry.getKey() + " [Value]:" +  entry.getValue());
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:복숭아
//[Key]:3 [Value]:수박
		
//keySet().iterator()
Iterator<Integer> keys = map.keySet().iterator();
while(keys.hasNext()){
    int key = keys.next();
    System.out.println("[Key]:" + key + " [Value]:" +  map.get(key));
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:복숭아
//[Key]:3 [Value]:수박

TreeMap의 전체출력 시 반복문을 사용하지 않고 Iterator를 사용하여도 됩니다. iterator로 Map안의 전체 요소를 출력하는 방법은 위와 같습니다.

Metadata

Metadata

Assignees

Labels

Refer★별★공부한 내용 다시 보기

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions