Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions DataStructure/그래프/그래프_예지.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
## 📖 학습 주제

- **주제**: 그래프
- **날짜**: 2024.10.21 - 2024.10.27

---

## 📌 학습 내용 요약

### [그래프]

- `그래프`: 정점이라 불리는 데이터를 간선 혹은 링크로 연결한 형태의 자료구조
- 트리와의 차이점
- 트리는 사이클을 형성하지 않고 연결된 노드 간에 상하 관계를 가짐
- 그래프는 사이클을 형성할 수 있고, 정점끼리 상하 관계를 갖지 않음
- 그래프의 유형
- `연결/비연결 그래프`
- `방향/무방향 그래프`
- `가중치 그래프`
- `서브그래프`

---

### 그래프의 유형

- `연결 그래프` : 그래프 상에 있는 임의의 두 정점 사이의 경로가 존재하는 그래프
- 2개의 아무 정점이나 골라 간선으로 이을 수 있으면 `연결 그래프`
- `비연결 그래프` : 어떤 정점 사이에는 경로가 존재하지 않을 수도 있는 그래프

![](https://velog.velcdn.com/images/yjlee0006/post/bc18eb16-75ba-40a8-bada-08ddf4d1ccce/image.png)

- `방향그래프` : 간선에 방향이 있는 그래프
- `무방향그래프` : 간선에 방향이 없는 그래프

![](https://velog.velcdn.com/images/yjlee0006/post/bc2e8585-5aa1-497a-884c-3675e47632aa/image.png)

- `가중치 그래프` : 간선에 가중치가 부여된 그래프
- 간선에 부여된 값 = 가중치 = 비용

![](https://velog.velcdn.com/images/yjlee0006/post/a6aa0a61-6602-403a-987b-3e1ca9d7b416/image.png)

- `서브 그래프` : 특정 그래프의 정점과 간선의 일부분으로 이루어진 그래프

![](https://velog.velcdn.com/images/yjlee0006/post/2406e7ab-5836-43a9-b2fe-11c7be1d60d3/image.png)

---

### 그래프의 표현

- **인접 행렬 기반 그래프 표현** : N x N 크기의 행렬로 그래프를 표현하는 방법

- N은 정점의 개수
- <행, 열> 값은 <출발정점, 도착정점>

- **인접 리스트 기반 그래프 표현** : 그래프의 특정 정점과 연결된 정점들을 연결 리스트로 표현하는 방법

![](https://velog.velcdn.com/images/yjlee0006/post/05b047c7-8d77-4451-9f29-e6b93bc193fd/image.png)

---

### 탐색 알고리즘

-**깊이 우선 탐색(DFS)** : 더 이상 방문 가능한 정점이 없을 때까지 최대한 깊이 탐색하기를 반복하는 탐색방법

![](https://velog.velcdn.com/images/yjlee0006/post/d459f4c0-998c-4b3e-b455-89cd56ce0667/image.png)

- **너비 우선 탐색(BFS)** : 최대한 넓게 탐색하기를 반복하는 방법, 인접한 모든 정점들을 방문하고, 방문한 정점들과 연결된 모든 정점을 방문, 방문한 정점들과 연결된 모든 정점들을 방문하기를 반복하는 탐색 방법

- DFS -> 배열 / 스택 사용
- 배열 : 특정정점의 방문 여부 확인
- 스택 : 방문 중 뒤로가기가 필요한 경우
- BFS -> 배열 / 큐 사용

- 배열 : 특정정점의 방문 여부 확인
- 큐 : 연결된 정점들을 저장하기 위해 사용

### 최단 경로 알고리즘

- 한 정점에서 목적지 정점까지이르는 가중치의 합이 최소가 되는 경로

- 활용

- 지도 서비스(목적지까지 이르는 최단거리)

- 종류
- **다익스트라 알고리즘** : 가중치가 음이 아닌 수라는 가정하에 사용가능. 특정 정점에서 다른 모든 정점까지의 최단 거리를 구해 주는 알고리즘.

---

## 📈 추가 학습 필요

알고리즘 구현에 자주 나오는 DFS에 대해 조금 더 자세히 공부해 보았다.

### DFS

- **장점**
- 현 경로상의 노드들만 기억하면 되므로 저장공간 수요가 비교적 적다.
- 목표 노드가 깊은 단계에 있을 경우 해를 빨리 구할 수 있다.

<br>

- **단점**
- 해가 없는 경로가 깊을 경우 탐색시간이 오래 걸릴 수 있다.
- 얻어진 해가 최단 경로가 된다는 보장이 없다.
- 깊이가 무한히 깊어지면 스택오버플로우가 날 위험이 있다. (깊이 제한을 두는 방법으로 해결가능)

<br>

- DFS의 구현

- 방법에 따른 구현 차이

- 인접 행렬로 구현

```python
def dfs(now, adjacent, visited):
visited[now] = True
N = len(adjacent)

for i in range(N):
if adjacent[now][i] == 0:
continue

if not visited[i]:
dfs(i, adjacent, visited)

# 사용 예시
# adjacent = [[0, 1, 0], [1, 0, 1], [0, 1, 0]] # 인접 행렬 예시
# visited = [False] * len(adjacent) # 방문 여부 리스트 초기화
# dfs(0, adjacent, visited) # 정점 0에서 DFS 시작
```

> DFS 하나당 N번의 loop를 돌게 되므로 O(n)의 시간복잡도를 가진다. 그런데 N개의 정점을 모두 방문 해야하므로 n\*O(n)이므로 O(n^2)의 시간복잡도를 가지게 된다.

- 인접 리스트로 구현

```python
def dfs(now, adjacent, visited):
visited[now] = True

for next in adjacent[now]:
if not visited[next]:
dfs(next, adjacent, visited)

# 사용 예시
# adjacent = [[1, 2], [0, 2], [0, 1]] # 인접 리스트 예시
# visited = [False] * len(adjacent) # 방문 여부 리스트 초기화
# dfs(0, adjacent, visited) # 정점 0에서 DFS 시작
```

> DFS가 총 N번 호출되긴 인접 리스트로 구현하게 되면 DFS하나당 각 정점에 연결되어 있는 간선의 개수만큼 탐색을 하게 되므로 예측이 불가능 하다. 모든 정점을 한번씩 다 방문하고, 모든 간선을 한번씩 모두 검사했다고 할 수 있으므로 O(n+e)의 시간 복잡도를 가지게 된다.

---

## 💡 참고 자료

- https://currygamedev.tistory.com/10
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
## 📖 학습 주제

- **주제**: 배열과 연결리스트
- **날짜**: 2024.10.21 - 2024.10.27

---

## 📌 학습 내용 요약

### <span style='background-color: #ede6b1'>[배열]</span>
- **`배열(array)`** : 일정한 메모리 공간을 차지하는 여러 요소들이 순차적으로 나열된 자료구조
- 배열의 종류
- 일차원 배열 : 한 쪽 방향으로 요소가 나열
- 이차원 배열 : 배열 속에 배열이 포함된 경우
- 삼차원 배열 : 배열 속에 배열 속에 배열이 포함된 경우
- 정적 배열 : 프로그램을 실행하기 전에 크기가 고정되어 있는 배열
- 동적 배열 : 실행 과정에서 크기가 변할 수 있는 배열
- 활용도
- 관련 있는 데이터를 일렬로 나열하여 관리하고자 할 때
- 다른 자료구조나 알고리즘을 구현하고자 할 때

<br>

- **`인덱스(index)`** : 0부터 시작하는 고유한 순서 번호
- 빅 오 표기법으로 표현
- 인덱스를 바탕으로 배열의 특정요소에 접근/수정하는 시간 : **`O(1)`**
- 앞에서 부터 차례대로 특정 요소가 있는지 찾는 연산 : **`O(n)`**
-> 요소의 갯수와 무관하게 일정하다.
- 특정 요소를 추가하거나 삭제하는 경우 : **`O(n)`**
-> 추가 혹은 삭제된 요소로 인해 이후의 요소들이 이동되어야 하기 때문!

---

### <span style='background-color: #ede6b1'>[연결 리스트]</span>
- **`연결 리스트(linked list)`** : 노드의 모음으로 구성된 자료구조
- 연결 리스트의 특징 : 모든 노드는 반드시 메모리 내에 순차적으로 저장되어 있을 필요 X
-> 연속적으로 구성되어 있는 데이터를 불연속적으로 저장할 떄 유용
- 연결 리스트의 종류
- `싱글 연결 리스트` : 노드 내 다음 노드의 위치 정보가 저장되어 한쪽 방향으로 꼬리를 무는 형태(데이터 + 다음노드)
- 단점 : 다음노드의 위치만 알 수 있으며, 이전노드의 위치는 알기 어려움
- `이중 연결 리스트` : 이전노드 + 데이터 + 다음노드
- 장점 : 양방향 탐색이 가능함
- 단점 : 저장공간이 더 필요함, 한 노드에 2개의 위치정보를 저장해야하기 때문
<img src = https://velog.velcdn.com/images/yjlee0006/post/16491f65-620a-4277-8d49-97ed167172a7/image.png width="600" height="100">
- `환형 연결 리스트(원형 연결 리스트)` : 꼬리노드가 헤드노드를 가리켜 노드들이 원형으로 구성된 연결리스트
- `이중 연결리스트 + 환형 연결 리스트` : 헤드노드의 이전 노드가 꼬리노드 가리킴/ 꼬리노드의 다음노드가 헤드노드를 가리킴
- 장점 : 모든 노드데이터를 여러 차례 순회해야할 때 유용하게 활용할 수 있음
<img src = https://velog.velcdn.com/images/yjlee0006/post/4fd7335c-61d1-4ee0-a8c2-e5c2dafacbcd/image.png width="600" height="100">

<br>

- **`노드(node)`** : 저장하고자 하는 데이터와 다음 노드의 위치(메모리 상의 주소) 정보를 포함하는 연결 리스트의 구성 단위
<img src="https://velog.velcdn.com/images/yjlee0006/post/6286213c-791e-45e5-986a-719ccfb7bb1c/image.png" width="200" height="100">

- 연결리스트의 형태
- `헤드(head)` : 연결리스트의 첫 번째 노드
- `꼬리(tail)` : 연결리스트의 마지막 노드

<br>

- 빅 오 표현법으로 표현
- 데이터가 몇 번째 노드에 있는지 알고, 특정 요소에 접근할 때 : **`O(n)`**
-> 앞에서부터 순차적으로 접근할 수 밖에 없기 때문

<img src = "https://velog.velcdn.com/images/yjlee0006/post/16e4e8d9-c187-4702-afdc-b555be658ee4/image.png" width="600" height = "100">

- 중간에 요소를 추가하거나 삭제할 때 : **`O(1)`**
-> 노드의 위치만 바꿔서 저장, 재정렬이 불필요하기 때문
-> 삽입/삭제할 위치만 주어지면 노드에 접근하는 시간이 동일

<img src = "https://velog.velcdn.com/images/yjlee0006/post/1dac9a1a-aded-46bf-a1c6-087a7aff9d79/image.png" width="600" height = "300">



---

## 📈 추가 학습 필요
연결리스트와 배열을 어떤 상황에서 사용해야하는지, 그리고 각각 사용했을 때의 장점과 단점을 잘 모르겠다.

### [연결 리스트]
- 👍 효율적인 상황
- 데이터의 동적 추가/삭제가 필요한 경우. 재정렬이 필요없기 때문.
- 👎🏻 비효율적인 상황
- **데이터 검색에 비효율적**
-> 특정 위치의 노드를 검색하기 위해서 처음부터 차례대로 접근해야하기 때문
- **메모리 오버헤드가 발생할 수 있다.**
-> 각 노드는 데이터 필드와 함께 메모리 주소 필드(링크필드)를 가지고 있어야 한다. 이 링크 필드는 추가적인 메모리를 사용하므로, 연결 리스트는 배열에 비해 메모리 오버헤드가 크다고 볼 수 있다.
- **역방향 탐색의 어려움**
-> 이중 연결 리스트를 통해 해소할 수 있지만, 또다른 메모리 오버헤드를 발생시킬 수 있다.
- **복잡한 구현**
-> 배열에 비해 복잡하다. 노드의 삽입, 삭제 시에 링크를 정확히 관리해야 하므로 버그가 발생할 가능성이 더 높다.

### [배열]
- 👍 효율적인 상황

- 빠른 데이터 접근: 인덱스를 통해 특정 요소에 즉시 접근할 수 있어, 접근 시간이 𝑂(1)로 매우 빠르다.
- 메모리 연속성: 배열은 메모리 내에서 연속적으로 저장되므로, 캐시 효율성이 높아 성능이 좋다.
- 간단한 구현: 배열은 구조가 단순하여 구현이 쉽고, 기본적인 데이터 구조로 많이 사용된다.
- 👎🏻 비효율적인 상황

- 고정된 크기: 정적 배열의 경우, 크기가 고정되어 있어 실행 중에 크기를 변경할 수 없다. 이로 인해 메모리 낭비가 발생할 수 있다.
- 데이터 추가/삭제의 비효율성: 배열의 중간에 요소를 추가하거나 삭제할 경우, 이후의 요소들을 이동해야 하므로 시간 복잡도가 𝑂(𝑛)이 된다.
- 메모리 낭비: 동적 배열을 사용할 경우, 배열의 크기를 늘리기 위해 추가 메모리를 할당해야 하며, 이 과정에서 메모리 낭비가 발생할 수 있다.


---

## 💡 참고 자료

- https://velog.io/@hyhy9501/5-1-Linked-List-%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4
70 changes: 70 additions & 0 deletions DataStructure/스택과 큐/스택과 큐_예지.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
## 📖 학습 주제

- **주제**: 스택과 큐
- **날짜**: 2024.10.21 - 2024.10.27

---

## 📌 학습 내용 요약

### <span style='background-color: #ede6b1'>[스택]</span>
- **`스택(stack)`** : 한쪽에서만 데이터의 삽입 및 삭제가 가능한 자료구조
- 스택 연산
- `푸시(push)` : 데이터를 저장하는 연산
- `팝(pop)` : 데이터를 빼네는 연산

> ✅ **후입선출(LIFO)**
최근에 호출된 함수의 매개변수가 가장 먼저 활용되고, 가장 먼저 메모리에서 삭제됨

- 활용
- 최근에 임시 저장한 데이터를 가장 먼저 활용해야하는 경우(뒤로가기)
- undo
- 후위 표기법 계산
---

### <span style='background-color: #ede6b1'>[큐]</span>
- **`큐(queue)`** : 한쪽으로 데이터를 삽입하고, 다른 한쪽으로 데이터를 삭제할 수 있는 자료구조
- 큐 연산
- `인큐(enqueue)` : 큐의 한쪽 끝에 데이터를 삽입하는 연산
- `디큐(dequeue)` : 다른 한쪽 끝으로 데이터를 빼내는(삭제) 연산

![](https://velog.velcdn.com/images/yjlee0006/post/248172de-2e3c-4c0e-955e-4feea17a6982/image.png)

> ✅ **선입선출(FIFO)**
가장 먼저 삽입된 데이터가 가장 먼저 삭제됨
- 활용
- 임시 저장된 데이터를 차례차례 내보내거나 꺼내와야하는 `버퍼`(줄세우기)
- 은행업무
- 대기열
- 변형된 큐의 형태
- `원형 큐` : 데이터를 삽입하는 쪽과 삭제하는 쪽 - 양쪽을 하나로 연결해 원형으로 사용
- `덱(Double-Ended Queue)` : 양쪽으로 데이터를 삽입/삭제 할 수 있는 큐
- `우선순위 큐` : 요소의 저장 순서와 무관하게 우선순위가 높은 순서대로 빠져나가는 큐

---

## 📈 추가 학습 필요
스택은 여러 알고리즘 구현에서 활용되는데 어떤 유형에서 주로 활용되는지 궁금해서 찾아보았다.
또한 기술면접에서 큐의 경우 우선순위 큐에 대해 많이 물어보기 때문에 조금 더 학습했다.

### stack
- 대표유형 : 미로찾기
DFS 알고리즘을 수행하는데 `Stack`을 사용할 수 있음
DFS는 `stack`을 이용해 경로를 관리함.
1. 현재 위치에서 가능한 방향의 좌표를 `stack`에 추가
2. `stack`의 가장 위 좌표로 이동(반복)
3. 더 이상 갈 곳이 없으면 `stack`에서 마지막 경로를 제거하고 이전 위치로 이동
4. 탐색 중 출구를 만나면 종료 - `stack`이 비어 있으면 더 이상 갈 곳이 없다는 의미로, 출구를 찾지 못한 상황

### 우선순위 큐
- 구현방식
- 배열, 연결 리스트, 힙
- 효율적인 구현방식
- `힙 방식`
worst case라도 시간 복잡도 O(logN)을 보장하기 때문에 일반적으로 완전 이진트리 형태의 힙을 이용해 구현

---

## 💡 참고 자료
- https://velog.io/@kangdev/%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-Stack-Queue
- https://dev-coco.tistory.com/159
Loading