diff --git a/JooKangSan/[week7]Graph/Find_Center_of_Star_Graph.js b/JooKangSan/[week7]Graph/Find_Center_of_Star_Graph.js new file mode 100644 index 0000000..7ae929d --- /dev/null +++ b/JooKangSan/[week7]Graph/Find_Center_of_Star_Graph.js @@ -0,0 +1,10 @@ +/** + * @param {number[][]} edges + * @return {number} + */ +function findCenter(edges) { + const [a, b] = edges[0]; + const [c, d] = edges[1]; + + return a === c || a === d ? a : b; +} \ No newline at end of file diff --git a/JooKangSan/[week7]Graph/Find_if_Path_Exists_in_Graph.js b/JooKangSan/[week7]Graph/Find_if_Path_Exists_in_Graph.js new file mode 100644 index 0000000..3815168 --- /dev/null +++ b/JooKangSan/[week7]Graph/Find_if_Path_Exists_in_Graph.js @@ -0,0 +1,40 @@ +/** + * @param {number} n + * @param {number[][]} edges + * @param {number} source + * @param {number} destination + * @return {boolean} + */ +function validPath(n, edges, source, destination) { + const graph = new Map(); + + for (let i = 0; i < n; i++) { + graph.set(i, []); + } + + for (const [u, v] of edges) { + graph.get(u).push(v); + graph.get(v).push(u); + } + + const visited = new Array(n).fill(false); + const queue = [source]; + visited[source] = true; + + while (queue.length > 0) { + const current = queue.shift(); + + if (current === destination) { + return true; + } + + for (const neighbor of graph.get(current)) { + if (!visited[neighbor]) { + visited[neighbor] = true; + queue.push(neighbor); + } + } + } + + return false; +} \ No newline at end of file diff --git a/JooKangSan/[week7]Graph/Find_the_Town_Judge.js b/JooKangSan/[week7]Graph/Find_the_Town_Judge.js new file mode 100644 index 0000000..25e84fe --- /dev/null +++ b/JooKangSan/[week7]Graph/Find_the_Town_Judge.js @@ -0,0 +1,21 @@ +/** + * @param {number} n + * @param {number[][]} trust + * @return {number} + */ +function findJudge(n, trust) { + const trustScores = new Array(n + 1).fill(0); + + for (const [a, b] of trust) { + trustScores[a] -= 1; + trustScores[b] += 1; + } + + for (let i = 1; i <= n; i++) { + if (trustScores[i] === n - 1) { + return i; + } + } + + return -1; +} \ No newline at end of file diff --git a/JooKangSan/[week7]Graph/Graph.md b/JooKangSan/[week7]Graph/Graph.md new file mode 100644 index 0000000..531ac89 --- /dev/null +++ b/JooKangSan/[week7]Graph/Graph.md @@ -0,0 +1,148 @@ +# 그래프 (Graph) 이론 + +## 1. 그래프의 정의 +- 정점(Vertex)과 간선(Edge)으로 구성된 데이터 구조 +- 객체 간의 관계를 표현하는 추상적 네트워크 + +## 3. 그래프의 종류 +### 3.1 방향성에 따른 분류 +1. 방향 그래프(Directed Graph) + - 간선에 방향성 존재 + - 일방향 관계 표현 + +2. 무방향 그래프(Undirected Graph) + - 간선에 방향성 없음 + - 양방향 관계 표현 + +### 3.2 연결성에 따른 분류 +1. 연결 그래프(Connected Graph) + - 모든 정점이 서로 연결됨 + +2. 비연결 그래프(Disconnected Graph) + - 일부 정점이 연결되지 않음 + +### 3.3 특수한 그래프 +- 트리(Tree) +- 완전 그래프(Complete Graph) +- 이분 그래프(Bipartite Graph) + +## 4. 그래프 표현 방법 +### 4.1 인접 행렬(Adjacency Matrix) +```javascript +class GraphAdjacencyMatrix { + constructor(vertices) { + this.vertices = vertices; + this.matrix = Array(vertices).fill().map(() => Array(vertices).fill(0)); + } + + addEdge(v1, v2) { + this.matrix[v1][v2] = 1; + this.matrix[v2][v1] = 1; // 무방향 그래프의 경우 + } +} +``` + +### 4.2 인접 리스트(Adjacency List) +```javascript +class GraphAdjacencyList { + constructor() { + this.adjacencyList = {}; + } + + addVertex(vertex) { + if (!this.adjacencyList[vertex]) { + this.adjacencyList[vertex] = []; + } + } + + addEdge(vertex1, vertex2) { + this.adjacencyList[vertex1].push(vertex2); + this.adjacencyList[vertex2].push(vertex1); // 무방향 그래프 + } +} +``` + +## 5. 그래프 탐색 알고리즘 +### 5.1 깊이 우선 탐색 (DFS) +```javascript +function dfs(graph, startVertex) { + const visited = new Set(); + + function explore(vertex) { + visited.add(vertex); + console.log(vertex); + + for (let neighbor of graph.adjacencyList[vertex]) { + if (!visited.has(neighbor)) { + explore(neighbor); + } + } + } + + explore(startVertex); +} +``` + +### 5.2 너비 우선 탐색 (BFS) +```javascript +function bfs(graph, startVertex) { + const visited = new Set(); + const queue = [startVertex]; + visited.add(startVertex); + + while (queue.length > 0) { + const currentVertex = queue.shift(); + console.log(currentVertex); + + for (let neighbor of graph.adjacencyList[currentVertex]) { + if (!visited.has(neighbor)) { + visited.add(neighbor); + queue.push(neighbor); + } + } + } +} +``` + +## 6. 최단 경로 알고리즘 +### 6.1 다익스트라 알고리즘 +```javascript +function dijkstra(graph, startVertex) { + const distances = {}; + const previous = {}; + const pq = new PriorityQueue(); + + // 초기화 로직 + for (let vertex in graph.adjacencyList) { + if (vertex === startVertex) { + distances[vertex] = 0; + pq.enqueue(vertex, 0); + } else { + distances[vertex] = Infinity; + } + previous[vertex] = null; + } + + // 최단 경로 탐색 로직 + while (!pq.isEmpty()) { + let currentVertex = pq.dequeue().val; + + for (let neighbor in graph.adjacencyList[currentVertex]) { + // 거리 계산 및 업데이트 로직 + } + } + + return { distances, previous }; +} +``` + +## 7. 그래프 문제 주의 사항 +1. 그래프 표현 방법 선택 +2. 적절한 탐색 알고리즘 적용 +3. 방문 체크로 무한 루프 방지 +4. 메모리 효율성 고려 + +## 8. 시간 복잡도 +- DFS/BFS: O(V + E) +- 다익스트라: O((V + E)logV) +- 플로이드-워셜: O(V³) \ No newline at end of file diff --git a/JooKangSan/[week7]Graph/Node_With_Highest_Edge_Score.js b/JooKangSan/[week7]Graph/Node_With_Highest_Edge_Score.js new file mode 100644 index 0000000..f25b8ac --- /dev/null +++ b/JooKangSan/[week7]Graph/Node_With_Highest_Edge_Score.js @@ -0,0 +1,20 @@ +function edgeScore(edges) { + const n = edges.length; + const scores = new Array(n).fill(0); + + for (let i = 0; i < n; i++) { + scores[edges[i]] += i; + } + + let maxScore = 0; + let maxNode = 0; + + for (let i = 0; i < n; i++) { + if (scores[i] > maxScore) { + maxScore = scores[i]; + maxNode = i; + } + } + + return maxNode; +} \ No newline at end of file diff --git a/JooKangSan/[week7]Graph/Shortest_distance_on_game_map.js b/JooKangSan/[week7]Graph/Shortest_distance_on_game_map.js new file mode 100644 index 0000000..0a77e24 --- /dev/null +++ b/JooKangSan/[week7]Graph/Shortest_distance_on_game_map.js @@ -0,0 +1,37 @@ +function solution(maps) { + const n = maps.length; + const m = maps[0].length; + + const visited = Array.from(Array(n), () => Array(m).fill(false)); + + const dx = [-1, 1, 0, 0]; + const dy = [0, 0, -1, 1]; + + const queue = [[0, 0, 1]]; + visited[0][0] = true; + + while (queue.length > 0) { + const [x, y, count] = queue.shift(); + + if (x === n - 1 && y === m - 1) { + return count; + } + + for (let i = 0; i < 4; i++) { + const nx = x + dx[i]; + const ny = y + dy[i]; + + if ( + nx >= 0 && nx < n && + ny >= 0 && ny < m && + maps[nx][ny] === 1 && + !visited[nx][ny] + ) { + visited[nx][ny] = true; + queue.push([nx, ny, count + 1]); + } + } + } + + return -1; +} \ No newline at end of file diff --git a/JooKangSan/[week8]BackTracking/BackTracking.md b/JooKangSan/[week8]BackTracking/BackTracking.md new file mode 100644 index 0000000..4b62d63 --- /dev/null +++ b/JooKangSan/[week8]BackTracking/BackTracking.md @@ -0,0 +1,102 @@ +# 백트래킹 (Backtracking) + +## 1. 백트래킹 +- 백트래킹은 모든 가능한 경우의 수를 탐색하는 알고리즘 기법 +- 문제 해결을 위해 모든 가능한 경우를 시도 후, 유망하지 않은 경우는 즉시 포기하는 방식 +- 깊이 우선 탐색(DFS)의 일종으로 볼 수 있음 + +## 2. 백트래킹의 핵심 특징 +1. 완전 탐색: 모든 가능한 경우의 수를 탐색 +2. 가지치기(Pruning)**: 불필요한 경로를 빠르게 제거 +3. 재귀적 접근: 문제를 작은 하위 문제로 분해 +4. 상태 되돌리기: 탐색 후 이전 상태로 복귀 + +## 3. 백트래킹 알고리즘의 일반적인 구조 +```javascript +function backtrack(현재상태, 다른매개변수) { + // 기저 조건: 문제의 해결책을 찾았을 때 + if (문제해결조건) { + 결과에 추가 + return + } + + // 현재 상태에서 가능한 모든 선택지 탐색 + for (각 선택지) { + // 선택지 추가 + 현재상태에 선택지 추가 + + // 재귀적 탐색 + backtrack(변경된상태) + + // 백트래킹: 상태 되돌리기 + 현재상태에서 선택지 제거 + } +} +``` + +## 4. 백트래킹의 대표적인 문제 유형 +1. **순열 생성** + - 모든 가능한 순서 조합 찾기 + - 예: 숫자 배열의 모든 순열 + +2. **조합 생성** + - 특정 길이의 조합 찾기 + - 예: n개 중 k개 선택 +3. **부분집합 생성** + - 주어진 집합의 모든 부분집합 생성 + +4. **제약 조건 문제** + - N-Queen 문제 + - 스도쿠 풀이 + - 괄호 생성 + +## 5. 백트래킹 vs 완전 탐색 +- **완전 탐색**: 모든 경우의 수를 무조건 탐색 +- **백트래킹**: 불필요한 경로를 효율적으로 제거 + +## 6. 시간 복잡도 +- 최악의 경우: O(2^n) 또는 O(n!) +- 가지치기를 통해 실제 시간 복잡도는 줄어 들 수 있음 + +## 7. 주의사항 +- 재귀 깊이에 주의 (스택 오버플로우 위험) +- 상태 되돌리기(백트래킹) 로직 명확히 구현 +- 문제의 특성에 맞는 가지치기 전략 필요 +- 메모리 사용에 유의 +- 기저 조건 정확히 설정 + +## 8. JavaScript 백트래킹 예제 +```javascript +function solution(n, m) { + // 순열을 계산하고자 하는 원소가 담긴 배열 [1, 2, 3, 4] + let arr = Array.from({ length: n }, (v, i) => i + 1); + // 각 원소의 인덱스 별 방문 여부 + let visited = new Array(n).fill(false); + // 현재 순열에 포함된 원소 + let selected = []; + + let answer = ""; + function dfs(arr, depth) { + // 모든 순열을 확인하는 부분 + if (depth === m) { + let result = []; // 순열 결과 저장 + for (let i of selected) result.push(arr[i]); + for (let x of result) answer += x + " "; + answer += "\n"; // 줄바꿈 처리 + return; + } + for (let i = 0; i < arr.length; i++) { + if (visited[i]) continue; // 이미 처리된 원소라면 무시하고 넘어감 + selected.push(i); // 현재 원소 선택 + visited[i] = true; // 방문처리 + dfs(arr, depth + 1); // 재귀함수 호출 + selected.pop(); // 현재 원소 선택 취소 + visited[i] = false; // 원소 방문 처리 취소 + } + } + dfs(arr, 0); + return answer; +} + +console.log(solution(4, 2)); +``` diff --git a/JooKangSan/[week8]BackTracking/Combination_Sum.js b/JooKangSan/[week8]BackTracking/Combination_Sum.js new file mode 100644 index 0000000..966d9b2 --- /dev/null +++ b/JooKangSan/[week8]BackTracking/Combination_Sum.js @@ -0,0 +1,30 @@ +/** + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ +var combinationSum = function(candidates, target) { + const result = []; + + const backtrack = (start, current, remaining) => { + if (remaining === 0) { + result.push([...current]); + return; + } + + if (remaining < 0) { + return; + } + + for (let i = start; i < candidates.length; i++) { + current.push(candidates[i]); + + backtrack(i, current, remaining - candidates[i]); + + current.pop(); + } + }; + + backtrack(0, [], target); + return result; +}; \ No newline at end of file diff --git a/JooKangSan/[week8]BackTracking/Combinations.js b/JooKangSan/[week8]BackTracking/Combinations.js new file mode 100644 index 0000000..32e84d3 --- /dev/null +++ b/JooKangSan/[week8]BackTracking/Combinations.js @@ -0,0 +1,26 @@ +/** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ +var combine = function(n, k) { + const result = []; + + const backtrack = (start, current) => { + if (current.length === k) { + result.push([...current]); + return; + } + + for (let i = start; i <= n; i++) { + current.push(i); + + backtrack(i + 1, current); + + current.pop(); + } + }; + + backtrack(1, []); + return result; +}; diff --git a/JooKangSan/[week8]BackTracking/Generate_Parentheses.js b/JooKangSan/[week8]BackTracking/Generate_Parentheses.js new file mode 100644 index 0000000..8bdeeea --- /dev/null +++ b/JooKangSan/[week8]BackTracking/Generate_Parentheses.js @@ -0,0 +1,25 @@ +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function(n) { + const result = []; + + const backtrack = (open, close, current) => { + if (current.length === 2 * n) { + result.push(current); + return; + } + + if (open < n) { + backtrack(open + 1, close, current + '('); + } + + if (close < open) { + backtrack(open, close + 1, current + ')'); + } + }; + + backtrack(0, 0, ''); + return result; +}; \ No newline at end of file diff --git a/JooKangSan/[week8]BackTracking/Permutations.js b/JooKangSan/[week8]BackTracking/Permutations.js new file mode 100644 index 0000000..6e7e89d --- /dev/null +++ b/JooKangSan/[week8]BackTracking/Permutations.js @@ -0,0 +1,27 @@ + +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permute = function(nums) { + const result = []; + + const backtrack = (current, remaining) => { + if (remaining.length === 0) { + result.push([...current]); + return; + } + + for (let i = 0; i < remaining.length; i++) { + current.push(remaining[i]); + + const nextRemaining = remaining.filter((_, index) => index !== i); + backtrack(current, nextRemaining); + + current.pop(); + } + }; + + backtrack([], nums); + return result; +}; \ No newline at end of file diff --git a/JooKangSan/[week8]BackTracking/Subsets.js b/JooKangSan/[week8]BackTracking/Subsets.js new file mode 100644 index 0000000..def9f68 --- /dev/null +++ b/JooKangSan/[week8]BackTracking/Subsets.js @@ -0,0 +1,22 @@ +/** + * @param {number[]} nums + * @return {number[][]} + */ +var subsets = function(nums) { + const result = []; + + const backtrack = (start, current) => { + result.push([...current]); + + for (let i = start; i < nums.length; i++) { + current.push(nums[i]); + + backtrack(i + 1, current); + + current.pop(); + } + }; + + backtrack(0, []); + return result; +}; \ No newline at end of file