-
-
Notifications
You must be signed in to change notification settings - Fork 195
[mallayon] Week 9 #985
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[mallayon] Week 9 #985
Changes from all commits
1b405e0
3045f60
bfd79dd
58fd46f
0bd99fe
0d0395b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/** | ||
* @link https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/ | ||
* | ||
* 접근 방법 : | ||
* - O(logn)으로 풀어야 하니까 이진 탐색 적용 | ||
* - 배열의 start, end 인덱스를 활용해서 최소값이 있는 방향을 탐색 | ||
* - nums[mid] > nums[end]이면, 최소값이 오른쪽에 있으니까 start를 mid+1로 이동 | ||
* - 반대로 nums[mid] < nums[end] 이면, 최소값이 왼쪽에 있으니까 end를 mid로 이동 | ||
* | ||
* 시간복잡도 : O(logn) | ||
* - 탐색 범위를 계속 절반으로 줄이니까 O(logn) | ||
* | ||
* 공간복잡도 : O(1) | ||
* - 고정된 변수만 사용 | ||
*/ | ||
function findMin(nums: number[]): number { | ||
let start = 0, | ||
end = nums.length - 1; | ||
|
||
while (start < end) { | ||
let mid = Math.floor(start + (end - start) / 2); | ||
|
||
if (nums[mid] > nums[end]) start = mid + 1; | ||
else end = mid; | ||
} | ||
|
||
return nums[start]; | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 접근 방법 두가지 모두 훌륭하게 찾아주신것 같습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 시간 복잡도 반영해두었습니다:) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
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; | ||
} | ||
} | ||
|
||
/** | ||
* @link https://leetcode.com/problems/linked-list-cycle/description/ | ||
* | ||
* 접근 방법 : | ||
* - 노드 순회하면서 방문한 노드에 저장 | ||
* - 방문할 노드가 이미 방문한 노드에 있으면 순환 구조 이므로 true 리턴 | ||
* | ||
* 시간복잡도 : O(n) | ||
* - 순환 이전 노드의 개수 n만큼 순회하면서 순환 여부 확인 | ||
* | ||
* 공간복잡도 : O(n) | ||
* - visited set에 순환되기 이전 노드 n개 저장 | ||
*/ | ||
function hasCycle(head: ListNode | null): boolean { | ||
const visited = new Set<ListNode>(); | ||
let current = head; | ||
|
||
while (current !== null) { | ||
if (visited.has(current)) return true; | ||
|
||
visited.add(current); | ||
current = current.next; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/* | ||
* 접근 방법 : | ||
* - 공간복잡도 O(1)로 풀이 | ||
* - 사이클이 있으면 두 포인터가 결국 같은 노드를 가리키게 되므로 투 포인터 사용 | ||
* - 사이클이 없는 경우를 위해서 tail노드의 null체크를 해야함 | ||
* | ||
* 시간복잡도 : O(n) | ||
* - 순환 이전 노드의 개수 n만큼 순회하면서 순환 여부 확인 | ||
* | ||
* 공간복잡도 : O(1) | ||
* - 추가 메모리없이 slow, fast 포인터만 사용하므로 O(1) | ||
*/ | ||
function hasCycle(head: ListNode | null): boolean { | ||
let slow = head; | ||
let fast = head; | ||
|
||
while (fast !== null && fast.next !== null) { | ||
slow = slow.next; | ||
fast = fast.next.next; | ||
|
||
if (slow === fast) return true; | ||
} | ||
|
||
return false; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/** | ||
* @link https://leetcode.com/problems/maximum-product-subarray/ | ||
* | ||
* 접근 방법 : | ||
* - 음수가 짝수번 나오면 최대값이 될 수 있으니까 최소곱, 최대곱을 모두 업데이트 | ||
* - 현재 값이 단독으로 최소, 최대일 수 있으니까 현재값, 최소곱 * 현재값, 최대곱 * 현재값을 비교 | ||
* | ||
* 시간복잡도 : O(n) | ||
* - nums 1회 순회 | ||
* | ||
* 공간복잡도 : O(1) | ||
* - 고정된 변수만 사용 | ||
*/ | ||
function maxProduct(nums: number[]): number { | ||
let currentMin = nums[0], | ||
currentMax = nums[0], | ||
maxSoFar = nums[0]; | ||
|
||
for (let i = 1; i < nums.length; i++) { | ||
const num = nums[i]; | ||
const minCandidate = currentMin * num; | ||
const maxCandidate = currentMax * num; | ||
|
||
currentMin = Math.min(num, minCandidate, maxCandidate); | ||
currentMax = Math.max(num, minCandidate, maxCandidate); | ||
|
||
maxSoFar = Math.max(currentMax, maxSoFar); | ||
} | ||
|
||
return maxSoFar; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/** | ||
* @link https://leetcode.com/problems/minimum-window-substring/description/ | ||
* 접근 방법 : 2개의 포인터 활용해서 슬라이딩 윈도우 방식 사용 | ||
* - t의 문자를 맵에 저장해서 개수 기록 | ||
* - right 포인터로 t의 모든 문자 포함할 때까지 윈도우 확장 | ||
* - 모든 문자 포함하면, left 포인터로 최소 윈도우 찾을 때까지 윈도우 축소 | ||
* - '확장 => 축소 => 최소 윈도우 업데이트' 이 과정을 반복 | ||
* | ||
* 시간복잡도 : O(n) | ||
* - n은 s의 길이, 각 문자 최대 2회 방문 (확장 + 축소) | ||
* | ||
* 공간복잡도 : O(n) | ||
* - 최악의 경우, 윈도우에 s의 모든 문자가 저장됨 | ||
*/ | ||
function minWindow(s: string, t: string): string { | ||
const targetCharCount = new Map<string, number>(); | ||
|
||
// t의 문자 개수 카운트 | ||
for (const char of t) { | ||
targetCharCount.set(char, (targetCharCount.get(char) ?? 0) + 1); | ||
} | ||
|
||
const requiredUniqueChars = targetCharCount.size; | ||
let matchedUniqueChars = 0; | ||
const windowCharCount = new Map<string, number>(); | ||
|
||
let minWindow = ""; | ||
let minWindowLength = Infinity; | ||
|
||
let left = 0, | ||
right = 0; | ||
|
||
while (right < s.length) { | ||
const char = s[right]; | ||
windowCharCount.set(char, (windowCharCount.get(char) ?? 0) + 1); | ||
|
||
// t에 속하는 문자이면서, 문자 개수가 같은 경우 | ||
if ( | ||
targetCharCount.has(char) && | ||
targetCharCount.get(char) === windowCharCount.get(char) | ||
) | ||
matchedUniqueChars++; | ||
|
||
while (matchedUniqueChars === requiredUniqueChars) { | ||
const windowLength = right - left + 1; | ||
|
||
// 최소 윈도우 업데이트 | ||
if (windowLength < minWindowLength) { | ||
minWindowLength = windowLength; | ||
minWindow = s.substring(left, right + 1); | ||
} | ||
|
||
const leftChar = s[left]; | ||
windowCharCount.set(leftChar, windowCharCount.get(leftChar)! - 1); | ||
|
||
//축소로 윈도우 내의 t문자가 감소했으면 matchedUniqueChars 감소 | ||
if (windowCharCount.get(leftChar)! < targetCharCount.get(leftChar)!) | ||
matchedUniqueChars--; | ||
|
||
// 윈도우 축소 | ||
left++; | ||
} | ||
|
||
// 윈도우 확장 | ||
right++; | ||
} | ||
|
||
return minWindow; | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 풀이를 굉장히 정석적으로 깔끔하게 해주신 것 같습니다 :) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/** | ||
* @link https://leetcode.com/problems/pacific-atlantic-water-flow/description/ | ||
* | ||
* 접근 방법 : | ||
* - pacific, atlantic를 동시에 도달하는 지점 찾기 위해서, 도달 여부 트래킹하는 2개의 visited 배열 사용한다. | ||
* - 바다 경계에서만 DFS를 호출해서 방문 여부 체크한다. | ||
* - 바다 경계에서 시작했기 때문에 DFS는 인접 셀의 높이가 같거나 높을 때만 호출되어야 한다. | ||
* | ||
* 시간복잡도 : O(m * n) | ||
* - 각 셀마다 최대 4번 DFS가 호출될 수 있다. O(m * n) | ||
* - 결과 순회할 때 m * n 만큼 순회한다. | ||
* | ||
* 공간복잡도 : O(m * n) | ||
* - 2개의 visited 배열 사용한다. | ||
* - 최악의 경우, m * n 모든 칸에서 DFS 호출된다. | ||
*/ | ||
|
||
const directions = [ | ||
[-1, 0], | ||
[1, 0], | ||
[0, -1], | ||
[0, 1], | ||
]; | ||
|
||
function pacificAtlantic(heights: number[][]): number[][] { | ||
const result: number[][] = []; | ||
|
||
const rows = heights.length; | ||
const cols = heights[0].length; | ||
|
||
// 두 바다 도달하는 지점 트래킹 하기 위한 배열 | ||
const pacificVisited = Array.from({ length: rows }, () => | ||
Array(cols).fill(false) | ||
); | ||
const atlanticVisited = Array.from({ length: rows }, () => | ||
Array(cols).fill(false) | ||
); | ||
|
||
const dfs = (row: number, col: number, visited: boolean[][]) => { | ||
if (visited[row][col]) return; | ||
// 방문 지점 기록 | ||
visited[row][col] = true; | ||
|
||
for (const [x, y] of directions) { | ||
const newRow = row + x; | ||
const newCol = col + y; | ||
|
||
// 새로운 위치가 경계안에 있고, 현재 높이보다 같거나 높을 때만 DFS 호출 | ||
if ( | ||
0 <= newRow && | ||
newRow < rows && | ||
0 <= newCol && | ||
newCol < cols && | ||
heights[newRow][newCol] >= heights[row][col] | ||
) | ||
dfs(newRow, newCol, visited); | ||
} | ||
}; | ||
|
||
// pacific 경계에서 DFS 호출 (첫 번쨰 열, 첫 번째 행) | ||
for (let row = 0; row < rows; row++) dfs(row, 0, pacificVisited); | ||
for (let col = 0; col < cols; col++) dfs(0, col, pacificVisited); | ||
|
||
// atlantic 경계에서 DFS 호출 (마지막 열, 마지막 행) | ||
for (let row = 0; row < rows; row++) dfs(row, cols - 1, atlanticVisited); | ||
for (let col = 0; col < cols; col++) dfs(rows - 1, col, atlanticVisited); | ||
|
||
for (let row = 0; row < rows; row++) { | ||
for (let col = 0; col < cols; col++) { | ||
if (pacificVisited[row][col] && atlanticVisited[row][col]) | ||
result.push([row, col]); | ||
} | ||
} | ||
|
||
return result; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O(log n)에 맞추어 binary search를 적절하게 활용 해 주신것 같습니다 :)