# 자료구조 - 자료구조란 데이터를 효율적으로 이용할 수 있도록 컴퓨터에 저장하는 방법이다. - 가장 간단한 자료구조로는 배열이 있으며, 배열은 데이터를 순차적으로 저장하는 구조이다. - 자료를 저장하는 방식에 따라 선형 구조와 비선형 구조로 나뉜다. - 선형 자료구조는 순차적으로 데이터를 저장하는 구조이며, 비선형 자료구조는 트리나 그래프와 같이 계층적인 구조를 가진다. - 선형 자료구조 : 데이터를 순차적으로 저장하는 자료구조 - 데이터들은 선(line)처럼 일렬로 늘어선 형태를 가진다. - 하나의 데이터 뒤에 다음 데이터가 존재한다. - 데이터들간의 앞뒤 관계가 1:1 관계를 가진다. - 리스트, 스택, 큐 등이 선형 자료구조에 속한다 - 비선형 자료구조 : 데이터를 순차적으로 저장하지 않는 자료구조 - 데이터들은 계층적인 구조를 가진다. - 하나의 데이터 뒤에 여러 개의 데이터가 존재할 수 있다. - 데이터들간의 앞뒤 관계가 1:N, N:N 관계를 가진다. - 트리, 그래프 등이 비선형 자료구조에 속한다. ## 리스트(List) - 자료구조 중 하나로, 순서가 있는 요소들의 모임 - 리스트의 특징 - 순서: 리스트의 요소들은 순서를 가지고 있어, 인덱스를 사용하여 접근할 수 있다. - 가변성: 리스트의 크기는 동적으로 변경할 수 있으며, 요소를 추가하거나 삭제할 수 있다. - 중복 허용: 리스트는 중복된 요소를 포함할 수 있다. ### 배열 리스트 (Array List) ![](https://i.imgur.com/2hFpsB8.png) - 내부 구조: **동적 배열** 을 사용하여 요소를 저장한다. - 조회 속도: 인덱스를 사용하여 요소에 빠르게 접근할 수 있다. - 추가/삭제 속도: 배열의 중간에 요소를 추가하거나 삭제하는 경우, 요소를 이동시켜야 하기 때문에 속도가 상대적으로 느리다. - 메모리 사용량: LinkedList에 비해 메모리 사용량이 적다. - 활용 시나리오: 조회가 빈번하고 추가/삭제가 상대적으로 적은 경우에 적합하다. ### Array List의 요소 추가 시 ![](https://i.imgur.com/qgYI2hx.png) ![](https://i.imgur.com/IoY9Iy0.png) 1. 메모리에 새로운 배열을 할당한다. 2. 기존 배열의 요소를 새로운 배열로 복사한다. 3. 새로운 요소를 추가한다. 4. 새로운 배열을 기존 배열로 설정한다. 5. 기존 배열을 메모리에서 해제한다. --- ### 연결 리스트 (Linked List) ![](https://i.imgur.com/glsQR75.png) - 내부 구조: **이중 연결 리스트를 사용**하여 요소를 저장한다. - 조회 속도: 인덱스를 사용하여 요소에 접근할 때, 처음이나 끝에서 순차적으로 탐색해야 하므로 속도가 상대적으로 느리다. - 추가/삭제 속도: 중간에 요소를 추가하거나 삭제하는 경우, 이웃하는 노드만 연결을 변경하면 되기 때문에 속도가 빠르다. (단, 인덱스를 찾는 과정에서의 탐색 시간은 제외) - 메모리 사용량: 각 노드가 이전 및 다음 노드의 참조를 유지하므로 메모리 사용량이 상대적으로 높다. - 활용 시나리오: 추가/삭제가 빈번하고 조회가 상대적으로 적은 경우에 적합하다. ### Linked List의 요소 삽입 시 ![](https://i.imgur.com/Ipb8Yso.png) 1. 새로운 노드를 생성한다. 2. 마지막 노드의 다음 노드를 새로운 노드로 설정한다. ## 스택(Stack) ![](https://i.imgur.com/Umpsupo.png) - LIFO(Last In, First Out) 원칙을 따르는 자료구조이다. - 가장 최근에 삽입된 요소가 가장 먼저 제거된다. - 함수 호출, 실행 콘택스트 관리등에 사용된다. - 예시: 웹 브라우저의 뒤로 가기 기능 --- ## 큐(Queue) ![](https://i.imgur.com/kkmqgUR.png) - FIFO(First In, First Out) 원칙을 따르는 자료구조이다. - 가장 먼저 삽입된 요소가 가장 먼저 제거된다. - 너비 우선 탐색(BFS), CPU 스케줄링, 캐시 구현 등에 활용된다. - 작업이 순차적으로 처리되어야 하는 상황에 적합하다. - 예시 : 은행의 대기열 ### 데크 (Deque, Deouble-Ended Queue) ![](https://i.imgur.com/caQ8hMC.png) - 양쪽 끝에서 요소의 삽입과 삭제가 가능한 자료구조이다. - 스택과 큐의 기능을 모두 포함하고 있다. - 양방향 연결 리스트로 구현할 수 있다. ## 셋(Set) - 중복을 허용하지 않는 자료구조 - 집합의 개념을 기반으로 하며, 모든 요소는 유일하다 - 데이터의 삽입, 삭제, 탐색이 빠르다 ### 트리 셋 (Tree Set) - 이진 검색 트리를 사용하여 셋(Set) 을 구현한 자료구조 - 이진 검색 트리를 사용하므로 정렬된 순서대로 저장한다. ### 해시 셋 (Hash Set) - 헤시테이블을 사용하여 셋(Set) 을 구현한 자료구조 - 헤시테이블을 사용하므로 순서를 유지하지 않는다 - 일반적으로 트리셋보다 빠른 검색 성능을 제공한다 ## 맵(Map) - Key(키)랑 Value(값)으로 나눠서 데이터 관리하는 자료구조 - 값에 대한 중복은 허용하나, 키에 대한 중복은 허용하지 않는다 - 순서가 보장되지 않는다 - 데이터의 삽입, 삭제, 탐색이 빠르다 - 트리 맵 (Tree Map) : Tree Set을 사용하여 Key를 관리하는 Map - 해시 맵 (Hash Map) : Hash Set을 사용하여 Key를 관리하는 Map ## 그래프 (Graph) ![](https://i.imgur.com/68Ij3Ri.png) - 노드(node)와 간선(edge)으로 이루어진 자료구조이다. - 노드는 그래프에서 위치를 나타내며, 간선은 노드들을 연결한다. - 간선은 방향이 있을 수도 있고 없을 수도 있다. 방향성이 없는 그래프를 무향 그래프(undirected graph), 방향성이 있는 그래프를 유향 그래프(directed graph)라고 한다. - 그래프는 실제로 다양한 상황에서 연결관계를 표현하고 분석하기 위한 자료구조이다. ## 트리 (Tree) ![](https://i.imgur.com/xjf4wfv.png) - 계층적인 구조를 가지며, 하나의 루트(root) 노드에서 시작하여 여러 개의 자식(child) 노드를 가질 수 있다. - 각 노드는 하나의 부모(parent) 노드와 여러 개의 자식(child) 노드를 가질 수 있다. - 트리는 실제로 계층적인 구조를 가진 데이터를 저장하고 탐색하기 위한 자료구조이다. ## 트리의 용어정리 ![](https://i.imgur.com/EOjU94e.png) - 노드의 차수 : 한노드가 가진 서브트리의 수 - 리프노드(단말,터미널) : 차수가 0인 노드 - 자식 노드 : 노드에 연결된 서브트리의 루트노드들 - 부모 노드 : 노드에 연결된 한단계 상위 레벨 노드 - 형제 노드 : 부모가 같은 노드 - 트리의 차수 : 트리노드들의 차수중 최대 차수 - 노드의 레벨 : 노드가 속한 트리의 깊이 - 트리의 높이 : 트리의 최대 레벨 ## 이진 트리(Binary Tree) ![](https://i.imgur.com/sRqRXRI.png) - 자식노드가 최대 두 개인 노드들로 구성된 트리 - 자식 노드를 각각 왼쪽 자식 노드와 오른쪽 자식 노드라고 부른다 - 순회방법에 따라 전위순회, 중위순회, 후위순회라고 부른다 - 전위 순회(Pre-order Traversal) - ![](https://i.imgur.com/QtYxrTN.png) - 중위 순회(In-order Traversal): - ![](https://i.imgur.com/7l4gkf6.png) - 특징: 이진 탐색트리에서 정렬된 순서로 노드를 방문한다. - 후위 순회(Post-order Traversal): - ![](https://i.imgur.com/BqqRLFb.png) ### 이진 트리의 종류 ![](https://i.imgur.com/KxFrqRs.png) - 포화 이진 트리(Full binary tree) - 모든 레벨에서 노드들이 모두 채워진 트리 - 완전 이진 트리(Complete binary tree) - 왼쪽부터 노드가 순서대로 채워진 이진트리 - 편향 트리(Skewed binary tree) - 왼쪽 또는 오른쪽 서브트리만 가지는 트리 - 최악의 경우에는 모든 노드를 탐색해야 하므로 시간 복잡도는 O(n)이 된다 ### 이진 탐색 트리(Binary Search Tree, BST) ![](https://i.imgur.com/JXiCwCZ.png) - 이진 탐색과 트리구조를 결합한 자료구조 - 이진탐색의 효율적인 탐색 능력을 유지하면서도, 빈번한 자료 입력과 삭제를 가능하게끔 고안됐다 - 특징 - 검색 속도가 빠르다. O(logn) - 삽입 및 삭제 연산이 효율적이다. O(logn) - 이진 탐색트리의 조건 - 각 노드의 왼쪽 서브트리에는 해당 노드의 값보다 작은 값을 지닌 노드로 이루어져 있다. - 각 노드의 오른쪽 서브트리에는 해당 노드의 값보다 큰 값을 지닌 노드로 이루어져 있다. - 중복된 노드가 없어야 한다. - 왼쪽 서브트리, 오른쪽 서브트리 또한 이진탐색트리이다. > 이진 탐색트리의 높이가 매우 높아지면(예: 한쪽으로 치우친 트리), 검색, 삽입, 삭제 연산의 성능이 저하되는데, 이 경우, 균형 잡힌 이진 탐색트리(AVL 트리, 레드-블랙 트리 등)를 사용하여 성능을 개선할 수 있다. ## 해시 (Hash) ![](https://i.imgur.com/sn2oLVl.png) - 해시: 데이터를 고유한 고정 길이의 값으로 변환하는 과정이다. - 해시 함수: 임의의 길이의 데이터를 고정된 길이의 데이터로 출력하는 함수이다 - 예를 들면 어떤 숫자를 10으로 나누었을 때 그 나머지를 구하는 함수도 일종의 해시 함수이다 - 특징 - 어떤 입력 값에도 항상 고정된 길이의 해시값을 출력한다 - 동일한 입력에 대해 항상 동일한 해시 값을 생성한다 - 입력 값의 아주 일부만 변경되어도 전혀 다른 결과 값을 출력한다 - 출력된 결과 값을 토대로 입력값을 유추할 수 없다 - 해시 값에서 원본 데이터를 쉽게 찾아낼 수 없다 - 해시 함수는 빠르게 해시 값을 생성한다 ## 해시 테이블 ![](https://i.imgur.com/BMbOWWM.png) - 해시 테이블은 컴퓨터 과학에서 매우 효율적인 자료 구조 중 하나로, 키(key)와 값(value)의 쌍을 저장하고 검색하기 위해 사용된다 - 해시 테이블은 해시 함수를 사용하여 키를 해시 코드(hash code)로 변환한 후, 이 해시 코드를 배열의 인덱스로 사용하여 값을 저장하거나 검색한다 - 해시함수를 사용하므로 해시 테이블은 일반적으로 빠른 검색, 삽입 및 삭제 작업을 제공한다 ### 해시 충돌 ![](https://i.imgur.com/Y7ocC1m.png) - 서로 다른 key값에 대해 동일한 해시 값이 도출하는 현상 - 해시 함수 자체의 문제보다는 해시 테이블 크기의 물리적 한계로 인한 경우가 많다 - input으로 들어올 수 있는 key값은 무한하지만 해시 함수로 도출된 해시 값을 저장할 물리적 공간은 유한하기 때문 - 일반적인 충돌 해결 방법은 체이닝(Chaining)과 오픈 어드레싱(Open addressing)이 있다. ### 체이닝 (Chaining) ![](https://i.imgur.com/HVz6NJa.png) - 각 인덱스에 연결 리스트를 사용하여 동일한 해시 코드를 가진 키와 값의 쌍을 저장한다. 충돌이 발생하면 해당 인덱스의 연결 리스트에 새로운 항목을 추가한다 ### 오픈 어드레싱 (Open Addressing) ![](https://i.imgur.com/0DbXgzK.png) - 충돌이 발생하면 다른 위치를 찾아 새로운 항목을 저장하는 방법아다. 선형 탐사, 제곱 탐사, 이중 해싱 등 여러 가지 오픈 어드레싱 방법이 있다 # Java Collection Framework ![](https://i.imgur.com/26IEkQ1.png) > 자바 컬렉션 프레임워크에는 그래프(Graph)와 관련된 직접적인 클래스나 인터페이스가 없는 이유는 그래프가 다양한 구현 방법과 활용 방식을 가지고 있어 범용적인 클래스나 인터페이스로 정의하기 어렵기 때문이다. ## List ```java List<String> list = new ArrayList<>(); // List<String> list = new LinkedList<>(); list.add("apple"); list.add("banana"); list.add("cherry"); System.out.println(list.get(1)); ``` ## Stack ```java Stack<String> stack = new Stack<>(); stack.push("apple"); stack.push("banana"); stack.push("cherry"); System.out.println(stack.pop()); System.out.println(stack.pop()); ``` ## Queue ```java Queue<String> queue = new LinkedList<>(); queue.add("apple"); queue.add("banana"); queue.add("cherry"); System.out.println(queue.poll()); System.out.println(queue.poll()); ``` ## Set ```java Set<String> set = new HashSet<>(); // Set<String> set = new TreeSet<>(); set.add("apple"); set.add("banana"); set.add("cherry"); set.add("apple"); for (String item : set) { System.out.println(item); } ``` ## Map ```java Map<String, Integer> map = new HashMap<>(); // Map<String, Integer> map = new TreeMap<>(); map.put("apple", 3); map.put("banana", 5); map.put("cherry", 2); System.out.println(map.get("banana")); ```