배열 보다 삽입/삭제가 빠르고 데이터 접근은 더 느린 자료구조이다.
자바의 LinkdedList는 List 인터페이스의 구현체이므로 add
remove
set
같은 메소드를 사용 가능하다.
내부적으로 이중 연결 리스트로 구현되어있고, 최적화가 잘 되어있어 직접 연결 리스트를 구현하는것 보다 바로 사용하는게 낫다.
List<Integer> list = new LinkedList<>();
list.addFirst(10);
list.addLast(20);
System.out.println(list.getFirst()); // 10
System.out.println(list.removeLast()); // 20
addFirst(E e)
addLast(E e)
E getFirst()
E getLast()
E get(int index)
E removeFirst()
E removeLast()
선입후출 자료구조다.
Stack
클래스를 사용하거나 ArrayList
로 구현할 수 있지만, 스택에 최적화된 ArrayDeque
를 쓰는 것이 좋다. 단일 스레드 환경에서 가장 빠르고 효율적이다.
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1);
stack.push(2);
System.out.println(stack.pop()); // 2
push()
pop()
add()
remove(size - 1)
참고: Stack 클래스는 오래된 클래스이고 동기화가 필요 없는 단일 스레드에서는 ArrayDeque가 더 효율적이다.
선입선출 자료구조다.
Queue
인터페이스를 구현한 ArrayDeque를
사용하면 대부분의 단일 스레드 환경에서 가장 빠르고 효율적이다. LinkedList
도 가능하지만, 배열 기반 구조인 ArrayDeque
가 일반적으로 더 좋다.
Queue<Integer> queue = new ArrayDeque<>();
queue.offer(1); // 또는 add(1)
int a = queue.poll(); // 또는 remove()
poll 은 remove 와 다르게 리스트가 비어있을때 예외가 아닌 null을 반환하므로 안전하다.
Queue<Integer> queue = new ArrayDeque<>();
→ Queue 메서드만 사용 가능LinkedList<Integer> list = new ArrayDeque<>();
→ 모든 메서드 사용 가능Deque<Integer> deque = new ArrayDeque<>();
→ Deque 관련 메서드 사용 가능단일 스레드 큐/스택 용도로는 ArrayDeque가 최적 선택입니다.
계층 구조로 데이터를 표현하는 자료구조이다. 탐색, 삽입, 삭제 동작을 모두 효율적으로 해야할 때 유용하다.
컬렉션에 TreeSet 과 TreeMap 이 구현되어 있지만
웬만하면 문제에 맞게 직접 구현해서 쓴다.
private static class Node<T> {
T data;
Node left;
Node right;
Node(T data, Node left, Node right) {
this.data = data;
this.left = left;
this.right = right;
}
}
//..
List<Node> tree = new LinkedList<>();
//..
// 중위 순회 코드
void inOrder(TreeNode node) {
if (node == null) return;
inOrder(node.left);
System.out.print(node.key + " ");
inOrder(node.right);
}
탐색을 효율적으로 하기 위한 자료구조이다.
TreeSet
으로 구현되어있다.
Set<Integer> tree = new TreeSet<>(Arrays.asList(5, 3, 1, 9, 7));
tree.add(2);
for(int i: tree) {
System.out.print(i + " "); // 1 2 3 5 7 9
}
이진 탐색 트리의 조건은 정렬과 중복된 값 없음 이므로 특정 리스트를 TreeSet 으로 변환하면 정렬되고 중복된 값이 제거된다.
값의 위치를 찾고싶다면 다시 List
로 변환해서 Collections.binarySearch()
를 사용해도 된다.
i
번 노드의 왼쪽 자식은 2i
, 오른쪽 자식은 2i+1
이라는 관계를 이용하여 매우 효울적으로 작동한다.// 배열을 사용하여 트리의 노드들을 저장하는 경우 (1-based 인덱스 사용)
int[] tree = new int[size + 1]; // size는 트리의 노드 수
이 구조는 힙(Heap) 등 여러 트리 기반 자료구조에서 사용된다.
완전 이진 트리에 부모노드가 자식노드보다 무조건 작거나 큰 값을 가지면 힙 구조이다.
모든 값이 아닌, 최대값이나 최소값을 빠르게 찾기 위한 구조이다.
PriorityQueue
클래스를 사용한다.
// 작은 수가 우선순위인 우선순위 큐
Queue<Integer> q = new PriorityQueue<>();
q.offer(3);
q.offer(1);
q.offer(12);
q.offer(2);
System.out.println(q); // [1, 2, 12, 3]
int a = q.poll(); // a == 1
System.out.println(q); // [2, 3, 12]
Comparator
를 활용하여 다른 우선순위를 적용할 수 있다.
기본: 작은 값 우선
큰 값 우선 : new PriorityQueue<>(Collections.reverseOrder());
임의의 순서: Comparator 사용
class Node {
int num; // 노드 번호
int cost; // 노드까지의 비용
public Node(int num, int cost) {
this.num = num;
this.cost = cost;
}
}
// Node.cost 가 작은 것이 우선순위인 우선순위 큐
Queue<Node> q = new PriorityQueue<>((o1, o2) -> Integer.compare(o1.cost, o2.cost));
Map<String, Integer> map = new HashMap<>();
map.put("s", 2);
map.put("s", 1); // 덮어쓰기
map.put("s1", 3);
map.get("s"); // 1
문자열의 집합을 표현하는 트리.
많은 단어에서 빠른 접두사 검색을 할 때 사용
각 간선 (또는 정점)이 하나의 문자에 대응하고
단말 노드 (자식이 없는) 노드는 루트부터 쭉 내려오며 문자를 모두 합한 문자열에 대응하게 된다.
[알고리즘] 그래프 표현 및 문제 풀이시 표현 방법 선택 기준 참고
Java 문법 공식 문서: oracle JDK 11 Docs