diff --git a/longest-consecutive-sequence/wogha95.js b/longest-consecutive-sequence/wogha95.js new file mode 100644 index 000000000..db247bedd --- /dev/null +++ b/longest-consecutive-sequence/wogha95.js @@ -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; +}; diff --git a/maximum-product-subarray/wogha95.js b/maximum-product-subarray/wogha95.js new file mode 100644 index 000000000..f46d314f0 --- /dev/null +++ b/maximum-product-subarray/wogha95.js @@ -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; +}; diff --git a/missing-number/wogha95.js b/missing-number/wogha95.js new file mode 100644 index 000000000..442da4b4c --- /dev/null +++ b/missing-number/wogha95.js @@ -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; +}; diff --git a/valid-palindrome/wogha95.js b/valid-palindrome/wogha95.js new file mode 100644 index 000000000..07de8d3a7 --- /dev/null +++ b/valid-palindrome/wogha95.js @@ -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(); + // 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; +}; diff --git a/word-search/wogha95.js b/word-search/wogha95.js new file mode 100644 index 000000000..0fee7db9b --- /dev/null +++ b/word-search/wogha95.js @@ -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); + } + } + + 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; + } +};