diff --git a/binary-tree-maximum-path-sum/Chaedie.py b/binary-tree-maximum-path-sum/Chaedie.py new file mode 100644 index 000000000..febb3796a --- /dev/null +++ b/binary-tree-maximum-path-sum/Chaedie.py @@ -0,0 +1,39 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +""" +Solution: DFS + 1) dfs 로 left, right 각각의 max 값을 구한다. + 2) maxSum 을 업데이트하고, + 3) return value 로는 leftMax 또는 rightMax 와의 합만 리턴한다. + (left, right 를 둘 다 포함하는 경우와 둘 중 하나만 선택하는 경우를 나눔) + +Time: O(n) +Space: O(n) + +""" + + +class Solution: + def maxPathSum(self, root: Optional[TreeNode]) -> int: + maxSum = root.val + + def dfs(root): + nonlocal maxSum + if not root: + return 0 + + leftMax = dfs(root.left) + rightMax = dfs(root.right) + leftMax = max(leftMax, 0) + rightMax = max(rightMax, 0) + + maxSum = max(maxSum, root.val + leftMax + rightMax) + return root.val + max(leftMax, rightMax) + + dfs(root) + return maxSum diff --git a/binary-tree-maximum-path-sum/gwbaik9717.js b/binary-tree-maximum-path-sum/gwbaik9717.js new file mode 100644 index 000000000..ac2aa925b --- /dev/null +++ b/binary-tree-maximum-path-sum/gwbaik9717.js @@ -0,0 +1,47 @@ +// Time complexity: O(n) +// Space complexity: O(n) + +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var maxPathSum = function (root) { + let answer = Number.MIN_SAFE_INTEGER; + + const dfs = (current) => { + const candidates = [current.val]; + + if (current.left) { + dfs(current.left); + candidates.push(current.left.val + current.val); + } + + if (current.right) { + dfs(current.right); + candidates.push(current.right.val + current.val); + } + + // 현재 노드가 루트일 경우 + if (current.left && current.right) { + answer = Math.max( + answer, + current.left.val + current.right.val + current.val + ); + } + + current.val = Math.max(...candidates); + answer = Math.max(answer, current.val); + }; + + dfs(root); + + return answer; +}; diff --git a/graph-valid-tree/Chaedie.py b/graph-valid-tree/Chaedie.py new file mode 100644 index 000000000..016a4f2b9 --- /dev/null +++ b/graph-valid-tree/Chaedie.py @@ -0,0 +1,39 @@ +""" +Conditions of Valid Tree +1) no Loop +2) all nodes has to be connected + +Time: O(node + edge) +Space: O(node + edge) +""" + + +class Solution: + def validTree(self, n: int, edges: List[List[int]]) -> bool: + if not n: + return True + if len(edges) != n - 1: + return False + + # Make Graph + graph = {i: [] for i in range(n)} + for n1, n2 in edges: + graph[n1].append(n2) + graph[n2].append(n1) + + # loop check + visit = set() + + def dfs(i, prev): + if i in visit: + return False + + visit.add(i) + for j in graph[i]: + if j == prev: + continue + if not dfs(j, i): + return False + return True + + return dfs(0, None) and n == len(visit) diff --git a/graph-valid-tree/KwonNayeon.py b/graph-valid-tree/KwonNayeon.py new file mode 100644 index 000000000..97c78a50d --- /dev/null +++ b/graph-valid-tree/KwonNayeon.py @@ -0,0 +1,46 @@ +""" +Valid Tree의 조건: +1. 모든 노드가 연결되어 있어야 함 +2. 사이클이 없어야 함 +3. edge의 개수는 n-1개 + +Time Complexity: O(V + E) +- V: 노드의 개수 +- E: edge의 개수 + +Space Complexity: O(V) +- 노드 방문 여부를 저장하는 visited set 사용 + +풀이방법: +1. 기본 조건 체크: edge의 개수는 n-1개 +2. 각 노드별로 연결된 노드들의 정보를 저장 + - 무방향 그래프이므로 양쪽 모두 저장 +3. DFS로 노드 탐색 + - 0번 노드부터 시작해서 연결된 모든 노드를 방문 + - 이미 방문한 노드는 재방문하지 않음 +4. 모든 노드 방문 확인 + - visited의 크기가 n과 같다면 모든 노드가 연결된 것 -> valid tree +""" +def validTree(n, edges): + if len(edges) != n - 1: + return False + + adj = [[] for _ in range(n)] + for a, b in edges: + adj[a].append(b) + adj[b].append(a) + + visited = set() + + def dfs(node): + if node in visited: + return + + visited.add(node) + + for next_node in adj[node]: + dfs(next_node) + + dfs(0) + return len(visited) == n + diff --git a/graph-valid-tree/dusunax.py b/graph-valid-tree/dusunax.py new file mode 100644 index 000000000..7f4581c61 --- /dev/null +++ b/graph-valid-tree/dusunax.py @@ -0,0 +1,67 @@ +''' +# 261. Graph Valid Tree + +## What constitutes a 🌲 +1. it's a graph. +2. Connected: edges == n - 1, visited node count == n +3. Acyclic: there is no cycle. + +## Approach A. DFS +use DFS to check if there is a cycle in the graph. +- if there were no cycle & visited node count == n, return True. + +## Approach B. Disjoint Set Union (서로소 집합) +use Disjoint Set Union to check if there is a cycle in the graph. +- if you find a cycle, return False immediately. +- if you find no cycle, return True. + +### Union Find Operation +- Find: find the root of a node. + - if the root of two nodes is already the same, there is a cycle. +- Union: connect two nodes. + +## Approach Comparison +- **A. DFS**: simple and easy to understand. +- **B. Disjoint Set Union**: quicker to check if there is a cycle. if there were more edges, Union Find would be faster. +''' +class Solution: + def validTreeDFS(self, n: int, edges: List[List[int]]) -> bool: + if len(edges) != n - 1: + return False + + graph = [[] for _ in range(n)] + for node, neighbor in edges: + graph[node].append(neighbor) + graph[neighbor].append(node) + + visited = set() + def dfs(node): + visited.add(node) + for neighbor in graph[node]: + if neighbor not in visited: + dfs(neighbor) + + dfs(0) + return len(visited) == n + + def validTreeUnionFind(self, n: int, edges: List[List[int]]) -> bool: + if len(edges) != n - 1: + return False + + parent = [i for i in range(n)] + + def find(x): + if x == parent[x]: + return x + parent[x] = find(parent[x]) + return parent[x] + + def union(x, y): + parent[find(x)] = find(y) + + for node, neighbor in edges: + if find(node) == find(neighbor): + return False + union(node, neighbor) + + return True diff --git a/graph-valid-tree/mike2ox.ts b/graph-valid-tree/mike2ox.ts new file mode 100644 index 000000000..1183fe43c --- /dev/null +++ b/graph-valid-tree/mike2ox.ts @@ -0,0 +1,45 @@ +/** + * Source: https://www.lintcode.com/problem/178/ + * Solution: 유효한 트리인지 순회하면서 확인하면 되기에 BFS로 구현 + * 시간 복잡도: O(V + E) - 노드와 간선에 한번은 방문 + * 공간 복잡도: O(V + E) - 인접리스트 만큼의 공간 필요 + */ +function validTree(n: number, edges: number[][]): boolean { + // 간선 개수 체크: 트리는 노드 개수 - 1개의 간선을 가져야 함 + if (edges.length !== n - 1) return false; + + // 인접 리스트 + const adjList: Map = new Map(); + for (let i = 0; i < n; i++) { + adjList.set(i, []); + } + + // 양방향 그래프 구성 + for (const [u, v] of edges) { + adjList.get(u)!.push(v); + adjList.get(v)!.push(u); + } + + const queue: [number, number][] = [[0, -1]]; // [노드, 부모노드] + const visited: Set = new Set([0]); + + while (queue.length > 0) { + const [node, parent] = queue.shift()!; + + // 모든 이웃 노드 확인 + for (const neighbor of adjList.get(node)!) { + // 부모 노드는 continue + if (neighbor === parent) continue; + + // 이미 방문한 노드를 만나면 사이클 존재 + if (visited.has(neighbor)) return false; + + // 이웃 노드를 큐에 추가하고 방문 표시 + visited.add(neighbor); + queue.push([neighbor, node]); + } + } + + // 모든 노드가 연결되어 있는지 확인 + return visited.size === n; +} diff --git a/maximum-depth-of-binary-tree/Chaedie.py b/maximum-depth-of-binary-tree/Chaedie.py new file mode 100644 index 000000000..1291ae2b9 --- /dev/null +++ b/maximum-depth-of-binary-tree/Chaedie.py @@ -0,0 +1,23 @@ +""" +Solution: BFS +Time: O(n) +Space: O(n) +""" + + +class Solution: + def maxDepth(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + q = deque([root]) + maxLevel = 0 + while q: + maxLevel += 1 + for i in range(len(q)): + node = q.popleft() + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + return maxLevel diff --git a/maximum-depth-of-binary-tree/KwonNayeon.py b/maximum-depth-of-binary-tree/KwonNayeon.py new file mode 100644 index 000000000..a3a36180a --- /dev/null +++ b/maximum-depth-of-binary-tree/KwonNayeon.py @@ -0,0 +1,56 @@ +""" +Constraints: +- The number of nodes in the tree is in the range [0, 10^4]. +- -100 <= Node.val <= 100 + +Time Complexity: O(N) +- N은 트리의 노드 수 +- 모든 노드를 한 번씩 방문하기 때문 + +Space Complexity: O(H) +- H는 트리의 높이 +- 재귀 호출로 인한 호출 스택의 최대 깊이가 트리의 높이와 같음 + +풀이방법: +1. Base case: root가 None인 경우 0을 반환 +2. 재귀를 활용하여 왼쪽과 오른쪽 서브트리 깊이를 각각 계산 +3. 두 서브트리의 깊이 중 최대값에 1을 더해서 반환 (현재 노드의 깊이를 포함) +""" +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +# Solution 1: 재귀 +class Solution: + def maxDepth(self, root: Optional[TreeNode]) -> int: + if root is None: + return 0 + + left_depth = self.maxDepth(root.left) + right_depth = self.maxDepth(root.right) + + return max(left_depth, right_depth) + 1 + +# Solution 2: 반복문 +class Solution: + def maxDepth(self, root: Optional[TreeNode]) -> int: + if root is None: + return 0 + + stack = [(root, 1)] + max_depth = 0 + + while stack: + current, depth = stack.pop() + + max_depth = max(max_depth, depth) + + if current.left: + stack.append((current.left, depth + 1)) + if current.right: + stack.append((current.right, depth + 1)) + + return max_depth diff --git a/maximum-depth-of-binary-tree/dusunax.py b/maximum-depth-of-binary-tree/dusunax.py new file mode 100644 index 000000000..e1c80a2a4 --- /dev/null +++ b/maximum-depth-of-binary-tree/dusunax.py @@ -0,0 +1,61 @@ +''' +# 104. Maximum Depth of Binary Tree + +use DFS to find the maximum depth of the binary tree. + +## A. if we use a helper function (not a good idea) +``` +helper function explanation: +- store the max_depth in the nonlocal variable.(outside of the helper function) +- base case: if the node is None, update the max_depth and return. +- in the helper function, do recursive call for the left and right children of the node. + - update the count for the depth of the tree. +- update the max_depth when the node is a leaf node's children. +``` + +## B. return the max_depth directly +👉 why helper function is not necessary? +recursion function can return the max_depth directly. +remove side effect & non-local variable. + +## TC is O(n) + +visit each node once for checking if it is a leaf node's children. + +## SC is O(h) + +h for height of the tree +''' +class Solution: + ''' + A. first approach with side effect + ''' + def maxDepthWithHelper(self, root: Optional[TreeNode]) -> int: + max_depth = 0 + + def helper(node, count): + nonlocal max_depth + if node is None: + max_depth = max(max_depth, count) + return + + helper(node.left, count+1) + helper(node.right, count + 1) + + helper(root, max_depth) + + return max_depth + + ''' + B. return the max_depth directly. + - more concise & readable + - no side effect & non-local variable + ''' + def maxDepth(self, root: Optional[TreeNode]) -> int: + if root is None: + return 0 + + left_count = self.maxDepth(root.left) + right_count = self.maxDepth(root.right) + + return max(left_count, right_count) + 1 diff --git a/maximum-depth-of-binary-tree/gwbaik9717.js b/maximum-depth-of-binary-tree/gwbaik9717.js new file mode 100644 index 000000000..e0a47d850 --- /dev/null +++ b/maximum-depth-of-binary-tree/gwbaik9717.js @@ -0,0 +1,39 @@ +// Time complexity: O(n) +// Space complexity: O(n) + +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var maxDepth = function (root) { + let answer = 0; + + const dfs = (current, depth) => { + if (!current) { + return; + } + + if (answer < depth) { + answer = depth; + } + + if (current.left) { + dfs(current.left, depth + 1); + } + + if (current.right) { + dfs(current.right, depth + 1); + } + }; + + dfs(root, 1); + return answer; +}; diff --git a/maximum-depth-of-binary-tree/mike2ox.ts b/maximum-depth-of-binary-tree/mike2ox.ts new file mode 100644 index 000000000..c7410b177 --- /dev/null +++ b/maximum-depth-of-binary-tree/mike2ox.ts @@ -0,0 +1,47 @@ +/** + * Source: https://leetcode.com/problems/maximum-depth-of-binary-tree/ + * 접근법: 최대 깊이만 고려하면 되기에 탐색 알고리즘 중 하나 선택 + * + * 시간복잡도: O(N) - 편향 트리인 경우, 모든 노드(N개) 검색 + * 공간복잡도: O(H) - 트리높이 + * + * 다른 접근법 + * - 재귀를 통해 가독성있는 코드 작성 가능(But, 깊이가 커지면 스택오버플로우가 + */ + +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +// solution1: array를 stack처럼 사용하면서 DFS구현 +function maxDepth(root: TreeNode | null): number { + if (!root) return 0; + const stack = new Array({ node: root, depth: 1 }); + let maxDepth = 1; + while (stack.length) { + const now = stack.pop(); + if (!now.node?.left && !now.node?.right) { + if (now.depth > maxDepth) maxDepth = now.depth; + continue; + } + stack.push({ node: now.node.left, depth: now.depth + 1 }); + stack.push({ node: now.node.right, depth: now.depth + 1 }); + } + return maxDepth; +} + +// solution2: recursion으로 DFS구현 +function maxDepth(root: TreeNode | null): number { + if (!root) return 0; + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +} diff --git a/merge-intervals/Chaedie.py b/merge-intervals/Chaedie.py new file mode 100644 index 000000000..eb376fd85 --- /dev/null +++ b/merge-intervals/Chaedie.py @@ -0,0 +1,19 @@ +""" +Time: O(n log(n)) +Space: O(n) +""" + + +class Solution: + def merge(self, intervals: List[List[int]]) -> List[List[int]]: + intervals.sort(key=lambda x: x[0]) + result = [intervals[0]] + + for start, end in intervals[1:]: + prev = result[-1] + + if prev[0] <= start <= prev[1]: + result[-1][1] = max(prev[1], end) + else: + result.append([start, end]) + return result diff --git a/merge-intervals/KwonNayeon.py b/merge-intervals/KwonNayeon.py new file mode 100644 index 000000000..ffbda221f --- /dev/null +++ b/merge-intervals/KwonNayeon.py @@ -0,0 +1,39 @@ +""" +Constraints: +- 1 <= intervals.length <= 10^4 +- intervals[i].length == 2 +- 0 <= starti <= endi <= 10^4 + +Time Complexity: O(nlogn) +- 정렬에 nlogn, 순회에 n이 필요하므로 전체는 O(nlogn) + +Space Complexity: O(n) +- 최악의 경우 모든 구간이 겹치지 않아 n개의 구간을 저장해야 함 + +풀이방법: +0. intervals를 시작점 기준으로 정렬 +1. merged 배열을 intervals의 첫 번째 구간으로 초기화 +2. intervals의 두 번째 구간부터 순회하면서: + - 현재 구간의 시작점이 merged 배열의 마지막 구간의 끝점보다 작거나 같으면 병합 + - 병합할 때는 끝점을 두 구간의 끝점 중 더 큰 값으로 설정 +3. 현재 구간이 merged의 마지막 구간과 겹치지 않으면 그대로 merged에 추가 +4. 2-3을 반복하여 모든 구간을 처리함 +""" +class Solution: + def merge(self, intervals: List[List[int]]) -> List[List[int]]: + if not intervals: + return [] + + intervals.sort(key=lambda x: x[0]) + + merged = [intervals[0]] + + for interval in intervals[1:]: + if interval[0] <= merged[-1][1]: + merged[-1][1] = max(merged[-1][1], interval[1]) + + else: + merged.append(interval) + + return merged + diff --git a/merge-intervals/gwbaik9717.js b/merge-intervals/gwbaik9717.js new file mode 100644 index 000000000..a65a32c19 --- /dev/null +++ b/merge-intervals/gwbaik9717.js @@ -0,0 +1,33 @@ +// Time complexity: O(nlogn) +// Space complexity: O(n) + +/** + * @param {number[][]} intervals + * @return {number[][]} + */ +var merge = function (intervals) { + intervals.sort((a, b) => { + if (a[0] === b[0]) { + return a[1] - b[1]; + } + + return a[0] - b[0]; + }); + + const answer = [intervals[0]]; + + for (let i = 1; i < intervals.length; i++) { + const current = intervals.at(i); + const prev = answer.at(-1); + + // No overlapping + if (current[0] > prev[1]) { + answer.push(current); + continue; + } + + answer[answer.length - 1] = [prev[0], Math.max(prev[1], current[1])]; + } + + return answer; +}; diff --git a/merge-intervals/mike2ox.ts b/merge-intervals/mike2ox.ts new file mode 100644 index 000000000..1d2fb4b02 --- /dev/null +++ b/merge-intervals/mike2ox.ts @@ -0,0 +1,27 @@ +/** + * Source: https://leetcode.com/problems/merge-intervals/ + * 풀이방법: 정령후 첫번째 구간 추가하고 이후 순회하면서 겹치는지 확인 후 병합 or 추가 + * 시간복잡도: O(NlogN) - 정렬에서 NlogN + * 공간복잡도: O(N) - 결과 저장할 공간 + */ + +function merge(intervals: number[][]): number[][] { + if (intervals.length <= 1) return intervals; + + intervals.sort((a, b) => a[0] - b[0]); + const result: number[][] = [intervals[0]]; + + for (let i = 1; i < intervals.length; i++) { + const current = intervals[i]; + const lastMerged = result[result.length - 1]; + + // 현재 구간의 시작점이 이전 구간의 끝점보다 작거나 같으면 merge + if (current[0] <= lastMerged[1]) { + lastMerged[1] = Math.max(lastMerged[1], current[1]); // 끝점을 두 구간의 끝점 중 더 큰 값으로 업데이트 + } else { + result.push(current); + } + } + + return result; +} diff --git a/reorder-list/Chaedie.py b/reorder-list/Chaedie.py new file mode 100644 index 000000000..f69575f74 --- /dev/null +++ b/reorder-list/Chaedie.py @@ -0,0 +1,42 @@ +""" +가장 뒤부터 돌아오는 방법을 찾아야한다. +1) 재귀 스택 방식 +2) 새로운 리스트를 만드는 방법 +3) two pointer 로 반 잘라서 reverse 한 뒤 merge +""" + +""" +Solution: 3) Two pointer +Time: O(n) +Space: O(1) +""" + + +class Solution: + def reorderList(self, head: Optional[ListNode]) -> None: + + # 절반 자르기 + slow, fast = head, head.next + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + # Reverse + second = slow.next + slow.next = None + prev = None + while second: + tmp = second.next + second.next = prev + prev = second + second = tmp + + # Merge + first = head + second = prev + while second: + tmp1, tmp2 = first.next, second.next + first.next = second + second.next = tmp1 + first = tmp1 + second = tmp2 diff --git a/reorder-list/KwonNayeon.py b/reorder-list/KwonNayeon.py new file mode 100644 index 000000000..21d57255a --- /dev/null +++ b/reorder-list/KwonNayeon.py @@ -0,0 +1,63 @@ +""" +Constraints: +- The number of nodes in the list is in the range [1, 5 * 10^4]. +- 1 <= Node.val <= 1000 + +Time Complexity: O(n) +- 리스트를 한 번씩 순회하면서 알고리즘의 각 단계를 수행함 + +Space Complexity: O(1) +- 정해진 개수의 변수 외에는 추가 공간을 사용하지 않음 + +풀이방법: +1. 중간 지점 찾기 + - slow/fast 포인터를 사용하여 중간 지점 찾기 +2. 뒷부분 뒤집기 + - prev, curr 포인터로 링크드 리스트의 방향 전환 + - next_temp에 다음 노드를 저장한 후 방향 변경 +3. 앞부분과 뒷부분 합치기 + - 두 리스트의 시작점(first, second)부터 시작 + - temp1, temp2에 다음 노드 저장 + - 포인터들을 번갈아가며 연결함 +""" +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reorderList(self, head: Optional[ListNode]) -> None: + """ + Do not return anything, modify head in-place instead. + """ + # 중간 지점 찾기 + slow = head + fast = head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + # 뒷부분 뒤집기 + prev = None + curr = slow.next + slow.next = None + while curr: + next_temp = curr.next + curr.next = prev + prev = curr + curr = next_temp + + # 앞부분과 뒷부분 합치기 + first = head + second = prev + while second: + temp1 = first.next + temp2 = second.next + + first.next = second + second.next = temp1 + + first = temp1 + second = temp2 + + diff --git a/reorder-list/dusunax.py b/reorder-list/dusunax.py new file mode 100644 index 000000000..e372d2a38 --- /dev/null +++ b/reorder-list/dusunax.py @@ -0,0 +1,49 @@ +''' +# 143. Reorder list +use two pointers for each steps. + +1. finding the middle + - two pointers: slow, fast + - move slow 1, fast 2 until fast reaches the end. +2. reversing the second half + - two pointers: prev, curr + - start from slow to end, do common reverse linked list operation. + - need to break the links to halves beforehand. +3. merging first & second + - two pointers: frist, second + - merge second between first, until second is None + +## TC is O(n) +- find the middle: O(n) +- reverse the second half: O(n) +- merge the two halves: O(n) + +## SC is O(1) +- no extra space is used. +''' +class Solution: + def reorderList(self, head: Optional[ListNode]) -> None: + # 1. finding the middle + slow, fast = head, head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + # 2. reversing second half + second_not_reversed = slow.next + slow.next = None + prev, curr = None, second_not_reversed + while curr: + temp = curr.next + curr.next = prev + prev = curr + curr = temp + + # 3. merging first & second + first, second = head, prev + while second: + temp1, temp2 = first.next, second.next + first.next = second + second.next = temp1 + first = temp1 + second = temp2 diff --git a/reorder-list/gwbaik9717.js b/reorder-list/gwbaik9717.js new file mode 100644 index 000000000..2076830a5 --- /dev/null +++ b/reorder-list/gwbaik9717.js @@ -0,0 +1,56 @@ +// Time complexity: O(n) +// Space complexity: O(1) + +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @return {void} Do not return anything, modify head in-place instead. + */ +var reorderList = function (head) { + // middle 찾기 + let slow = head; + let fast = slow; + + while (fast && fast.next) { + fast = fast.next.next; + slow = slow.next; + } + + const middle = slow; + + // 후반부 뒤집기 (middle 부터) + let next = null; + let current = middle; + + while (current) { + const temp = current.next; + current.next = next; + next = current; + current = temp; + } + + // 합치기 + let back = next; + let reordered = head; + + while (reordered && back) { + const temp = reordered.next; + + reordered.next = back; + back = back.next; + reordered = reordered.next; + + reordered.next = temp; + reordered = reordered.next; + } + + if (reordered) { + reordered.next = null; + } +}; diff --git a/reorder-list/mike2ox.ts b/reorder-list/mike2ox.ts new file mode 100644 index 000000000..6fb2b1e43 --- /dev/null +++ b/reorder-list/mike2ox.ts @@ -0,0 +1,55 @@ +/** + * Source: https://leetcode.com/problems/reorder-list/ + * 풀이방법: 임시 배열을 사용해서 투포인트 전략으로 풂 + * 시간복잡도: O(n) + * 공간복잡도: O(n) + * + * 추가 풀이 + * - node를 가리키는 두 인자만 사용해서 투포인트 전략이 가능(but, 구현 x) +/ + +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +/** + Do not return anything, modify head in-place instead. + */ +function reorderList(head: ListNode | null): void { + if (!head || !head.next) return; + + // 1. 모든 노드를 배열에 저장 + const nodes: ListNode[] = []; + let current: ListNode | null = head; + while (current) { + nodes.push(current); + current = current.next; + } + + // 2. 배열의 양끝에서 시작하여 리스트 재구성 + let left = 0; + let right = nodes.length - 1; + + while (left < right) { + // 현재 왼쪽 노드의 다음을 저장 + nodes[left].next = nodes[right]; + left++; + + if (left === right) break; + + // 현재 오른쪽 노드를 다음 왼쪽 노드에 연결 + nodes[right].next = nodes[left]; + right--; + } + + // 마지막 노드의 next를 null로 설정 + nodes[left].next = null; +}