Skip to content
Merged
111 changes: 111 additions & 0 deletions alien-dictionary/forest000014.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
# Time Complexity: O(wl)
- w은 words의 길이, l은 words[i]의 길이
- word 하나를 trie에 등록하는 과정 O(l)
- 모든 word를 trie에 등록하는 과정 O(wl)
- graph를 순회하면서 위상정렬하는 과정 O(26^2) = O(1)
# Space Complexity: O(wl)
- trie의 공간복잡도 O(wl)
*/

class Solution {

private class TrieNode {
char ch;
boolean ends;
List<TrieNode> children;

TrieNode(char ch) {
this.ch = ch;
children = new ArrayList<>();
}
}

public String alienOrder(String[] words) {
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < 26; i++) {
graph.add(new ArrayList<>());
}
boolean[] visited = new boolean[26];
int[] inDegree = new int[26];
int[] outDegree = new int[26];
Queue<Character> queue = new LinkedList<>();

TrieNode root = new TrieNode('.');

for (int i = 0; i < words.length; i++) {
TrieNode curr = root;

for (int j = 0; j < words[i].length(); j++) {
// 유효한 순서가 아님이 확실하면, 곧바로 false를 리턴한다.
// 유효한 순서가 아님이 확실하지 않으면, trie에 추가하고, relation을 추가한다.
// 단, words[i]의 마지막 글자라면, trie의 마지막에 ends = true를 세팅한다.

char ch = words[i].charAt(j);
visited[ch - 'a'] = true;

if (curr.children.size() == 0) {
curr.children.add(new TrieNode(ch));
curr = curr.children.get(curr.children.size() - 1);
if (j == words[i].length()) curr.ends = true;
continue;
}

char lastCh = curr.children.get(curr.children.size() - 1).ch;
if (lastCh == ch) {
curr = curr.children.get(curr.children.size() - 1);
if (j == words[i].length() - 1) {
if (!curr.children.isEmpty()) return "";
else curr.ends = true;
}
continue;
}

for (int p = 0; p < curr.children.size() - 1; p++) {
if (curr.children.get(p).ch == ch) return "";
}

addEdge(graph, inDegree, outDegree, lastCh, ch);
curr.children.add(new TrieNode(ch));
curr = curr.children.get(curr.children.size() - 1);
}
}

for (int i = 0; i < 26; i++) {
if (inDegree[i] == 0 && outDegree[i] != 0) queue.offer((char)('a' + i));
}

StringBuilder sb = new StringBuilder();

for (int i = 0; i < 26; i++) {
if (visited[i] && inDegree[i] == 0 && outDegree[i] == 0) sb.append((char)('a' + i));
}

while (!queue.isEmpty()) {
char ch = queue.poll();
sb.append(ch);

for (int next : graph.get(ch - 'a')) {
if (--inDegree[next] == 0) queue.offer((char)('a' + next));
}
}

for (int i = 0; i < 26; i++) {
if (inDegree[i] > 0) return "";
}

return sb.toString();
}

private boolean addEdge(List<List<Integer>> graph, int[] inDegree, int[] outDegree, char from, char to) {
int sz = graph.get(from - 'a').size();
for (int i = 0; i < sz; i++) {
if (graph.get(from - 'a').get(i) == to - 'a') return false;
}

graph.get(from - 'a').add(to - 'a');
outDegree[from - 'a']++;
inDegree[to - 'a']++;
return true;
}
}
39 changes: 39 additions & 0 deletions course-schedule/forest000014.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
# Time Complexity: O(n)
# Space Complexity: O(n)
위상 정렬을 사용해서 풀었습니다.
*/

class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] inDegree = new int[numCourses];
Queue<Integer> queue = new LinkedList<>();
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
graph.add(new ArrayList<>());
}

for (int i = 0; i < prerequisites.length; i++) {
graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
inDegree[prerequisites[i][0]]++;
}

for (int i = 0; i < numCourses; i++) {
if (inDegree[i] == 0) queue.offer(i);
}

while (!queue.isEmpty()) {
int curr = queue.poll();

for (int next : graph.get(curr)) {
inDegree[next]--;
if (inDegree[next] == 0) queue.offer(next);
}
}

for (int i = 0; i < numCourses; i++) {
if (inDegree[i] > 0) return false;
}
return true;
}
}
29 changes: 29 additions & 0 deletions longest-palindromic-substring/forest000014.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
# Time Complexity: O(n^2)
# Spcae Complexity: O(1)
*/
class Solution {
public String longestPalindrome(String s) {
String ans = "";
for (int i = 0; i < s.length(); i++) {
int l = i;
int r = i;

while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
if (r - l + 1 > ans.length()) ans = s.substring(l, r + 1);
l--;
r++;
}

l = i;
r = i + 1;
while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
if (r - l + 1 > ans.length()) ans = s.substring(l, r + 1);
l--;
r++;
}
}

return ans;
}
}
50 changes: 50 additions & 0 deletions merge-k-sorted-lists/forest000014.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
# Time Complexity: O(nlogk)
- n은 lists[i].length의 합
# Space Complexity: O(k)
- pq에는 최대 k개의 원소가 저장됨
*/
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {

public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0) return null;

PriorityQueue<ListNode> pq = new PriorityQueue<>(new Comparator<ListNode>() {
@Override
public int compare(ListNode n1, ListNode n2) {
return n1.val - n2.val;
}
});
for (int i = 0; i < lists.length; i++) {
if (lists[i] == null) continue;
pq.offer(lists[i]);
}

ListNode head = next(pq);
ListNode curr = head;

while (!pq.isEmpty()) {
curr.next = next(pq);
curr = curr.next;
}

return head;
}

private ListNode next(PriorityQueue<ListNode> pq) {
ListNode top = pq.poll();
if (top == null) return null;
if (top.next != null) pq.offer(top.next);
return top;
}
}
54 changes: 54 additions & 0 deletions minimum-window-substring/forest000014.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
# Time Complexity: O(m * c)
- c는 문자열에 사용된 문자의 가짓수(= 소문자 26 + 대문자 26)
- 슬라이딩 윈도우로 s 전체를 훑는데 O(m)이 필요하고, 각 윈도우마다 t의 전체 문자가 포함되어 있는지 판단하는데 O(c)가 필요함
# Space Complexity: O(c)
- sMap(슬라이딩 윈도우에 포함된 문자 카운트), tMap(문자열 t에 포함된 문자 카운트) 각각 O(c)
*/
class Solution {
public String minWindow(String s, String t) {
Map<Character, Integer> sMap = new HashMap<>();
Map<Character, Integer> tMap = new HashMap<>();
for (int i = 0; i < t.length(); i++) {
tMap.merge(t.charAt(i), 1, Integer::sum);
}

String ans = "";
int ansLen = Integer.MAX_VALUE;
int l = 0;
int r = 0;
sMap.merge(s.charAt(0), 1, Integer::sum);
while (l <= r) {
if (included(sMap, tMap)) {
if (r - l + 1 < ansLen) {
ansLen = r - l + 1;
ans = s.substring(l, r + 1);
}
sMap.merge(s.charAt(l), -1, Integer::sum);
l++;
} else {
r++;
if (r >= s.length()) break;
sMap.merge(s.charAt(r), 1, Integer::sum);
}
}

while (l <= r && included(sMap, tMap)) {
if (r - l + 1 > ansLen) {
ansLen = r - l + 1;
ans = s.substring(l, r + 1);
}
sMap.merge(s.charAt(l), -1, Integer::sum);
l++;
}

return ans;
}

private boolean included(Map<Character, Integer> sMap, Map<Character, Integer> tMap) {
for (Character key : tMap.keySet()) {
if (!sMap.containsKey(key) || sMap.get(key) < tMap.get(key)) return false;
}
return true;
}
}
84 changes: 84 additions & 0 deletions pacific-atlantic-water-flow/forest000014.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
# Time Complexity: O(m * n * log(m * n))
# Space Complexity: O(m * n)
- visited, pq
DFS와 PQ를 조합하여 풀었습니다.
*/
class Solution {
private class Cell {
int r;
int c;
int h;

Cell(int r, int c, int h) {
this.r = r;
this.c = c;
this.h = h;
}
}

public List<List<Integer>> pacificAtlantic(int[][] heights) {
int m = heights.length;
int n = heights[0].length;
PriorityQueue<Cell> pq1 = new PriorityQueue<>(new Comparator<Cell>() {
@Override
public int compare(Cell c1, Cell c2) {
return c1.h - c2.h;
}
});
PriorityQueue<Cell> pq2 = new PriorityQueue<>(new Comparator<Cell>() {
@Override
public int compare(Cell c1, Cell c2) {
return c1.h - c2.h;
}
});
int[][] visited = new int[m][n];

for (int i = 0; i < m; i++) {
pq1.offer(new Cell(i, 0, heights[i][0]));
pq2.offer(new Cell(i, n - 1, heights[i][n - 1]));
visited[i][0] |= 1;
visited[i][n - 1] |= 2;
}
for (int i = 1; i < n; i++) {
pq1.offer(new Cell(0, i, heights[0][i]));
pq2.offer(new Cell(m - 1, i - 1, heights[m - 1][i - 1]));
visited[0][i] |= 1;
visited[m - 1][i - 1] |= 2;
}

int[] dr = {-1, 0, 1, 0};
int[] dc = {0, 1, 0, -1};
while (!pq1.isEmpty()) {
Cell curr = pq1.poll();
for (int i = 0; i < 4; i++) {
int nr = curr.r + dr[i];
int nc = curr.c + dc[i];
if (nr < 0 || nr >= m || nc < 0 || nc >= n || heights[nr][nc] < heights[curr.r][curr.c] || (visited[nr][nc] & 1) == 1) continue;
pq1.offer(new Cell(nr, nc, heights[nr][nc]));
visited[nr][nc] |= 1;
}
}
while (!pq2.isEmpty()) {
Cell curr = pq2.poll();
for (int i = 0; i < 4; i++) {
int nr = curr.r + dr[i];
int nc = curr.c + dc[i];
if (nr < 0 || nr >= m || nc < 0 || nc >= n || heights[nr][nc] < heights[curr.r][curr.c] || (visited[nr][nc] & 2) == 2) continue;
pq2.offer(new Cell(nr, nc, heights[nr][nc]));
visited[nr][nc] |= 2;
}
}

List<List<Integer>> ans = new ArrayList<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (visited[i][j] == 3) {
ans.add(new ArrayList<>(List.of(i, j)));
}
}
}

return ans;
}
}
25 changes: 25 additions & 0 deletions rotate-image/forest000014.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
# Time Complexity: O(n^2)
# Space Complexity: O(1)
*/
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;

for (int i = 0; i < n / 2; i++) {
for (int j = i; j < n - i - 1; j++) {
// [i][j] -> [j][n - i - 1]
// [j][n - i - 1] -> [n - i - 1][n - j - 1]
// [n - i - 1][n - j - 1] -> [n - j - 1][i]
// [n - j - 1][i] -> [i][j]
// (각 인덱스의 등장 횟수를 체크해서, 각각의 등장횟수가 4번임을 확인하면, 틀린 인덱스가 아님을 quick하게 체크해볼 수는 있음. 이 방법으로 맞다는 보장은 안 됨.)

int tmp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = tmp;
}
}
}
}
Loading