트리 (Tree)
Tree 구조는 계층적인 구조를 표현할 수 있는 자료구조이다.
Tree 구조는 루트 노드에서 시작하여 여러개의 하위 노드들이 연결된 형태로 구성되며, 각 노드는 각자의 하위 노드들을 가질 수 있다.
Java에서는 Tree 구조를 구현하기 위해 다음의 인터페이스와 클래스들을 사용한다.
아래 인터페이스와 클래스들은 중복값을 허용하지 않는다.
사용 인터페이스
- java.util.Set
Tree 구조를 구현하기 위한 인터페이스로, 이 인터페이스를 implements 하는 클래스들은 모두 Tree 구조를 갖는다. - java.util.SortedSet
Set 인터페이스를 상속받으며, 정렬된 Tree 구조를 표현하기 위한 인터페이스이다.
사용 클래스
- java.util.TreeSet
SortedSet 인터페이스를 implements하는 클래스로, 기본적으로 오름차순 정렬이다
이진 트리 (BinaryTree)
- 각 노드가 최대 두 개의 자식 노드를 가지는 트리 자료구조이다.
- 이진 트리에서 자식 노드가 없는 노드를 리프(leaf) 노드라고 한다.
- 각 노드는 최대 두 개의 자식 노드를 가질 수 있다.
- 왼쪽 자식 노드는 현재 노드보다 작은 값을, 오른쪽 자식 노드는 현재 노드보다 큰 값을 가진다.
- 왼쪽 서브트리는 현재 노드보다 작은 값의 모임이며, 오른쪽 서브트리는 현재 노드보다 큰 값의 모임이다.
이진 탐색 트리 (Binary Search Tree)
- 이진 트리는 탐색(Binary Search)을 위한 자료구조로 많이 사용된다.
- 이진 탐색 트리(Binary Search Tree)는 이진 트리의 한 종류로, 모든 노드가 위의 특징을 만족해야 한다. 이진 탐색 트리에서는 특정 값을 가진 노드를 빠르게 찾을 수 있다.
- Java에서 이진 트리를 구현하려면 TreeNode 클래스와 같은 노드 클래스를 만들고, 노드 클래스를 이용하여 이진 트리를 구현할 수 있다.
- 이진 탐색 트리를 구현하려면, 각 노드가 key-value 쌍을 가지도록 하고, key 값으로 노드를 정렬한다.
- 또한, 삽입(insertion), 삭제(deletion), 탐색(search) 등의 기능을 구현해야 한다.
- 자바에서 이진 트리를 사용하는 방법에는 직접 구현하는 것 외에도 **java.util.TreeMap**과 java.util.TreeSet 클래스를 사용하는 방법이 있다.
TreeSet
- TreeSet은 이진 검색 트리를 사용하여 원소를 저장한다.
- 이진 검색 트리를 사용하므로 삽입, 삭제, 검색의 시간복잡도는 O(log N) 이다.
- 트리의 각 노드는 Key와 Value를 가지며 노드의 정렬 기준은 Key이다.
TreeSet 클래스 생성자
TreeSet 클래스의 생성자는 다음과 같다.
- 기본생성자
빈 TreeSet 객체를 생성한다.
TreeSet<String> treeSet = new TreeSet<>();
- Comparator를 인자로 받는 생성자
지정된 Comparator를 사용하여 원소를 정렬하는 TreeSet 객체를 생성한다.
TreeSet<Integer> treeSet = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
}
);
- SortedSet을 인자로 받는 생성자
주어진 SortedSet의 모든 원소를 포함하는 TreeSet 객체를 생성한다.
SortedSet<Integer> sortedSet = new TreeSet<>();
sortedSet.add(1);
sortedSet.add(2);
sortedSet.add(3);
TreeSet<Integer> treeSet = new TreeSet<>(sortedSet);
- Collection을 인자로 받는 생성자
주어진 컬렉션의 모든 원소를 포함하는 TreeSet 객체를 생성한다.
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
TreeSet<String> treeSet = new TreeSet<>(list);
- 다른 TreeSet을 인자로 받는 생성자
TreeSet<String> originalTreeSet = new TreeSet<>();
originalTreeSet.add("apple");
originalTreeSet.add("banana");
originalTreeSet.add("cherry");
TreeSet<String> newTreeSet = new TreeSet<>(originalTreeSet);
TreeSet 메서드
- add(E e)
주어진 원소를 TreeSet에 추가한다.
TreeSet<String> treeSet = new TreeSet<String>();
treeSet.add("apple");
treeSet.add("banana");
treeSet.add("cherry");
- contains(Object o)
주어진 원소가 TreeSet에 포함되어 있는지 여부를 반환한다.
boolean containsBanana = treeSet.contains("banana");
System.out.println("containsBanana: " + containsBanana);
//containsBanana: true
- first()
TreeSet의 가장 작은 원소를 반환한다.
String firstElement = treeSet.first();
System.out.println("firstElement: " + firstElement);
//firstElement: apple
- last()
TreeSet의 가장 큰 원소를 반환한다.
String lastElement = treeSet.last();
System.out.println("lastElement: " + lastElement);
//lastElement: cherry
- iterator()
TreeSet의 iterator를 반환한다.
System.out.print("treeSet: ");
for (String element : treeSet) {
System.out.print(element + " ");
}
System.out.println();
//treeSet: apple banana cherry
- remove(Object o)
주어진 원소를 TreeSet에서 삭제한다.
treeSet.remove("banana");
- size()
TreeSet의 원소 개수를 반환한다.
int size = treeSet.size();
//size: 2
- clear()
TreeSet의 모든 원소를 삭제한다.
treeSet.clear();
- isEmpty()
TreeSet이 비어 있는지 여부를 반환한다.
boolean isEmpty = treeSet.isEmpty();
System.out.println("isEmpty: " + isEmpty);
//isEmpty: true
TreeMap
TreeMap 클래스의 생성자
- 기본생성자
기본 생성자로써 TreeMap 객체를 생성한다. 이때 기본적으로는 key 값들이 정렬되도록 Comparator 를 지정해줘야 한다.
TreeMap<String, Integer> treeMap = new TreeMap<>();
- Comparator를 인자로 받는 생성자
Comparator 를 사용하여 TreeMap 객체를 생성한다.
TreeMap<String, Integer> treeMap = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
TreeMap 클래스의 주요 메서드
- put(K key, V value)
key와 value를 TreeMap에 추가한다.
treeMap.put("banana", 5);
treeMap.put("apple", 3);
treeMap.put("cherry", 1);
treeMap.put("dragonfruit", 2);
- get(Object key)
key에 해당하는 value를 반환한다.
Integer value = treeMap.get("banana"); // 5
- remove(Object key)
key에 해당하는 entry를 TreeMap에서 삭제한다.
treeMap.remove("banana");
- firstKey()
TreeMap의 첫번째 key를 반환한다.
String firstKey = treeMap.firstKey(); // "apple"
- lastKey()
TreeMap의 마지막 key를 반환한다.
String lastKey = treeMap.lastKey(); // "dragonfruit"
- keySet()
TreeMap의 key들을 Set 형태로 반환한다.
Set<String> keys = treeMap.keySet();
- values()
TreeMap의 value들을 Collection 형태로 반환한다.
Collection<Integer> values = treeMap.values();
- entrySet()
TreeMap의 entry들을 Set 형태로 반환한다.
Set<Map.Entry<String, Integer>> entries = treeMap.entrySet();
- size()
TreeMap의 크기를 반환한다.
int size = treeMap.size();
- isEmpty()
TreeMap이 비어있는지 여부를 반환한다.
boolean isEmpty = treeMap.isEmpty();
- clear()
TreeMap의 모든 entry를 삭제한다.
boolean isEmpty = treeMap.isEmpty();
이진 탐색 트리를 꼭 구현해야 할까?
자료구조를 선택할 때는 해당 자료구조가 가지는 특징과 자신이 해결해야 하는 문제의 특성을 고려하여 선택해야 한다.
이진탐색 문제의 경우 TreeSet이나 TreeMap을 사용해 충분히 해결할 수 있다.
원소들의 삽입과 삭제가 빈번하게 일어나지 않기 때문이다.
하지만 반대로 원소들의 삽입과 삭제가 빈번하게 일어나는 경우, TreeSet과 TreeMap은 비교적 느릴 수 있다.
이진트리는 일반적으로 원소 삽입과 삭제 시간 복잡도가 O(log n)이므로, 원소가 많을수록 처리 시간이 늘어난다.
'Java > 문법, 자료구조, 알고리즘' 카테고리의 다른 글
Builder 패턴과 @Builder 어노테이션 (0) | 2023.04.15 |
---|---|
BufferedReader로 입력 받을 때 NPE 방지하기 (0) | 2023.04.04 |
PriorityQueue (0) | 2023.04.01 |
== 와 .equals() 차이 (0) | 2023.03.28 |
StringTokenizer (0) | 2023.03.25 |