├── .gitignore
├── README.md
├── binary-search-tree
└── README.md
├── build-tool
├── README.md
└── solution.py
├── counting-islands
├── README.md
└── solution.py
├── intersection
└── README.md
├── joker.jpg
├── kth-largest
└── README.md
├── largest-product
└── README.md
├── letter-board
└── README.md
├── merge-intervals
├── README.md
└── solution.py
├── mergesort
├── README.md
└── solution.py
├── monotonic
├── README.md
├── solution.go
└── solution.py
├── reverse-collection
└── README.md
├── reverse-html
├── README.md
├── solution.py
└── solution2.py
├── shift-integers
├── README.md
└── solution.py
├── starts-in-group
└── README.md
├── subsequence
├── README.md
└── solution.py
├── target-number
├── README.md
└── solution-sinwoobang.py
├── tic-tac-toe
├── README.md
└── solution.py
└── two-stack-queue
├── README.md
└── solution.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | .mypy_cache
3 | .pytest_cache
4 | *.swp
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Interview Questions
2 |
3 | Job interviews sometimes can be a daunting task. Especially when you're
4 | confronted with those problems with algorithms and data structures.
5 |
6 | I'm sure you've been through a lot when you were in school. Linked lists,
7 | binary trees, directed acyclic graphs, hash maps, greedy algorithm, divide and
8 | conquer, dynamic programming, depth-first search, breadth-first search, and all
9 | those good stuff. Not to mention space and time complexity analysis.
10 |
11 | Even though computer science classes were a pain in the \*\*\*, somehow
12 | you managed to complete them all and made your friends and family proud of
13 | you. You will definitely remember all those all-nighter days with your
14 | classmates trying to build a compiler, a file system, and a network router. It
15 | was traumatizing. You realized you can push yourself much further than you
16 | thought possible.
17 |
18 | Even with years of hard work, you probably wouldn't claim yourself as a master
19 | of computer science, because you know there is much more to be learned. But at
20 | least you had some level of confidence that you understand the fundamentals of
21 | computer science.
22 |
23 | Soon after you got yourself a job as a software engineer. You started building
24 | awesome stuff. Years have passed by, and things got rusty. Even though you were
25 | writing code on a daily basis, you weren't always concerned with implementing
26 | algorithms and data structures yourself. In many cases, you were working on the
27 | top of some libraries and frameworks to get things done.
28 |
29 | More years have passed by, and you started thinking about taking a different
30 | set of challenges outside your company. You started responding to recruiting
31 | messages on LinkedIn that you have been ignoring. Writing a new resume, phone
32 | screening, and online coding tests are usual hurdles to jump over.
33 |
34 | Then here we go. You made all the way through the final round of your hiring
35 | process and you were invited to an on-site interview. You are standing in front
36 | of a big whiteboard. Your interviewer is asking you to write some code to merge
37 | two lists of sorted integers. It is supposed to be an *easy* problem, but
38 | writing code on a whiteboard without syntax highlighting, without
39 | auto-complete, without any assistance from a compiler or an interpreter
40 | whatsoever is a completely different experience when compared to writing code
41 | with a highly advanced integrated development environment (IDE) that reads your
42 | mind to write as much as half of the code on your behalf.
43 |
44 |
45 |
46 |
47 |
48 | I recently had serveral job interviews with different companies. Some
49 | interviews went well, others were a bit more challenging than they are supposed
50 | to be. I've got a few offers and one rejection so far. Some are still
51 | in progress and one of them is highly likely going to turn out as a rejection.
52 |
53 | I was very lucky that one of the companies that I had an interview with gave me
54 | detailed feedback. They shared comments from each of the four interviewers, which
55 | is very unusual. In essence, they had a positive evaluation of the behavioral
56 | interview (communication skills, personality, cultural fit, leadership, etc.)
57 | and the system design interview (designing scalable systems at extreme sizes),
58 | but they had some doubts about my coding abilities. Regardless of the outcome,
59 | I sincerely appreciate that kind of feedback. That's what keeps me moving
60 | forward.
61 |
62 | It's okay to fail. It's okay to make mistakes. As [Dr.
63 | Hong](http://www.romela.org/dr-dennis-hong/) said, *you can't always win, but
64 | you can always learn*. I've created this repository in an attempt to *learn
65 | from mistakes*.
66 |
67 |
68 | ## This Repository
69 |
70 | This repository contains some on-site interview questions that I have
71 | encountered throughout the last few years. I'm still working on providing
72 | solutions for some of the problems. No particular company name is mentioned,
73 | but those questions are general enough that they can be asked in any tech
74 | interview.
75 |
76 | The solutions are the best recollection of mime, but could be slightly
77 | different from what I actually wrote down on a whiteboard during the interview.
78 | There is plenty of room for improvement for each solution I provided, but I
79 | wanted to show what kind of code I was able to write under a harsh environment
80 | (a whiteboard, time constraints, psychological pressures, etc.)
81 |
82 |
83 | ## Practice
84 |
85 | Keep in mind that:
86 |
87 | Getting a complete, working solution is important. Pseudo-code is not enough.
88 | Interviewers are generally okay with any language of your choice among popular
89 | ones (C/C++, Java, Python, etc.) but I'd avoid using bizarre languages like
90 | [Whitespace](https://en.wikipedia.org/wiki/Whitespace_(programming_language)),
91 | [Brainfuck](https://en.wikipedia.org/wiki/Brainfuck), or
92 | [아희](https://aheui.github.io/specification.en). No matter what language you
93 | use, practice to get *working* code with some time limit. Try to get all the
94 | boundary conditions right.
95 |
96 | You are generally expected to write code on a whiteboard or on a basic text
97 | editor without syntax highlighting. You should be able to run the code in your
98 | head comfortably. It may be acceptable to write some fuzzy code that partially
99 | works then gradually improve by running the code with a compiler or an
100 | interpreter in a real work environment. But that is not how things work at a
101 | job interview. For this reason, writing code on an actual whiteboard or a piece
102 | of paper is the best way to prepare yourself for tech interviews. If you still
103 | insist typing code on your favorite editor, at least try to write code at the
104 | entirety, improve the code if possible, simulate all possible edge cases, fix
105 | any bug you found. When you think all is good, run your code to see if your
106 | code really works.
107 |
108 | Last but not least, practice working under tight time constraint. Depending on
109 | the format of your interview and the level of complexity of the problems, you
110 | will be given 10-45 minutes per each problem. It is highly unlikely that you
111 | will encounter anything super complicated, but problems can be quite
112 | challenging under time constraint.
113 |
114 |
115 | ## Time Limits
116 |
117 | In many cases, a final round consists of multiple interview sessions. The
118 | longest one I have had so far was five sessions long. Each interview session
119 | runs for about 50-60 minutes. Generally, you and your interviewer will spend
120 | some time introducing yourself, with regards to your current role and your past
121 | experience, to the other and vice versa. Your interviewer may ask you some
122 | behavioral questions. Most interviewers want to leave a few minutes at the end
123 | of the session for you to ask some questions. That leaves about 30-40 minutes
124 | at most for the coding part itself.
125 |
126 | For each problem description, I wrote down how much time I had for that
127 | particular problem.
128 |
129 |
130 | ## Contribution
131 |
132 | Feel free to submit your solutions via pull requests. Some rules to follow:
133 |
134 | - Your solution must be contained in a single file.
135 | - Avoid relying on external libraries and frameworks.
136 | - File name should be in a format of `solution-{your GitHub username}.{ext}`.
137 | For example, if your GitHub username is `johndoe` and your solution is
138 | written in C++, your filename should be `solution-johndoe.cpp`
139 |
--------------------------------------------------------------------------------
/binary-search-tree/README.md:
--------------------------------------------------------------------------------
1 | Binary Search Tree
2 | ==================
3 | Given a binary tree, determine whether it is a binary search tree.
4 |
5 | Time limit: 20min
--------------------------------------------------------------------------------
/build-tool/README.md:
--------------------------------------------------------------------------------
1 | Build Toold
2 | ===========
3 |
4 | Problem: Suppose we are making a build tool that takes a list of paths
5 | containing source files. The build tool produces a list of paths that it
6 | actually has to work on.
7 |
8 | When building code for a particular directory, source files in all its
9 | subdirectories are handled. For example, both `/a/b/c` and `/a/b` are given,
10 | the build tool only needs to work on `/a/b` as all files under `/a/b/c` are
11 | already handled when working on `/a/b`.
12 |
13 | The build tools is also expected to perform deduplications of directories.
14 |
15 | Time limit: 40 min
16 |
--------------------------------------------------------------------------------
/build-tool/solution.py:
--------------------------------------------------------------------------------
1 | class Node(object):
2 |
3 | def __init__(self, name):
4 | self.name = name
5 | self.is_leaf = False
6 | self.children = {}
7 |
8 |
9 | def build(paths):
10 | head = Node('')
11 | for path in paths:
12 | node = head
13 | for name in path.split('/')[1:]:
14 | if name in node.children:
15 | node = node.children[name]
16 | else:
17 | child = Node(name)
18 | node.children[name] = child
19 | node = child
20 | node.is_leaf = True
21 |
22 | return head
23 |
24 |
25 | def traverse(node, prev=[], depth=0):
26 | if node.is_leaf:
27 | yield '/'.join(prev + [node.name])
28 | else:
29 | for key, child in node.children.items():
30 | for x in traverse(child, prev + [node.name], depth + 1):
31 | yield x
32 |
33 |
34 | def test_traverse():
35 | paths = [
36 | '/a/b/c',
37 | '/a/b/d',
38 | '/a/b',
39 | '/c',
40 | '/c',
41 | ]
42 |
43 | result = list(traverse(build(paths)))
44 | assert result == ['/a/b', '/c']
45 |
--------------------------------------------------------------------------------
/counting-islands/README.md:
--------------------------------------------------------------------------------
1 | Counting Islands
2 | ================
3 |
4 | Problem: A world is described as an `n`-by-`m` 2D array of `0`s and `1`s. `0`
5 | and `1` represent water and land respectively. An islands is defined as a chunk
6 | of land surrounded by water.
7 |
8 | Either horizontally or vertically consecutive series of `1`s is considered as a
9 | distinct island. Diagonally adjacent `1`s are not considered as a single
10 | island.
11 |
12 | For example, the following world contains five islands.
13 |
14 | ```
15 | 1 1 0 1 0 0 1 0
16 | 1 0 0 1 1 1 0 0
17 | 0 0 1 1 1 0 0 1
18 | 0 0 1 1 1 0 0 1
19 | 1 0 0 1 0 0 1 1
20 | 1 1 1 0 0 1 1 0
21 | ```
22 |
23 | You may assume that no island contains a lake (water surrounded by land).
24 |
25 | Time limit: 45 min
26 |
--------------------------------------------------------------------------------
/counting-islands/solution.py:
--------------------------------------------------------------------------------
1 | def count_islands(world, x, y):
2 | h = len(world)
3 | w = len(world[0])
4 |
5 | count = 0
6 | for j in range(0, h):
7 | for i in range(0, w):
8 | if world[j][i] != 0:
9 | count += 1
10 | mark_world(world, i, j)
11 |
12 | return count
13 |
14 |
15 | def mark_world(world, x, y):
16 | queue = []
17 | queue.append((x, y))
18 | while queue:
19 | i, j = queue.pop(0)
20 | if in_boundary(world, i, j):
21 | if world[j][i] != 0:
22 | world[j][i] = 0
23 | queue.append((i - 1, j))
24 | queue.append((i + 1, j))
25 | queue.append((i, j - 1))
26 | queue.append((i, j + 1))
27 |
28 |
29 | def in_boundary(world, x, y):
30 | h = len(world)
31 | w = len(world[0])
32 |
33 | return (0 <= x < w) and (0 <= y < h)
34 |
35 |
36 | def test_1():
37 | world = [
38 | [0, 1, 0],
39 | [1, 1, 0],
40 | [0, 0, 1],
41 | ]
42 | assert count_islands(world, 0, 0) == 2
43 |
44 |
45 | def test_2():
46 | world = [
47 | [1, 1, 0, 1, 0, 0, 1, 0],
48 | [1, 0, 0, 1, 1, 1, 0, 0],
49 | [0, 0, 1, 1, 1, 0, 0, 1],
50 | [0, 0, 1, 1, 1, 0, 0, 1],
51 | [1, 0, 0, 1, 0, 0, 1, 1],
52 | [1, 1, 1, 0, 0, 1, 1, 0],
53 | ]
54 | assert count_islands(world, 0, 0) == 5
55 |
--------------------------------------------------------------------------------
/intersection/README.md:
--------------------------------------------------------------------------------
1 | Intersection
2 | ============
3 |
4 | Given two sorted lists of integers, find the intersection of the two lists.
5 |
6 | For example, suppose two lists are given as:
7 |
8 | xs = [1, 2, 3, 5, 8, 13, 21, 34]
9 | ys = [3, 6, 9, 12, 15, 18, 21]
10 |
11 | Then your solution should return `[3, 21]`.
12 |
13 | Time limit: 10 min
14 |
15 | Variation
16 | ---------
17 | What if one list is significantly larger than the other? For example, one
18 | contains billions of elements whereas the other only contains several. Would
19 | it possible to come up with a more efficient solution?
20 |
--------------------------------------------------------------------------------
/joker.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suminb/interview-questions/1afe34345aae7b9ba311e20888b392bb0c28ab3b/joker.jpg
--------------------------------------------------------------------------------
/kth-largest/README.md:
--------------------------------------------------------------------------------
1 | k-th Largest Element
2 | ====================
3 |
4 | Problem: Given a list of unsorted integers, find the k-th largest element in
5 | linear time.
6 |
--------------------------------------------------------------------------------
/largest-product/README.md:
--------------------------------------------------------------------------------
1 | Largest Product
2 | ===============
3 |
4 | Given a list of unsorted integers and a positive integer `k`, draw `k` elements
5 | from the list that makes the largest product.
6 |
7 | Suppose the following input is given,
8 |
9 | [1, -9, 5, 0, 7, 3, -8, -1], k = 3
10 |
11 | Then we would like to return `[-9, -8, 7]`. You may assume `k` is equal to or less than the number of elements in the list.
12 |
13 | What are the time complexity and the space complexity of your solution?
14 |
15 | Time limit: 20 min
16 |
--------------------------------------------------------------------------------
/letter-board/README.md:
--------------------------------------------------------------------------------
1 | Letter Board
2 | ============
3 |
4 | NOTE: This was for an online coding test, rather than for an on-site interview.
5 | I was provided with a basic editor, [CoderPad](https://coderpad.io), with no
6 | typical IDE-like features whatsoever. The only thing comes in handy was
7 | auto-indentation.
8 |
9 | Problem: Given an `N` by `N` board with letters and a word, determine whether
10 | the word appears on the board.
11 |
12 | - You may traverse in any (up, down, right, left, all diagonal) direction
13 | - You may change the direction at each step
14 | - You may *not* re-use a letter at the same location
15 |
16 | For example, a board may look like this:
17 |
18 | ```
19 | T Z J Q
20 | A R E E
21 | P B A S
22 | C F X H
23 | ```
24 |
25 | Then you will find words like `TREE`, `TAR`, `BEE`, `SEA`, and so on.
26 |
27 | Time limit: 45 min
28 |
--------------------------------------------------------------------------------
/merge-intervals/README.md:
--------------------------------------------------------------------------------
1 | Merge Intervals
2 | ===============
3 |
4 | Problem 1: Merge two intervals, where an interval is defined as a tuple of
5 | `[min, max]`. If those two intervals have no *overlapping*, return the original
6 | input as-is. The output is a list of intervals.
7 |
8 | Suppose two intervals are given as `[1, 3]`, `[2, 5]` then the merged interval
9 | is `[[1, 5]]`. When `[1, 2]`, `[5, 7]` are given, then the output shall be
10 | `[[1, 2], [5, 7]]`.
11 |
12 | Problem 2: Merge `k` intervals.
13 |
14 | Time limit: 15 min
15 |
--------------------------------------------------------------------------------
/merge-intervals/solution.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def merge(interval1, interval2):
5 | min1, max1 = interval1
6 | min2, max2 = interval2
7 |
8 | min_ = min(min1, min2)
9 | max_ = max(max1, max2)
10 |
11 | if max_ - min_ <= (max1 - min1) + (max2 - min2):
12 | return [(min_, max_)]
13 | else:
14 | return [interval1, interval2]
15 |
16 |
17 | def merge_all(*intervals):
18 | raise NotImplemented
19 |
20 |
21 | @pytest.mark.parametrize('interval1, interval2, expected', [
22 | ((1, 3), (2, 5), [(1, 5)]),
23 | ((3, 8), (2, 5), [(2, 8)]),
24 | ((1, 2), (5, 7), [(1, 2), (5, 7)]),
25 | ((5, 6), (1, 2), [(5, 6), (1, 2)]),
26 | ])
27 | def test_merge(interval1, interval2, expected):
28 | assert merge(interval1, interval2) == expected
29 |
30 |
31 | @pytest.mark.skip
32 | def test_merge_all():
33 | intervals = [(1, 4), (2, 5), (3, 4), (7, 8), (8, 9)]
34 | expected = [(1, 5), (7, 9)]
35 | assert merge_all(*intervals) == expected
36 |
--------------------------------------------------------------------------------
/mergesort/README.md:
--------------------------------------------------------------------------------
1 | Merge Sort
2 | ==========
3 |
4 | Problem 1: Given two lists of sorted integers (in ascending order), merge
5 | them into a single list.
6 |
7 | Problem 2: Discuss an algorithm to merge `k` lists. Analyze time complexity of
8 | the algorithm. Can you make it faster? (Hint: *O(nk log k)* is possible)
9 |
10 | Problem 3: Implement merge sort algorithm using the function above.
11 |
12 | Time limit: 45 min
13 |
--------------------------------------------------------------------------------
/mergesort/solution.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def merge(l1, l2):
5 | i = j = 0
6 | n, m = len(l1), len(l2)
7 |
8 | merged = []
9 | while i < n and j < m:
10 | if l1[i] < l2[j]:
11 | merged.append(l1[i])
12 | i += 1
13 | else:
14 | merged.append(l2[j])
15 | j += 1
16 |
17 | return merged + l1[i:] + l2[j:]
18 |
19 |
20 | def sort(ls):
21 | n = len(ls)
22 | if n > 1:
23 | mid = n // 2 # integer division
24 | return merge(sort(ls[:mid]), sort(ls[mid:]))
25 | else:
26 | return ls
27 |
28 |
29 | @pytest.mark.parametrize('l1, l2, expected', [
30 | ([], [], []),
31 | ([], [1, 2, 3], [1, 2, 3]),
32 | ([0, 1, 2], [], [0, 1, 2]),
33 | ([1, 3, 5, 7], [2, 4, 6], [1, 2, 3, 4, 5, 6, 7]),
34 | ])
35 | def test_merge(l1, l2, expected):
36 | assert merge(l1, l2) == expected
37 |
38 |
39 | @pytest.mark.parametrize('ls, expected', [
40 | ([], []),
41 | ([1], [1]),
42 | ([2, 1], [1, 2]),
43 | ([3, 0, 4, -5, 2, 7, 8], [-5, 0, 2, 3, 4, 7, 8]),
44 | ])
45 | def test_sort(ls, expected):
46 | assert sort(ls) == expected
47 |
--------------------------------------------------------------------------------
/monotonic/README.md:
--------------------------------------------------------------------------------
1 | Monotonic Lists
2 | ===============
3 |
4 | Problem: Given a list of integers, determine whether the list is monotonic.
5 | That is, determine whether its elements are either never-increasing or
6 | never-decreasing. In mathematical sense, *never-increasing* means
7 | *ai ≥ ai+1* whereas *never-decreasing* means
8 | *ai ≤ ai+1*.
9 |
10 | Examples of monotonic lists:
11 |
12 | - `[1, 2, 3, 4]`
13 | - `[4, 3, 2, 1]`
14 | - `[0, 0, 0, 0]`
15 |
16 | Examples of non-monotonic lists:
17 |
18 | - `[1, 2, 1, 2]`
19 |
20 | Time limit: 10 min
21 |
--------------------------------------------------------------------------------
/monotonic/solution.go:
--------------------------------------------------------------------------------
1 | // NOTE: I've written this code without any prior Go experience whatsoever, so
2 | // there is most likely plenty of room for improvement. I didn't write code in
3 | // Go in the real interview :p
4 |
5 | package main
6 |
7 | func isMonotonic(list []int) bool {
8 | n := len(list)
9 | if n <= 2 {
10 | return true
11 | } else {
12 | diff := make([]int, n-1)
13 | for i := 0; i < n-1; i++ {
14 | diff[i] = list[i+1] - list[i]
15 | }
16 |
17 | flag1, flag2 := true, true
18 | for i := 0; i < n-1; i++ {
19 | flag1 = flag1 && diff[i] <= 0
20 | flag2 = flag2 && diff[i] >= 0
21 | }
22 |
23 | return flag1 || flag2
24 | }
25 | }
26 |
27 | func assert(v bool) {
28 | if !v {
29 | panic("Assertion failed")
30 | }
31 | }
32 |
33 | func main() {
34 | assert(isMonotonic([]int{}))
35 | assert(isMonotonic([]int{0}))
36 | assert(isMonotonic([]int{1, 2, 3, 4}))
37 | assert(isMonotonic([]int{4, 3, 2, 1}))
38 | assert(isMonotonic([]int{0, 0, 0, 0}))
39 | assert(!isMonotonic([]int{1, 2, 1, 2}))
40 | }
41 |
--------------------------------------------------------------------------------
/monotonic/solution.py:
--------------------------------------------------------------------------------
1 | def is_monotonic(xs):
2 | delta = [x - y for x, y in zip(xs[1:], xs[:-1])]
3 | return all([d >= 0 for d in delta]) or all([d <= 0 for d in delta])
4 |
5 |
6 | def test_is_monotonic():
7 | assert is_monotonic([1, 2, 3, 4])
8 | assert is_monotonic([4, 3, 2, 1])
9 | assert is_monotonic([0, 0, 0, 0])
10 | assert not is_monotonic([1, 2, 1, 2])
11 |
--------------------------------------------------------------------------------
/reverse-collection/README.md:
--------------------------------------------------------------------------------
1 | Functionally Reverse Collection
2 | ===============================
3 |
4 | Given an interface `Collection` that provides the following functionalities,
5 | write a function to reverse a collection.
6 |
7 | ```java
8 | interface Collection {
9 | boolean isEmpty();
10 | T first();
11 | T last();
12 | Collection dropFirst();
13 | Collection dropLast();
14 | Collection append(T);
15 | Collection empty();
16 | }
17 | ```
18 |
19 | Time limit: 10min
20 |
--------------------------------------------------------------------------------
/reverse-html/README.md:
--------------------------------------------------------------------------------
1 | Reverse HTML
2 | ============
3 |
4 | Problem: Reverse an HTML document. Text and HTML shall be reversed but HTML
5 | code must be kept in a valid form.
6 |
7 | - Input example: `Hello World`
8 | - Output example: `dlroW olleH`
9 |
10 | You may assume:
11 |
12 | - HTML elements contain no attributes.
13 | - HTML elements always have opening and closing. No single element
14 | (e.g., `
`).
15 |
16 | Time limit: 40 min
17 |
--------------------------------------------------------------------------------
/reverse-html/solution.py:
--------------------------------------------------------------------------------
1 | # NOTE: I don't think I can jot this down on a whiteboard in 40
2 | # minutes, and that leads me to believe that there should be a
3 | # simpler solution.
4 |
5 |
6 | class Node(object):
7 |
8 | def __init__(self, parent, mode, data):
9 | self.mode = mode
10 | self.data = data
11 | self.parent = parent
12 | self.children = []
13 |
14 |
15 | class Token(object):
16 |
17 | def __init__(self, mode, data):
18 | self.mode = mode
19 | self.data = data
20 |
21 | def __repr__(self):
22 | if self.mode == 'text':
23 | return self.data
24 | elif self.mode == 'tag_open':
25 | return '<{0}>'.format(self.data)
26 | elif self.mode == 'tag_close':
27 | return '{0}>'.format(self.data)
28 |
29 | def __eq__(self, other):
30 | return self.mode == other.mode and self.data == other.data
31 |
32 |
33 | def reverse(html):
34 | tokens = tokenize(html)
35 | root = Node(None, 'text', '')
36 | build(tokens, root)
37 |
38 | reversed_tokens = []
39 | traverse(root, reversed_tokens)
40 |
41 | return ''.join([str(t) for t in reversed_tokens])
42 |
43 |
44 | def traverse(node, tokens):
45 | if node.mode == 'tag_open':
46 | tokens.append(Token('tag_open', node.data))
47 | for child in node.children[::-1]:
48 | traverse(child, tokens)
49 | tokens.append(Token('tag_close', node.data))
50 | elif node.mode == 'text':
51 | tokens.append(Token(node.mode, node.data[::-1]))
52 | for child in node.children[::-1]:
53 | traverse(child, tokens)
54 |
55 |
56 | def build(tokens, prev_node):
57 | if tokens:
58 | token = tokens[0]
59 | node = Node(prev_node, token.mode, token.data)
60 | if token.mode == 'text':
61 | prev_node.children.append(node)
62 | build(tokens[1:], prev_node)
63 | elif token.mode == 'tag_open':
64 | prev_node.children.append(node)
65 | build(tokens[1:], node)
66 | elif token.mode == 'tag_close':
67 | # prev_node.append(node)
68 | build(tokens[1:], node.parent)
69 |
70 |
71 | def tokenize(html):
72 | tokens = []
73 | text = ''
74 | tag = ''
75 | mode = 'text'
76 | for c1, c2 in zip(html, html[1:] + ' '):
77 | if c1 == '<':
78 | if text:
79 | tokens.append(Token(mode, text))
80 | text = ''
81 |
82 | if c2 == '/':
83 | mode = 'tag_close'
84 | else:
85 | mode = 'tag_open'
86 | elif c1 == '/':
87 | pass
88 | elif c1 == '>':
89 | tokens.append(Token(mode, tag))
90 | tag = ''
91 | mode = 'text'
92 | else:
93 | if mode == 'text':
94 | text += c1
95 | elif mode in ['tag_open', 'tag_close']:
96 | tag += c1
97 |
98 | if mode == 'text' and text:
99 | tokens.append(Token(mode, text))
100 | elif mode == 'tag_close' and tag:
101 | tokens.append(Token(mode, tag))
102 |
103 | return tokens
104 |
105 |
106 | def test_tokenize():
107 | data = 'Hello World'
108 | expected = [
109 | Token('text', 'Hello '),
110 | Token('tag_open', 'i'),
111 | Token('text', 'W'),
112 | Token('tag_open', 'b'),
113 | Token('text', 'orld'),
114 | Token('tag_close', 'b'),
115 | Token('tag_close', 'i'),
116 | ]
117 | assert tokenize(data) == expected
118 |
119 |
120 | def test_1():
121 | data = 'Reverse HTML'
122 | expected = 'LMTH esreveR'
123 | assert reverse(data) == expected
124 |
125 |
126 | def test_2():
127 | data = 'Hello World'
128 | expected = 'dlroW olleH'
129 | assert reverse(data) == expected
130 |
--------------------------------------------------------------------------------
/reverse-html/solution2.py:
--------------------------------------------------------------------------------
1 | # This is a second trial. This time, I wrote down the code on a piece of paper,
2 | # trying to make it as completely as possible, and then copied it to a text
3 | # editor. Besides a couple of typing errors and one minor mistake that can be
4 | # easily found and corrected, everything worked as expected. That one minor
5 | # mistake was this:
6 | #
7 | # I passed `tag_buf` when creating `Tag` instance where I was supposed to pass
8 | # `''.join(tag_buf)`.
9 | #
10 | # This is definitely easier than building a tree and traversing it, and I was
11 | # able to finish coding (on paper) in about 15 minutes or so.
12 |
13 | # TODO: This violates the file naming convention mentioned in README. Need to
14 | # come up with a suitable name.
15 |
16 | class Tag:
17 |
18 | def __init__(self, tag, opening=True):
19 | self.tag = tag
20 | self.opening = opening
21 |
22 | def __str__(self):
23 | if self.opening:
24 | return f'<{self.tag}>'
25 | else:
26 | return f'{self.tag}>'
27 |
28 | def flip_mode(self):
29 | self.opening = not self.opening
30 | return self
31 |
32 |
33 | def tokenize(html):
34 | tag_buf = []
35 | tag_mode = None
36 | tokens = []
37 |
38 | for c in html:
39 | if tag_mode:
40 | if c == '/':
41 | tag_mode = 'close'
42 | elif c == '>':
43 | tokens.append(Tag(''.join(tag_buf), tag_mode == 'open'))
44 | tag_buf = []
45 | tag_mode = None
46 | else:
47 | tag_buf.append(c)
48 | elif c == '<':
49 | tag_mode = 'open'
50 | else:
51 | tokens.append(c)
52 |
53 | return tokens
54 |
55 |
56 | def reverse_tokens(tokens):
57 | def process(token):
58 | if isinstance(token, Tag):
59 | return str(token.flip_mode())
60 | else:
61 | return token
62 |
63 | return ''.join([process(t) for t in tokens[::-1]])
64 |
65 |
66 | def reverse(html):
67 | return reverse_tokens(tokenize(html))
68 |
69 | def test_1():
70 | data = 'Reverse HTML'
71 | expected = 'LMTH esreveR'
72 | assert reverse(data) == expected
73 |
74 |
75 | def test_2():
76 | data = 'Hello World'
77 | expected = 'dlroW olleH'
78 | assert reverse(data) == expected
79 |
--------------------------------------------------------------------------------
/shift-integers/README.md:
--------------------------------------------------------------------------------
1 | Shift Integers
2 | ==============
3 |
4 | Problem: Given a list of integers, shift non-zeros to the left (or shift zeros
5 | to the right). The order of elements must be preserved. For example, suppose we
6 | are given a list of integers like:
7 |
8 | [1, 3, -4, 0, 9, 2, 0, 5]
9 |
10 | Then the outcome is expected to be:
11 |
12 | [1, 3, -4, 9, 2, 5, 0, 0]
13 |
14 | Expected time complexity: O(n)
15 | Expected space complexity: O(1)
16 |
17 | Time limit: 15 min
18 |
--------------------------------------------------------------------------------
/shift-integers/solution.py:
--------------------------------------------------------------------------------
1 | def shift(ls):
2 | i = c = 0
3 | n = len(ls)
4 |
5 | while i < n:
6 | m = ls[i]
7 | if c > 0:
8 | ls[i - c] = ls[i]
9 | if m == 0:
10 | c += 1
11 | i += 1
12 |
13 | for i in range(n - c, n):
14 | ls[i] = 0
15 |
16 | return ls
17 |
18 |
19 | def test():
20 | data = [1, 3, -4, 0, 9, 2, 0, 5]
21 | expected = [1, 3, -4, 9, 2, 5, 0, 0]
22 | assert shift(data) == expected
23 |
--------------------------------------------------------------------------------
/starts-in-group/README.md:
--------------------------------------------------------------------------------
1 | Stars In Group
2 | ==============
3 |
4 | Problem: There is an N by N grid and a set of letters in the grid. The value of
5 | `N` ranges from `MIN_INT` to `MAX_INT`, so in a 64-bit architecture the grid
6 | would represent a quite large space and the letters are sparsely located. A
7 | group is defined as adjacent letters in the grid, except that diagonally
8 | neighboring letters are not considered belonging to the same group. Given a
9 | coordinate `(x, y)`, write a function to return a list of all letters in the
10 | group to which the letter is belonging.
11 |
12 | For example, let's suppose the letters are located as follows:
13 |
14 | (-1, 0): A
15 | (0, 0): B
16 | (0, 1): C
17 | (2, 0): X
18 | (3, 1): Y
19 |
20 | Visually speaking, letters are located as:
21 |
22 | C Y
23 | A B X
24 |
25 | Given a coordinate `(0, 0)`, all letters belonging in that particular group are
26 | A, B, and C and thus the function shall return `[A, B, C]`. If the coordinate
27 | is given as `(2, 0)` then the function should return `[X]` as `Y` is diagonally
28 | adjacent to `X` and hence belonging to a different group. If a coordinate of an
29 | empty space, say `(1, 1)`, if given then the function should return an empty
30 | list.
31 |
32 | Time limit: 20 min
--------------------------------------------------------------------------------
/subsequence/README.md:
--------------------------------------------------------------------------------
1 | Consecutive Subsequence
2 | =======================
3 |
4 | Problem: Given a list of integers and a target number, which is an integer,
5 | determine whether there exists a consecutive subsequence that adds up to the
6 | target number. You may assume all integers in the list are non-negative for
7 | now.
8 |
9 | For example,
10 |
11 | list = [1, 4, 5, 3, 2], target = 8
12 |
13 | then your function returns `true`. If there is no such subsequence that adds
14 | up to `target` then it returns `false`.
15 |
16 | Time limit: 15 min
17 |
18 | Variation 1
19 | -----------
20 | Find the subsequence. For example,
21 |
22 | list = [1, 4, 5, 3, 2], target = 8
23 |
24 | then your function returns `[5, 3]`. If there are multiple subsequences
25 | satisfying the constraint, you may return one of the valid solutions.
26 |
27 | Variation 2
28 | -----------
29 |
30 | The list may contain negative numbers. Hint: there is a solution that runs in linear time.
31 |
--------------------------------------------------------------------------------
/subsequence/solution.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | # TODO: Deal with negative integers
5 | def find(seq, target):
6 | n = len(seq)
7 | s = 0
8 | i = j = 0
9 |
10 | while True:
11 | if s == target:
12 | break
13 | elif s < target and j < n:
14 | s += seq[j]
15 | j += 1
16 | elif s > target and i < n:
17 | s -= seq[i]
18 | i += 1
19 |
20 | return seq[i:j]
21 |
22 |
23 | @pytest.mark.parametrize('seq, target, expected', [
24 | ([1, 2, 3, 4], -1, []),
25 | ([1, 2, 3, 4], 1, [1]),
26 | ([1, 2, 3, 4], 7, [3, 4]),
27 | ])
28 | def test_find(seq, target, expected):
29 | assert find(seq, target) == expected
30 |
--------------------------------------------------------------------------------
/target-number/README.md:
--------------------------------------------------------------------------------
1 | Target Number
2 | =============
3 |
4 | Problem: Given a list of sorted integers in ascending order and a target number
5 | `k` which is an integer, determine whether it is possible to pick two integers
6 | from the list that add up to the target number.
7 |
8 | For example,
9 |
10 | list = [-5, 0, 2, 3, 7, 8], k = 9
11 |
12 | then `2 + 7 = 9`, hence the answer is `true`. If `k = 1` the answer is `false`
13 | as no two numbers from the list add up to `1`.
14 |
15 | Time complexity: Should be faster than *O(n2)*.
16 |
17 | Time limit: 15 min
18 |
--------------------------------------------------------------------------------
/target-number/solution-sinwoobang.py:
--------------------------------------------------------------------------------
1 | """A solution using Set. Time Complexity is O(N)."""
2 | from typing import List, Set
3 |
4 |
5 | def is_possible(nums: List[int], k: int):
6 | nums_set: Set[int] = set(nums) # It takes O(n).
7 | for num in nums: # It takes O(n) as well.
8 | remain = k - num
9 | if remain in nums_set: # It takes O(1).
10 | return True
11 | return False
12 |
13 |
14 | def test_1():
15 | nums = [-5, 0, 2, 3, 7, 8]
16 | k = 9
17 | assert is_possible(nums, k)
18 |
19 |
20 | def test_2():
21 | nums = [-5, 0, 2, 3, 7, 8]
22 | k = 1
23 | assert not is_possible(nums, k)
24 |
--------------------------------------------------------------------------------
/tic-tac-toe/README.md:
--------------------------------------------------------------------------------
1 | Tic-tac-toe
2 | ===========
3 |
4 | Problem: Given an `n`-by-`n`
5 | [Tic-tac-toe](https://en.wikipedia.org/wiki/Tic-tac-toe) board, write a
6 | function to determine whether there is a winner.
7 |
8 | A winner is a player who succeeded in placing `n` of their marks (`O` or `X`)
9 | in a horizontal, vertical, or diagonal row.
10 |
11 | Time limit: 40 min
12 |
--------------------------------------------------------------------------------
/tic-tac-toe/solution.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | def judge(board) -> bool:
5 | """Make a judgement whether there is a winner.
6 |
7 | :param board: N-by-N array
8 | """
9 | if not board:
10 | return False
11 |
12 | n = len(board)
13 | # Check for horizontal lines
14 | if any([judge_line([board[i][j] for j in range(n)]) for i in range(n)]):
15 | return True
16 | # Check for vertical lines
17 | if any([judge_line([board[i][j] for i in range(n)]) for j in range(n)]):
18 | return True
19 | # Check for diagonal lines
20 | if judge_line([board[i][i] for i in range(n)]):
21 | return True
22 | if judge_line([board[i][n - i - 1] for i in range(n)]):
23 | return True
24 |
25 | return False
26 |
27 |
28 | def judge_line(line) -> bool:
29 | return all([x == y for x, y in zip(line[:-1], line[1:])])
30 |
31 |
32 | @pytest.mark.parametrize('board, expected', [
33 | ([], False),
34 | ([
35 | ['O'],
36 | ], True),
37 | ([
38 | ['O', 'X'],
39 | ['X', 'O'],
40 | ], True),
41 | ([
42 | ['O', 'X', 'O'],
43 | ['O', 'X', 'X'],
44 | ['X', 'X', 'O'],
45 | ], True),
46 | ([
47 | ['O', 'X', 'O', 'X'],
48 | ['O', 'X', 'X', 'O'],
49 | ['X', 'O', 'O', 'X'],
50 | ['X', 'O', 'O', 'X'],
51 | ], False),
52 | ])
53 | def test_judge(board, expected):
54 | actual = judge(board)
55 | assert expected == actual
--------------------------------------------------------------------------------
/two-stack-queue/README.md:
--------------------------------------------------------------------------------
1 | Two Stacks As Queue
2 | ===================
3 |
4 | Problem: Implement a queue using two stacks. Then discuss how things will work
5 | out in a multithreading environment.
6 |
7 | You don't need to implement a stack yourself.
8 |
9 | Time limit: 10 min
10 |
--------------------------------------------------------------------------------
/two-stack-queue/solution.py:
--------------------------------------------------------------------------------
1 | stack1, stack2 = [], []
2 |
3 |
4 | def enqueue(x):
5 | stack1.append(x)
6 |
7 |
8 | def dequeue():
9 | if stack2:
10 | return stack2.pop()
11 |
12 | while stack1:
13 | stack2.append(stack1.pop())
14 |
15 | return stack2.pop()
16 |
17 |
18 | def test_1():
19 | enqueue(1)
20 | enqueue(2)
21 | enqueue(3)
22 |
23 | assert dequeue() == 1
24 | assert dequeue() == 2
25 | assert dequeue() == 3
26 |
27 |
28 | def test_2():
29 | enqueue(1)
30 | assert dequeue() == 1
31 |
32 | enqueue(2)
33 | enqueue(3)
34 | assert dequeue() == 2
35 |
36 | enqueue(4)
37 | assert dequeue() == 3
38 | assert dequeue() == 4
39 |
--------------------------------------------------------------------------------