-
Notifications
You must be signed in to change notification settings - Fork 126
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
[재호] WEEK 04 Solutions #408
Changes from all commits
ff5acf1
77d6e59
5a921e5
15652ff
918e6da
3ff3afb
649b32c
1f55061
f1d5d77
0975c1c
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,43 @@ | ||
/** | ||
* TC: O(N) | ||
* SC: O(N) | ||
* nums의 숫자에 접근하는 횟수는 2번에서 N만큼, 4번에서 최대 N만큼 입니다. | ||
* 즉, 2N번 만큼 nums의 숫자에 접근합니다. | ||
* N^2이 아닌 이유: N^2이 아닌 2N으로 생각한 이유는 2번에서 N번의 순회를 하지만 각 순회가 서로에게 영향을 미치기 때문에 최대 순회는 2N으로 계산했습니다. (1번의 N 순회 제외) | ||
* @see https://github.com/DaleStudy/leetcode-study/pull/408#discussion_r1747071917 | ||
*/ | ||
|
||
/** | ||
* @param {number[]} nums | ||
* @return {number} | ||
*/ | ||
var longestConsecutive = function (nums) { | ||
let maxCount = 0; | ||
const obj = {}; | ||
|
||
// 1. 숫자의 존재 여부를 키로 접근하기 위해 저장 | ||
for (const num of nums) { | ||
obj[num] = true; | ||
} | ||
|
||
// 2. 시작점들을 찾기 위해 순회 | ||
for (const num of nums) { | ||
// 3. 연속적인 배열의 시작점인지 확인 | ||
if (obj[num - 1]) { | ||
continue; | ||
} | ||
|
||
// 4. 연속적인 배열의 시작점부터 끝점까지 순회 | ||
let count = 1; | ||
let next = num + 1; | ||
while (obj[next]) { | ||
count += 1; | ||
next += 1; | ||
} | ||
|
||
// 5. 가장 긴 배열의 길이 갱신 | ||
maxCount = Math.max(maxCount, count); | ||
} | ||
|
||
return maxCount; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// TC: O(N) | ||
// SC: O(1) | ||
|
||
/** | ||
* @param {number[]} nums | ||
* @return {number} | ||
*/ | ||
var maxProduct = function (nums) { | ||
let maximumProduct = Number.MIN_SAFE_INTEGER; | ||
let subProduct = 1; | ||
// 1. 좌에서 우로 누적곱을 저장하기 위해 순회 | ||
for (let index = 0; index < nums.length; index++) { | ||
// 2. 0을 만나면 누적곱에 곱하지 않고 1로 초기화 | ||
if (nums[index] === 0) { | ||
maximumProduct = Math.max(maximumProduct, 0); | ||
subProduct = 1; | ||
continue; | ||
} | ||
// 3. 매번 누적곱을 갱신 | ||
subProduct *= nums[index]; | ||
maximumProduct = Math.max(maximumProduct, subProduct); | ||
} | ||
|
||
subProduct = 1; | ||
// 4. 우에서 좌로 누적곱을 저장하기 위해 순회 | ||
for (let index = nums.length - 1; index >= 0; index--) { | ||
// 5. 0을 만나면 누적곱에 곱하지 않고 1로 초기화 | ||
if (nums[index] === 0) { | ||
maximumProduct = Math.max(maximumProduct, 0); | ||
subProduct = 1; | ||
continue; | ||
} | ||
// 6. 매번 누적곱을 갱신 | ||
subProduct *= nums[index]; | ||
maximumProduct = Math.max(maximumProduct, subProduct); | ||
} | ||
|
||
return maximumProduct; | ||
}; | ||
Comment on lines
+8
to
+39
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,21 @@ | ||
// TC: O(N) | ||
// SC: O(1) | ||
|
||
/** | ||
* @param {number[]} nums | ||
* @return {number} | ||
*/ | ||
var missingNumber = function (nums) { | ||
// result: subN - num의 누적합을 저장하는 변수 | ||
let result = 0; | ||
// subN: 1부터 N까지 올라가는 변수 | ||
let subN = 1; | ||
|
||
for (const num of nums) { | ||
result += subN - num; | ||
subN += 1; | ||
} | ||
|
||
// 최종 누적합은 (1~N까지 합) - (nums의 모든 원소 합) 이므로 누락된 숫자만 남게 됩니다. | ||
return result; | ||
}; | ||
DaleSeo marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// 3차 | ||
// TC: O(N) | ||
// SC: O(1) | ||
// N: s.length | ||
|
||
/** | ||
* @param {string} s | ||
* @return {boolean} | ||
*/ | ||
var isPalindrome = function (s) { | ||
// 1. 투포인터를 양끝에서 시작합니다. | ||
let left = 0; | ||
let right = s.length - 1; | ||
|
||
while (left < right) { | ||
// 2. 현재 가리키는 문자가 영대소문자, 숫자가 아니면 건너뜁니다. | ||
while (left < right && !isValid(s[left])) { | ||
left += 1; | ||
} | ||
while (left < right && !isValid(s[right])) { | ||
right -= 1; | ||
} | ||
|
||
// 3. 문자의 갯수가 홀수인 경우 투 포인터가 모두 가운데를 가리키는 경우 순회를 끝났다고 생각합니다. | ||
if (left === right) { | ||
break; | ||
} | ||
|
||
// 4. 투 포인터가 가리키는 문자가 다른 경우 정답(false)를 반환합니다. | ||
if (!isSame(s[left], s[right])) { | ||
return false; | ||
} | ||
|
||
// 5. 다음 문자로 넘어갑니다. | ||
left += 1; | ||
right -= 1; | ||
} | ||
|
||
// 6. 모든 순회가 끝냈다면 palindrome이라고 판단합니다. | ||
return true; | ||
|
||
function isValid(spell) { | ||
if ("0" <= spell && spell <= "9") { | ||
return true; | ||
} | ||
if ("a" <= spell && spell <= "z") { | ||
return true; | ||
} | ||
if ("A" <= spell && spell <= "Z") { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
function isSame(spellA, spellB) { | ||
if (spellA === spellB) { | ||
return true; | ||
} | ||
if (Math.abs(s[left].charCodeAt() - s[right].charCodeAt()) === 32) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
}; | ||
|
||
// 2차 | ||
// TC: O(N) | ||
// SC: O(N) | ||
// N: s.length | ||
|
||
/** | ||
* @param {string} s | ||
* @return {boolean} | ||
*/ | ||
var isPalindrome = function (s) { | ||
const phrase = s.toLowerCase(); | ||
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. 선재적으로 소문자 변환을 하는 대신에 31번째 줄에서 비교할 때 하면 공간 효율을 개선할 수 있지 않을까요? 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. |
||
// 1. 투포인터를 양끝에서 시작합니다. | ||
let left = 0; | ||
let right = phrase.length - 1; | ||
|
||
while (left < right) { | ||
// 2. 현재 가리키는 문자가 영소문자, 숫자가 아니면 건너뜁니다. | ||
while (left < right && !isValid(phrase[left])) { | ||
left += 1; | ||
} | ||
while (left < right && !isValid(phrase[right])) { | ||
right -= 1; | ||
} | ||
|
||
// 3. 문자의 갯수가 홀수인 경우 투 포인터가 모두 가운데를 가리키는 경우 순회를 끝났다고 생각합니다. | ||
if (left === right) { | ||
break; | ||
} | ||
|
||
// 4. 투 포인터가 가리키는 문자가 다른 경우 정답(false)를 반환합니다. | ||
if (phrase[left] !== phrase[right]) { | ||
return false; | ||
} | ||
|
||
// 5. 다음 문자로 넘어갑니다. | ||
left += 1; | ||
right -= 1; | ||
} | ||
|
||
// 6. 모든 순회가 끝냈다면 palindrome이라고 판단합니다. | ||
return true; | ||
|
||
function isValid(spell) { | ||
if ("0" <= spell && spell <= "9") { | ||
return true; | ||
} | ||
if ("a" <= spell && spell <= "z") { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
}; | ||
|
||
// 1차 | ||
// TC: O(N) | ||
// SC: O(N) | ||
// N: s.length | ||
|
||
/** | ||
* @param {string} s | ||
* @return {boolean} | ||
*/ | ||
var isPalindrome = function (s) { | ||
// 1. 문자열을 모두 소문자로 바꾸고 영소문자, 숫자만 남기고 모두 제거합니다. | ||
const phrase = s | ||
.toLowerCase() | ||
.split("") | ||
.filter( | ||
(spell) => | ||
("0" <= spell && spell <= "9") || ("a" <= spell && spell <= "z") | ||
); | ||
|
||
// 2. 양끝의 문자를 확인해서 palindrome인지 판별합니다. | ||
for (let index = 0; index < Math.floor(phrase.length / 2); index++) { | ||
if (phrase[index] !== phrase[phrase.length - index - 1]) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// 2차 | ||
// result flag 변수를 활용 => boolean 반환으로 개선 | ||
// TC: O(M * N * 4^W) | ||
// SC: O(MIN) | ||
// W: word.length, MIN: min(M * N, W) | ||
|
||
/** | ||
* @param {character[][]} board | ||
* @param {string} word | ||
* @return {boolean} | ||
*/ | ||
var exist = function (board, word) { | ||
for (let r = 0; r < board.length; r++) { | ||
for (let c = 0; c < board[0].length; c++) { | ||
if (dfs(r, c, 0)) { | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
|
||
function dfs(row, column, wordIndex) { | ||
if (!isValid(row, column)) { | ||
return false; | ||
} | ||
if (board[row][column] !== word[wordIndex]) { | ||
return false; | ||
} | ||
if (wordIndex === word.length - 1) { | ||
return true; | ||
} | ||
|
||
const temp = board[row][column]; | ||
board[row][column] = "#"; | ||
|
||
if ( | ||
dfs(row + 1, column, wordIndex + 1) || | ||
dfs(row - 1, column, wordIndex + 1) || | ||
dfs(row, column + 1, wordIndex + 1) || | ||
dfs(row, column - 1, wordIndex + 1) | ||
) { | ||
return true; | ||
} | ||
|
||
board[row][column] = temp; | ||
|
||
return false; | ||
} | ||
|
||
function isValid(row, column) { | ||
if (row < 0 || board.length <= row) { | ||
return false; | ||
} | ||
if (column < 0 || board[0].length <= column) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
}; | ||
|
||
// 1차 | ||
// TC: O(M * N * 4^W) | ||
// SC: O(MIN) | ||
// W: word.length, MIN: min(M * N, W) | ||
|
||
/** | ||
* @param {character[][]} board | ||
* @param {string} word | ||
* @return {boolean} | ||
*/ | ||
var exist = function (board, word) { | ||
let result = false; | ||
|
||
// 1. 2차원 배열의 모든 좌표를 순회 | ||
for (let r = 0; r < board.length; r++) { | ||
for (let c = 0; c < board[0].length; c++) { | ||
dfs(r, c, 0); | ||
} | ||
} | ||
Comment on lines
+72
to
+80
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. 처음 result 라는 변수를 선언 하시고 시작하시는데 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. 오, 그럼 flag 변수를 사용하지 않을 수 있군요! |
||
|
||
return result; | ||
|
||
function dfs(row, column, wordIndex) { | ||
// 2. 범위를 벗어난 좌표인지 검증 | ||
if (!isValid(row, column)) { | ||
return; | ||
} | ||
// 3. word에서 찾고 있는 문자인지 확인 | ||
if (board[row][column] !== word[wordIndex]) { | ||
return; | ||
} | ||
// 4. word 문자를 다 찾았는지 확인 | ||
if (wordIndex === word.length - 1) { | ||
result = true; | ||
return; | ||
} | ||
|
||
// 5. 방문한 좌표를 표시하기 위해 다른 문자로 바꿔치기 해둠 | ||
const temp = board[row][column]; | ||
board[row][column] = "#"; | ||
// 6. 상하좌우로 좌표 방문 | ||
dfs(row + 1, column, wordIndex + 1); | ||
dfs(row - 1, column, wordIndex + 1); | ||
dfs(row, column + 1, wordIndex + 1); | ||
dfs(row, column - 1, wordIndex + 1); | ||
// 7. 해당 좌표의 재귀 방문이 끝나면 미방문으로 표시하기 위해 원래 문자로 되돌리기 | ||
board[row][column] = temp; | ||
} | ||
|
||
// 유효한 좌표인지 확인하는 함수 | ||
function isValid(row, column) { | ||
if (row < 0 || board.length <= row) { | ||
return false; | ||
} | ||
if (column < 0 || board[0].length <= column) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
}; |
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.
설명과 결론이 잘 부합하지 않는 것 같은데요? 중첩 루프 잖아요?
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.
다시 보니 설명이 부족해서 오해를 살 수 있었던 것 같습니다!
N^2이 아닌 2N으로 생각한 이유는 2번에서 N번의 순회를 하지만 각 순회가 서로에게 영향을 미치기 때문에 최대 순회는 2N으로 계산했습니다. (1번의 N 순회 제외)