1
Notes for Preparing Coding Interview X Wang
Versio Ve rsion n 1.0
CONTENTS i
Freface
4
5 Java Questions 1 Top 10 Algorithms for Coding Interview 6 Reverse Polish Notation 15 2 LeetCode - Evaluate Reverse Longest Palindromic Substring in Java Java 18 3 Leetcode Solution of Longest 4 Leetcode Solution - Word Break 22 5 LeetCode - Word Ladder 24 6 LeetCode - Median of Two Two Sorted Arrays Arrays Java Java 28 7 LeetCode - Regular Regular Expression Matching in Java Java 30 8 LeetCode - Merge Merge Intervals 32 9 LeetCode - Insert Interval 34 10 LeetCode - Two Sum (Java) 36 11 LeetCode - 3 Sum 37 12 LeetCode - String to Integer (atoi) 40 13 LeetCode - Merge Sorted Array (Java) 42 14 LeetCode - Valid Parentheses (Java) 44 15 LeetCode - Implement strStr() (Java) 46 16 LeetCode - Set Matrix Zeroes (Java) 48 17 Leetcode solution - Add Two Numbers in Java 50 18 Reorder List in Java 54 19 Leetcode - Linked List Cycle 59 20 LeetCode - Copy List with Random Pointer 61 21 LeetCode - Merge Two Sorted Lists (Java) 64 22 LeetCode - Remove Duplicates from Sorted List 66 Traversal ersal in Java 68 23 Leetcode Solution for Binary Tree Preorder Trav 24 Leetcode Solution of Binary Tree Inorder Traversal in Java 70 25 Leetcode Solution of Iterative Binary Tree Postorder Traversal Traversal in Java 26 LeetCode - Word Ladder 74 27 LeetCode - Validate Binary Search Tree (Java) 78 28 LeetCode - Flatten Binary Tree to Linked List 80 29 LeetCode - Path Sum 82 30 Construct Binary Tree from Inorder and Postorder Trav Traversal ersal 84 31 LeetCode Clone Graph Java 86 32 LeetCode Solution - Merge Sort LinkedList in Java 89 33 Quicksort Array in Java 93 34 LeetCode Solution - Sort a linked list using insertion sort in Java 95 35 Iteration vs. Recursion in Java 98 ii
2
72
Contents 36 Edit
Distance in Java 101 37 Leetcode Solution of Longest Palindromic Substring in Java 38 Leetcode Solution - Word Break 108
104
3
Part I FREFACE
Part II J AVA Q U E S T I O N S
1 TOP
10 ALGORITHMS
FOR CODING INTERVIEW
Web Version, PDF Download Latest Update: 1/9/2014 The following are top 10 algorithms related topics for coding interviews. As understanding those concepts requires much more effort, this list below only serves as an introduction. They are viewed from a Java perspective and the following topics will be covered: String/Array, Linked List, Tree, Graph, Sorting, Recursion vs. Iteration, Dynamic Programming, Bit Manipulation, Probability, Com binations and Permutations, and other problems that need us to find patterns. 1.1
s tr in g/ array
First of all, String in Java is a class that contains a char array and other fields and methods. Without code auto-completion of any IDE, the following methods should be remembered. toCharArray ( ) / / g e t c h ar a r ra y o f a S t ri n g Arrays . so rt ( ) / / s o r t an a r r a y Arrays . toS tr in g ( char [ ] a ) / / c o n v er t t o s t r i n g charAt( i n t x ) / / g e t a c h a r a t t h e s p e c i f i c i n d e x length () / / s t r i n g l e n g t h length / / a r r a y s i z e substr ing ( i n t beginInde x ) substr ing ( i n t beginIndex , i n t endIndex) Int ege r . valueOf () / / s t r i n g t o i n t e g e r S t r i n g . v al ue Of ( ) / i n t e g e r t o s t r i n g
Strings/arrays are easy to understand, but questions related to them often require advanced algorithm to solve, such as dynamic programming, recursion, etc. Classic problems: Evaluate Reverse Polish Notation, Longest Palindromic Substring, Word Break, Word Ladder, Median of Two Sorted Arrays, Regular Expression Matching, Merge Intervals, Insert Interval, Two Sum, 3Sum, String to Integer, Merge Sorted Array, Valid Parentheses, Implement strStr()(), Set Matrix Zeroes. 1.2
l in ke d l is t
The implementation of a linked list is pretty simple in Java. Each node has a value and a link to next node.
6
1.2.
c l a s s Node { i n t val
; Node nex t ; Node( i n t x ) { v al = x ; n e x t = null ; }
}
Two popular applications of linked list are stack and queue. Stack class
Stack { Node top ; public Node peek () { i f ( t op ! = null ) { r e t u r n top
;
} r e t u rn n u ll ;
} public Node pop() { i f ( top == null ) { r e t u rn n u l l ; } e l s e {
Node temp = new Node( top . val ) ; top = top . next ; r e t u r n temp; } } p u b l ic v oi d push (Node n) i f ( n ! = null ) {
{
n. next = top ; top = n; } } }
Queue c l a s s Queue{
Node f i r s t , l a s t ; p u b l ic v oi d enqueue (Node n ) { i f ( f i r s t == null ) {
first = n; last = first ; } e l s e { l a s t . n ex t = n ;
LINKED LIST
7
1.3.
TREE
last = n; } } public
Node dequeue ( ) { i f ( f i r s t == null ) { r e t u rn n u l l ;
} e l s e { Node temp = new Node( f i r s t . val ) ; f i r s t = f i r s t . n ex t ; r e t u r n temp; } } }
It is worth to mention that Java standard library already contains a class called “Stack“, and LinkedList can be used as a Queue. (LinkedList implements the Queue interface) If you need a stack or queue to solve problems during your interview, you can directly use them. Classic Problems: Add Two Numbers, Reorder List, Linked List Cycle, Copy List with Random Pointer, Merge Two Sorted Lists, Remove Duplicates from Sorted List. 1.3
tree
Tree here is normally binary tree. Each node contains a left node and right node like the following: class
TreeNode{ i n t value ; TreeNode l e f t ; TreeNode ri gh t ;
}
Here are some concepts related with trees: •
•
•
•
•
Binary Search Tree: for all nodes, left children <= current node <= right children Balanced vs. Unbalanced: In a balanced tree, the depth of the left and right subtrees of every node differ by 1 or less. Full Binary Tree: every node other than the leaves has two children. Perfect Binary Tree: a full binary tree in which all leaves are at the same depth or same level, and in which every parent has two children. Complete Binary Tree: a binary tree in which every level, except possibly the last, is completely filled, and all nodes are as far left as possible
Classic problems: Binary Tree Preorder Traversal , Binary Tree Inorder Traversal, Binary Tree Postorder Traversal, Word Ladder, Validate Binary Search Tree, Flatten Binary Tree to Linked List, Path Sum, Construct Binary Tree from Inorder and Postorder Traversal.
8
1.4.
1.4
GRAPH
graph
Graph related questions mainly focus on depth first search and breath first search. Depth first search is straightforward, you can just loop through neighbors starting from the root node. Below is a simple implementation of a graph and breath first search. The key is using a queue to store nodes.
1) Define a GraphNode c l a s s GraphNode{ i n t val ;
GraphNode ne xt ; GraphNode[ ] neig hbors ; boolean v i s i t e d ; GraphNode( i n t x ) { v al = x ; } GraphNode( i n t x , GraphNode [ ] n) { v al = x ; neighbors = n; } public
String toString () { r e t u r n " v a l ue : " + t h i s . val ;
} } 2) Define a Queue c l a s s Queue{
GraphNode fi rs t , la st ;
9
1.4.
p u b l ic ic v oi oi d enqueue(GraphNode i f ( f i r s t == null ) {
n) {
first = n; last = first ; } e l s e { l a s t . n ex ex t = n ; last = n; } } public
GraphN GraphNode ode dequeue ( ) { i f ( f i r s t == null ) { r e t u rn r n n u l l ;
} e l s e { GraphNode temp = new Gra GraphNode( f i r s t . val , f i r s t . neig hbors ) ; f i r s t = f i r s t . n ex ex t ; r e t u r n temp; } } } 3)
Breath First Search uses a Queue
p ub u b li l i c c l a s s GraphTes GraphTestt
{
p ub u b li l i c s t a t i c v oi oi d
GraphNod GraphNodee GraphNod GraphNodee GraphNod GraphNodee GraphNod GraphNodee GraphNod GraphNodee
main( Stri ng [] args ) { new GraphNode( 1 ) ; n1 = new new GraphNode( 2 ) ; n2 = new new GraphNode( 3 ) ; n3 = new new GraphNode( 4 ) ; n4 = new new GraphNode( 5 ) ; n5 = new
n1 . neighbors neighbors n2 . neighbors neighbors n3 . neighbors neighbors n4 . neighbors neighbors n5 . neighbors neighbors
= = = = =
new new new new new new new new new new
Grap GraphN hNod odee [ ] { n2 , n3 , n5 } ; Grap GraphN hNod odee [ ] { n1 , n 4 } ; Grap GraphN hNod odee [ ] { n1 , n4 , n5 } ; Grap GraphN hNod odee [ ] { n2 , n3 , n5 } ; Grap GraphN hNod odee [ ] { n1 , n3 , n4 } ;
b r e a t h F i r s t S e a r c h ( n1 ,
5) ;
} p ub u b li l i c s t a t i c v oi oi d br ea thF ir st Sea rc h (Gra (GraphNode i f ( root . val == x)
root , i n t x ) {
System System . out . pri nt ln ( " fin d in ro ot " ) ; new Queue() ; Queu Queuee queue = new r o o t . v i s i t e d = t r u e ; queue queue . enque enqueue ue ( roo t ) ; while ( qu q ueue .
f i r s t ! = null ) { Grap GraphN hNod odee c = ( Grap GraphN hNod odee ) queue . dequeue ( ) ; f o r (Graph (GraphNo Node de n : c . nei ghb ors ) {
GRAP GRAPH H
10
1.5.
i f ( ! n .
SORTIN SORTING G
visited ) { System System . out . pr in t (n + " " ) ; n . v i s i t e d = t r u e ; i f ( n . v a l == x ) System System . out . pri nt ln ( " Find "+n) ; queue queue . enque enqueue( ue( n) ;
} } } } }
Output: value: 2 value: 3 value: 5 Find value: 5 value:
4
Classic Problems: Clone Problems: Clone Graph 1.5
s or or t tii ng ng
Time complexity of different sorting algorithms. You can go to wiki to see basic idea of them. * BinSort, Radix Sort and CountSort use different set of assumptions than the rest, and so they are not “general” sorting methods. (Thanks to Fidel for pointing this out) In addition, here are some implementations/demos: Mergesort implementations/demos: Mergesort,, Quicksort Quicksort,, InsertionSort InsertionSort.. 1.6
r ec e c ur u r si s i on o n v s . iteration
Recurs Recursion ion should should be a builtbuilt-in in thought thought for program programmer mers. s. It can be demons demonstra trated ted by a simple simple example. Question: there are n stairs, each time one can climb 1 or 2 . How many different ways to climb the stairs?
Step 1 : Finding the relationship before n and n- 1. To get n, there are only two ways, one 1 -stair from n-1 or 2 -stairs from n-2. If f(n) is the number of ways to climb to n, then f(n) = f(n- 1) + f(n-2) Step 2 : Make sure the start condition is correct. f(0) = 0 ; f( 1) = 1 ; p ub ub li li c s t a t i c i n t f ( i n t n ) { i f (n <= 2 ) r e t u r n n ; i n t x = f ( n − 1) + f ( n − 2) ; return x ;
}
11
1.7.
DYNAMIC DYNAMIC PROGRAM PROGRAMMING MING
The time complexi complexity ty of the recursi recursive ve method method is expone exponenti ntial al to n. There There are a lot of redund redundant ant computations. f(5) f( 4) + f(3) f( 3) + f(2) + f(2) + f(1) f( 2) + f(1) + f(2) + f(2) + f(1) It should be straightforward to convert the recursion to iteration. p ub ub li li c s t a t i c i n t if
(n <=
f ( i n t n ) { 2) {
return
n;
} int int
first = third =
for
1, 0;
s ec ec on on d =
2;
( i n t i = 3 ; i <= <= n ; i ++ + +) { t h i r d = f i r s t + s ec e c o nd nd ; f i r s t = s ec ec on on d ; s ec ec on on d = t h i r d ;
} return
thir d ;
}
For this example, example, iteration iteration takes takes less less time. time. You may also want want to check check out Recursion out Recursion vs Iteration.. tion 1.7
dynamic dynamic programmin programming g
Dynamic programming is a technique for solving problems with the following properties: •
An instance is solved using the solutions for smaller instances.
•
The solution for a smaller instance might be needed multiple times.
•
•
The solutions to smaller instances are stored in a table, so that each smaller instance is solved only once. Additional space is used to save time.
The problem of climbing steps perfectly fit those 4 properties. Therefore, it can be solve by using dynamic programming. p ub ub li li c s t a t i c i n t [ ]
new in t [ 1 0 0 ] ; A = ne
p ub ub li li c s t a t i c i n t f 3 ( i n t i f (n <= 2 )
n) {
A[n]= n; i f (A[ n ]
>
0)
r e t u r n A[ A[ n ] ; else
12
1.8.
BIT MANIPULATION
A [ n ] = f 3 ( n − 1) + f 3 ( n − 2) ; / / s t o r e r e s u l t s s o o nl y c a l c u l a t e o nc e ! r e t u r n A[ n ] ; }
Classic problems: Edit Distance, Longest Palindromic Substring, Word Break. 1.8
b i t m a ni p ul at i on
Bit operators: Get bit i for a give number n. (i count from 0 and starts from right) p u bl i c s t a t i c b oo le an g e t B i t ( i n t num, i n t i n t r e s u l t = num & ( 1 << i ) ;
i){
i f ( r e s u l t == 0 ) { r e tu r n f a l s e ; } e l s e { r e t u rn t r u e ;
} }
For example, get second bit of number 10 . i=1, n= 10 1«1= 10 1010&10=10 10 is not 0 , so return true; Classic Problems: Find Single Number. 1.9
p ro b ab i li t y
Solving probability related questions normally requires formatting the problem well. Here is just a simple example of such kind of problems. There are 50 people in a room, what’s the probability that two people have the same birthday? (Ignoring the fact of leap year, i.e., 365 day every year)
Very often calculating probability of something can be converted to calculate the opposite. In this example, we can calculate the probability that all people have unique birthdays. That is: 365/365 * 364/365 * 363/365 * âA˘ e˛ * 365-n/365 * âA˘ e˛ * 365-49/365. And the probability that at least two people have the same birthday would be 1 - this value. p u bl i c s t a t i c d ou ble c a c u l a t e P r o b a b i l i t y ( i n t double x = 1 ; f o r ( i n t
i = 0 ; i
} double pro = Math.round(( 1 − x ) r e t u r n pro/ 1 0 0 ;
}
∗
100) ;
n) {
13
1.10.
. COMBINATIONS AND PERMUTATIONS
calculateProbability(50) = 0 .97 1.10
.
combinations and permutations
The difference between combination and permutation is whether order matters. Example 1 : Given 5 numbers - 1 , 2 , 3 , 4 and 5 , print out different sequence of the 5 numbers. 4 can not be the third one, 3 and 5 can not be adjacent. How many different combinations?
Example 2 : Given 5 banaba, 4 pear, and 3 apple, assuming one kind of fruit are the same, how many different combinations?
Please leave your comment if you think any other problem should be here. 1.11
. others
Other problems need us to use observations to form rules to solve them. Classic problems: Reverse Integer, Pow(x,n). 1.12
high frequency problems
1.”Valid
Number”, “Climbing Stairs” Validate Binary Search Tree, Merge Two Sorted Lists, Two Sum, String to Integer, 3Sum, Merge Intervals, Merge Sorted Array, Valid Parentheses, Implement strStr()(), Pow(x,n), Insert Interval, Set Matrix Zeroes 2.
“Add Two Numbers”, “Binary Tree Traversal(pre/in/post-order)”, “Find Single Number”, “Word Break”, “Reorder List”, “Edit Distance”, ” Reverse Integer”, “Copy List with Random Pointer”, “Evaluate Reverse Polish Notation”, “Word Ladder”, “Median of Two Sorted Arrays Java”, “Regular Expression Matching”,
14
2 L E E T C O D E - E VA L U A T E R E V E R S E P O L I S H N O T AT I O N
The problem: E va l ua t e t h e v al ue o f an a r i t h m e t i c e x p re s s i on i n R ev er se P o l i s h N ot at io n . V al id o p er a to r s a re + , expression .
−
,
∗
, / . Ea ch o pe ra nd may b e an i n t e g e r o r a n ot h er
Some examples : [ " 2 " , " 1 " , " +" , " 3 " , " ∗ " ] −> ( ( 2 + 1 ) ∗ 3 ) −> 9 [ " 4 " , " 13 " , " 5 " , " / " , " + " ] −> ( 4 + ( 1 3 / 5 ) ) −>
2.1
6
naive approach
This problem is simple. After understanding the problem, we should quickly realize that this problem can be solved by using a stack. We can loop through each element in the given array. When it is a number, push it to the stack. When it is an operator, pop two numbers from the stack, do the calculation, and push back the result.
15
2.1.
NAIVE APPROACH
The following is the code. It runs great by feeding a small test. However, this code contains compilation errors in leetcode. Why? p ub li c c l a s s
Test { main( Stri ng [] args ) throws IOException { S t r i n g [ ] t o ke ns = new S t ri n g [ ] { " 2 " , " 1 " , " +" , " 3 " , " ∗ " } ; System . out . p r in tl n (evalRPN ( tok ens ) ) ;
p ub li c s t a t i c v oid
} p ub lic s t a t i c i nt evalRPN ( Str in g [ ] i n t returnValue = 0 ;
tokens ) {
S t r in g o p er a to r s = " +−∗/" ; S t ac k < S t r i n g > s t a c k = new Stack
() ; for
( S t r in g t : t ok en s ) { i f ( ! ope rato rs . cont ain s ( t ) ) { st ac k . push( t ) ; } else { i n t a = Inte ger . valueOf ( stack .pop () ) ; i n t b = Inte ger . valueOf ( stack .pop () ) ; switch ( t ) { case "+" : sta ck . push( Str ing . valueOf ( a + break ; case "− " : sta ck .push( Stri ng . valueOf (b − break ; case " ∗ " : sta ck .push( Stri ng . valueOf ( a ∗ break ; case "/" : sta ck . push( Str ing . valueOf (b / break ; } }
b) ) ; a) ) ; b) ) ; a) ) ;
} returnValue = Inte ger . valueOf ( stack .pop () ) ; return
returnValue ;
} }
The problem is that switch string statement is only available from JDK 1 .7. Leetcode apparently use versions below that.
16
2.2.
2.2
ACCEPTED ACCEPTED SOLUTION SOLUTION
a cc cc e pt pt e d s o lu lu t io io n
If you want to use switch statement, you can convert the above by using the following code which use the index of a string “+-*/”. p ub u b li l i c c l a s s S o l ut u t i on on { p u bl bl i c i n t eval evalRP RPN( N( Str in g i n t returnValue
=
[] tokens ) {
0;
S t r i ng n g o p er e r a to t o r s = " +−∗/ " ; new Stack s t a c k = new Stack() ; f o r ( S t r i n g t : t o ke ke ns ns ) { i f ( ! oper ato rs . co nt ai ns
(t )){
st ac k . push( push( t ) ; } e l s e { i n t a = Inte ger . valueO valueOff ( stack .po .pop () ) ; i n t b = Inte ger . valueO valueOff ( stack .po .pop () ) ; i n t index = ope rato rs . indexOf( indexOf( t ) ; switch ( index index ) { case 0 : sta ck . push( push( Stri ng . valueO valueOff ( a+b) a+b) ) ; break ; case 1 : sta ck .push .push ( Str in g . valueOf valueOf (b−a ) ) ; break ; case 2 : sta ck .push .push ( Str in g . valueOf valueOf ( a ∗ b ) ) ; break ; case 3 : sta ck . push( push( Stri ng . valueO valueOff (b/a (b/a ) ) ; break ; } } } returnValue returnValue = Inte ger . valueO valueOff ( stack .pop .pop () ) ; return
} }
returnValue ;
17
3 L E E T C O D E S O L U T I O N O F L O N G E S T PA PA L I N D R O M I C S U B S T R I N G I N J AV AVA A
Finding the longest palindromic substring is a classic problem of coding interview. In this post, I will summarize 3 different solutions for this problem. 3.1
naive naive approa approach ch
Naively, we can simply examine every substring and check if it is palindromic. The time complexity is O(nˆ O(n3ˆ ). If this is submitted to LeetCode onlinejudge, an error message will be returned - “Time Limit Exceeded”. Therefore, this approach is just a start, we need better algorithm. p ub ub li li c s t a t i c
S t r i n g l o n ge ge s t Pa Pa l i nd n d r o me me 1 ( S t ri ri n g s ) {
i n t maxPalinLength
= 0; S t r i n g l o n ge g e s t Pa Pa l i nd n d r o me me = null ; i n t length = s . length () ; / / c h e c k a l l p o s s i b l e s u b s t r i n g s f o r ( i n t i = 0 ; i < l e ng n g th t h ; i + +) +) { f o r ( i n t j = i + 1 ; j < l en e n gt gt h ; j ++) { i n t l en en = j − i ;
S t r i n g c u rr r r = s . s u b s t ri ri n g ( i , j + 1 ) ; i f ( isPal indro me ( cur r ) ) { i f ( len > maxP maxPali alinLe nLeng ngth) th) { longestPalindrome longestPalindrome = curr ; maxPalin maxPalinLeng Length th = len ; } } } } return
longestPalindro me ;
} p u bl bl i c s t a t i c b oo oo le le an an for
i s Pa P a l i nd n d r o me me ( S t r i n g s ) {
( i n t i = 0 ; i < s . l e n gt gt h ( ) − 1 ; i + + ) { i f ( s . charAt charAt ( i ) != s . charAt charAt ( s . lengt h () r e tu t u r n f a l s e ;
18
−
1
−
i)) {
3.2.
DYNAMIC DYNAMIC PROGRAM PROGRAMMING MING
} } r e tu t u r n t r ue u e ;
}
3.2
dynamic dynamic programmin programming g
Let s be the input string, i and j are two indices of the string. Define a 2-dimension array “table” and let table[i][j] denote whether substring from i to j is palindrome. Start condition: ta bl e [ i ][ i ] == 1 ; ta bl e [ i ] [ i + 1 ] == 1
=> s . charAt ( i ) == s . charAt ( i + 1 )
Changing condition: ta bl e [ i ] [ j ] ==
=> ta bl e [ i + 1 ] [ j − 1] ==
1
1
&& s . charAt ( i ) == s . charAt ( j )
Time O(nˆ O(n2ˆ ) Space O(nˆ O(n2ˆ ) p ub ub li li c s t a t i c if (s
i f ( s
S t r i n g l o n ge ge s t Pa Pa l i nd n d r o me me 2 ( S t ri ri n g s ) { = = null ) r e t u rn rn n u ll l l ;
. l e n g t h ( ) < =1 ) return s ;
i n t maxLen
= 0; S t r i ng n g l o n g e s t S tr t r = null ; int int
length = s . length () ; new in t [ lengt h ][ length ] ; [ ] [ ] t ab a b le l e = ne
/ / e v e r y s i n g l e l e t t e r i s p a l i n d r o m e f o r ( i n t i = 0 ; i < l e ng n g th t h ; i + +) +) {
tab le [ i ][ i ] =
1;
} printT able ( tab le ) ; / / e . g . b c b a / / t wo c o n s e c u t i v e s am e l e t t e r s a r e p a l i n d r o m e f o r ( i n t i = 0 ; i <= l e n gt gt h − 2 ; i + + ) { i f ( s . charAt charAt ( i ) == s . charAt charAt ( i + 1 ) ) {
tab le [ i ][ i + 1 ] = 1 ; l o n g e s t S t r = s . s u b s t ri ri n g ( i , i + } }
2) ;
19
3.3.
SIMPLE ALGORITHM
printT able ( tab le ) ; / / c o n d i t i o n f o r c a l c u l a t e wh o le t a b l e f o r ( i n t l = 3 ; l <= l e n gt h ; l ++) { f o r ( i n t i = 0 ; i <= l e ng th − l ; i + +) { int j = i + l − 1 ; i f ( s . charAt ( i ) == s . charAt( j ) ) {
table [ i ][ j ] = table [ i + 1 ][ j − 1 ] ; i f ( ta bl e [ i ] [ j ] == 1 && l > maxLen) l o n g e s t S t r = s . s u b s t ri n g ( i , j +
1) ;
} else { tab le [ i ][ j ] =
0;
} print Table ( tab le ) ; } } return
longestStr ;
} p ub li c s t a t i c v oid print Table ( i n t [ ] [ ] f o r ( i n t [ ] y : x ) { f o r ( i n t z : y) {
x){
System . out . pri nt ( z + " " ) ; } System . out . pr in tl n () ; } System . out . pr i nt l n ( "−−−−−− " ) ; }
Given an input, we can use printTable method to examine the table after each iteration. For example, if input string is “dabcba”, the final matrix would be the following: 1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 1 0 1 0
0 1 0 0 0 1
From the table, we can clear see that the longest string is in cell table[ 1][5]. 3.3
s i mp l e a l go r it h m
Time O(nˆ2), Space O(1) public
S t r i n g l o n ge s t Pa l i nd r o me ( S t r i n g s ) { i f ( s . isEmpty ( ) ) { r e t u rn n u ll ; } if
( s . length () == return s ;
1)
{
20
3.4.
MANACHER’S ALGORITHM
} S t r i n g l o n g e s t = s . s u b s t r i n g ( 0 , 1 ) ; f o r ( i n t i = 0 ; i < s . l e ng th ( ) ; i ++) { / / g e t l o n g e s t p a l i n d r o m e w i th c e n t e r o f i
S t r i n g tmp = h e l p e r ( s , i , i ) ; i f (tmp. length () > longe st . length () ) { longest = tmp; } / / g e t l o n g e s t p a l i n d r o m e w i th c e n t e r o f i , i + 1
tmp = h e l pe r ( s , i , i + 1 ) ; i f (tmp. length () > longe st . length () ) { longest = tmp; } } return
longe st ;
} / / Given a c e n t e r , e i t h e r one l e t t e r o r tw o l e t t e r , / / Find l o n g e s t p a l i n d r o m e public S t r i n g h e l p e r ( S t r i n g s , i n t begin , i n t end ) { while ( begin >= 0 && end <= s . len gth ( ) − 1 && s . charAt ( begin ) == s .
charAt( end) ) { begi n −−; end++; } return
s . subs tri ng ( begin +
1,
end) ;
}
3.4
m ana ch er ’ s algorithm
Manacher’s algorithm is much more complicated to figure out, even though it will bring benefit of time complexity of O(n). Since it is not typical, there is no need to waste time on that.
21
4 LEETCO DE SOLUTION - WORD BREAK
This is my solution for Word Break in Java. 4.1
t he p ro bl em
Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words. For example, given s = “leetcode”, dict = ["leet", "code"]. Return true because “leetcode” can be segmented as “leet code”. 4.2
naive approach
This problem can be solve by using a naive approach, which is trivial. A discussion can always start from that though. p ub li c c l a s s S o l ut i on { public boolean wordBreak( Stri ng s , r e t u r n wordBreakHelper ( s ,
Set di ct ) { dict , 0 ) ;
} public boolean wordBreakHelper ( Stri ng i f ( st ar t == s . length () ) r e t ur n t r u e ; for ( String a : i n t len = i n t end =
s , Set dict , i n t s t a r t ) {
dict ) { a . length () ; sta rt+len ;
/ / e nd i n d e x s h o u l d b e <= s t r i n g l e n g t h i f (end > s . lengt h () ) continue ; i f (s
. substr ing ( st art , st ar t+len ) . equals (a ) ) i f ( wordBreakHelper (s , dict , st ar t+len ) ) r e t u rn t r u e ;
} r e tu r n f a l s e ;
22
4.3.
DYNAMIC PROGRAMMING
} }
ˆ Time: O(2n) 4.3
dynamic programming
The key to solve this problem by using dynamic programming approach: •
Define an array t[] such that t[i]==true => 0-(i-1) can be segmented using dictionary
•
Initial state t[0] == true
p ub li c c l a s s S o l ut i on { public boolean wordBreak( Stri ng s , Set boolean [ ] t = new boolean [ s . length () + 1 ] ; t [ 0 ] = t r u e ; / / s e t f i r s t t o b e t r u e , why ? / / B e c a u s e we n ee d i n i t i a l s t a t e f o r ( i n t i = 0 ; i < s . l e n g t h ( ) ; i + +) { / / s h o u l d c o n t i n u e from match p o s i t i o n i f ( ! t [ i ] ) continue ; for ( String a : dict ) { i n t len = a . length () ; i n t e nd = i + l e n ; i f (end > s . lengt h () ) continue ; i f ( s . substr ing ( i , end) t [end] = t r u e ;
} } } return
t [ s . lengt h ( ) ];
} }
Time: O(string length * dict size)
. equals ( a ) ) {
di ct ) {
23
5 LEETCODE - WORD LADDER
The problem: Given two words ( s t a r t and end ) , and a d i c t i on a r y , f i n d t h e l e n gt h o f s h o r t e s t t r a n s f o r m a ti o n s eq ue nc e fro m s t a r t t o end , s uc h t h a t : Only o ne l e t t e r ca n b e c hange d a t a t im e Each i n t e rm e d i at e word m ust e x i s t i n t h e d i c t i o n a r y For example , Given : star t = " hit " end = "cog" dic t = [ " hot" , " dot" , "dog" , " lo t " , " log " ] As o ne s h o r t e s t t r a ns f o rm a t io n i s " h i t " r e t u r n i t s l en gt h 5 .
> "hot"
−
> "dot"
−
> "dog"
−
> "cog" ,
−
Note: Return 0 i f t h e r e i s no s uc h t r a n s f o r m a t i o n s e qu en ce . All words have the same length . A l l words c o n t a i n o nl y l o we r c as e a l p h a b e t i c c h a r a c t e r s .
This problem is a classic problem that has been asked frequently during interviews. The following are two Java solutions. 5.1
naive approach
In a simplest way, we can start from start word, change one character each time, if it is in the dictionary, we continue with the replaced word, until start == end. p ub li c c l a s s S o l ut i on { p u bl i c i n t l a dd e rL e ng t h ( S t r i n g
s t a r t , S t r i n g end , H as hS et < S t r i n g > d i c t ) {
i n t len = 0 ; H as hS et < S t r i n g > v i s i t e d = new HashSet( ) ; f o r ( i n t i = 0 ; i < s t a r t . l e n gt h ( ) ; i ++) { char [ ] s t a r t A r r = s t a r t . t oC ha rA rr ay ( )
24
;
5.2.
f o r ( char c = ’ a ’ ; c <= ’ z ’ ; c + +) { i f ( c==s t ar t . toCharArray ( ) continue ;
BREATH FIRST SEARCH
[i ]) {
} sta rtA rr [ i ] = c ; S t r i n g t e m p = new Stri ng ( sta rtA rr ) ; i f ( dic t . cont ai ns (temp) ) { len++; start = temp; i f ( temp. eq uals (end) ) { r e t u r n len ; } } } } r e t u r n len
;
} }
Apparently, this is not good enough. The following example exactly shows the problem. It can not find optimal path. The output is 3 , but it actually only takes 2 . I np ut : " a " , " c " , [ " a " , " b " , " c " ] Output : 3 2 Expecte d :
5.2
b r eat h f i rs t s e ar c h
So we quickly realize that this looks like a tree searching problem for which breath first guarantees the optimal solution. Assuming we have all English words in the dictionary, and the start is “hit” as shown in the diagram below.
25
5.2.
BREATH FIRST SEARCH
We can use two queues to traverse the tree, one stores the nodes, the other stores the step num bers. Before starting coding, we can visualize a tree in mind and come up with the following solution. p ub li c c l a s s S o l ut i on { p u bl i c i n t l a dd e rL e ng t h ( S t r i n g if
( di ct . siz e () == return 0 ;
int
result =
s t a r t , S t r i n g end , H as hS et < S t r i n g > d i c t ) {
0)
0;
Linke dLis t wordQueue = new LinkedList() ; LinkedList distanceQueue = new LinkedList() ; wordQueue . add ( st a r t ) ; distanceQueue . add ( 1 ) ; while ( ! wordQueue . isEmpty () ) {
St ri ng currWord = wordQueue . pop ( ) ; Inte ger currDistanc e = distanceQueue . pop () ; i f ( currWord . equa ls (end) ) { r e t u r n currDistan ce ;
} f o r ( i n t i = 0 ; i
i ++) { toCharArray () ;
currCharArr[ i ] = c ; St ri ng newWord = new Stri ng ( currCharArr ) ;
26
5.3.
i f (
WHAT LEARNED FROM THIS PROBLEM?
di c t . co nt ai ns (newWord) ) { wordQueue . add (newWord) ; distanceQueue . add( currDi stan ce + 1 ) ; d i c t . remove (newWord) ;
} } } } return 0 ;
} }
5.3
what learned from this problem ?
•
Use breath-first or depth-first search to solve problems
•
Use two queues, one for words and another for counting
27
6 LEETCODE - MEDIAN OF TWO SORTED ARRAYS JAVA
LeetCode Problem: There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). 6.1
keys to solve this problem
This problem can be converted to the problem of finding kth element, k is (A’s length + B’ Length)/2. If any of the two arrays is empty, then the kth element is the non-empty array’s kth element. If k == 0, the kth element is the first element of A or B. For normal cases(all other cases), we need to move the pointer at the pace of half of an array length. p u bl i c s t a t i c d ou ble findMedianSorte dArrays ( i n t i n t m = A. lengt h ; i n t n = B. length ; if
( (m + n ) %
else
{
2
return / / e v en return
!=
A[] , i n t B [ ] ) {
0 ) / / o dd
( double ) f in dK th ( A, B , (m + n ) /
0 ,
n
( findKth (A, B, (m + n ) / 2 , 0 , m − 1 , 0 , n − 1 ) + f i nd K th ( A, B , (m + n ) / 2 − 1 , 0 , m − 1 , 0 , n 0.5;
−
} } p ub li c s t a t i c i n t int aStart i n t aLen i n t bLen
findKth ( i n t A[] , i n t B [ ] , i n t k , , i n t aEnd, i n t bStart , i n t bEnd) {
= aEnd = bEnd
aStart + − bStart + −
1; 1;
/ / Handle s p e c i a l c a s e s i f (aLen == 0 ) return B[ bStart + k ]; i f (bLen == 0 )
28
2 , 0 ,
m − 1 ,
−
1) ;
1) )
∗
6.1.
return A[ aSta rt if
(k ==
KEYS TO SOLVE THIS PROBLEM
+ k ];
0)
return A[ aSta rt ] i n t aMid i n t bMid
< B [ b S t a r t ] ? A[ a S t a r t ] : B [ b S t a r t ] ;
= aLen ∗ k / (aLen + bLen) ; / / a ’ s m i d d le c o u nt = k − aMid − 1 ; / / b ’ s m i d d le c o u nt
/ / ma ke aMid an d bMid t o b e a r r a y i n d e x
aMid = aMid + aStart ; bMid = bMid + b S t a r t ; (A[aMid] > B[bMid ]) { k = k − (bMid − b S t a r t + aEnd = aMid; b S t a r t = bMid + 1 ; } else { k = k − (aMid − a S t a r t + bEnd = bMid ; aStart = aMid + 1; } if
return
}
1) ;
1) ;
f i nd K th ( A, B , k , a S t a r t , aEnd , b S t a r t , bEnd ) ;
29
7 L E E T C O D E - R E G U L A R E X P R E S S I O N M A T C H I N G I N J AVA
Problem: Implement regular expression matching with support for ’.’ and ’*’.
’ . ’ M at ch es any s i n g l e c h a r a c t e r . ’ ∗ ’ M at ch es z e ro o r more o f t h e p r ec e di n g e l em en t . The m at ch in g s h ou ld c ov er t h e e n t i r e i n pu t s t r i n g ( n ot p a r t i a l ) . The function prototype should be : bool isMa tch ( c o n st c h ar ∗ s , c o n s t c h ar
∗
p)
Some examples : isMatch( "aa " , "a " ) r et u rn f a l s e isMatch( "aa " , " aa" ) r e tu r n t r u e isMatch( "aaa " , " aa" ) r et u rn f a l s e i s Ma tc h ( " a a " , " a ∗ " ) r e tu r n t r u e isMatch( "aa " , " . ∗ " ) r e tu r n t r u e isMatch( "ab" , " . ∗ " ) r e tu r n t r u e i s Ma tc h ( " a ab " , " c ∗ a ∗ b " ) r e tu r n t r u e
7.1
thoughts for this problem
Overall, there are 2 different cases: 1) the second char of pattern is “*” , and 2) the second char of pattern is not “*”. For the 1 st case, if the first char of pattern is not “.”, the first char of pattern and string should be the same. Then continue to match the left part. For the 2 nd case, if the first char of pattern is “.” or first char of pattern == the first i char of string, continue to match the left. Be careful about the offset. 7.2
java solution
The following Java solution is accepted.
30
7.2.
p ub li c c l a s s S o l ut i on { public boolean i s Ma tc h ( S t r i n g i f (p. length () == 0 ) r e t u r n s . lengt h ( )
==
JAVA SOLUTION
s , String p) { 0;
/ / p ’ s l e n g t h 1 i s s p e c i a l c as e i f (p. length () == 1 || p. charAt ( 1 ) ! = ’ ∗ ’ ) { i f (s . length () < 1 || (p . charAt ( 0 ) != ’ . ’ && s . charAt ( 0 ) ! = p . c ha rA t
(0) ) ) r e tu r n f a l s e ; r e t u r n isMatch( s .
substr ing ( 1 ) , p . s u b s t r i n g ( 1 ) ) ;
} e l s e { int
len = s . length () ;
i n t i = −1; while ( i
&& ( i < 0 || p . charAt ( 0 ) == ’ . ’ || p . charAt ( 0 ) == s . char At ( i ) ) ) { i f ( isMatch( s . substr ing ( i + 1 ) , p. substring ( 2 ) ) ) r e t u rn t r u e ; i ++;
} r e tu r n f a l s e ;
} } }
31
8 L E E T C O D E - M E R G E I N T E R VA L S
Problem: Given a c o l l e c t i o n o f i n t e r v a l s , merge a l l o ve rl ap pi ng i n t e r v a l s . For example , Given [ 1 , 3 ] , [ 2 , 6 ] , [ 8 , 1 0 ] , [ 1 5 , 1 8 ] , r e t u r n [ 1 , 6 ] , [ 8 , 1 0 ] , [ 1 5 , 1 8 ] .
8.1
thoughts of this problem
The key to solve this problem is defining a Comparator first to sort the arraylist of Intevals. And then merge some intervals. The take-away message from this problem is utilizing the advantage of sorted list/array. 8.2
java solution
class
I n t er v a l { int star t ; i n t end; I n t e rv a l ( ) { start = 0; end = 0 ; } I n t e r v a l ( i n t s , i n t e ) { start = s ; end = e ; }
} p ub li c c l a s s S o l ut i on { public A r r ay L i st < I n t e r v a l if
> merge ( A r r ay L i s t < I n t e r v a l > i n t e r v a l s ) {
( i n t e r v a l s == null || i n t e r v a l s . s i z e ( ) <=
32
1)
8.2.
return
JAVA SOLUTION
inte rvals ;
/ / s o r t i n t e r v a l s by u s i n g s e l f − d e f i n e d C o mp a ra t or C o l l e c t i o n s . s o r t ( i n t e r v a l s , new IntervalComparato r ( ) ) ;
A r ra y Li s t < I n t e r v a l > r e s u l t = new ArrayList () ; I n t e r v a l p re v = i n t e r v a l s . g e t ( 0 ) ; f o r ( i n t i = 1 ; i < i n t e r v a l s . s i z e ( ) ; i + +) { I n t e r v a l c u rr = i n t e r v a l s . g e t ( i ) ; if
( prev . end >= curr . st ar t ) { / / merged c a s e
I n t e r v a l m e r g e d = new Int er val ( prev . st art , Math. max( prev . end , cur r . end ) ) ; prev = merged; } else { re su lt .add( prev) ; prev = curr ; } } re su lt .add( prev) ; return
resul t ;
} } class
}
IntervalComparator implements Comparator { p u bl ic i n t compare( Int er val i 1 , I nt er va l i 2 ) { r e t u r n i 1 . s t a r t − i 2 . s t a r t ; }
33
9 L E E T C O D E - I N S E R T I N T E R VA L
Problem: Given a set of non-overlapping & sorted intervals, insert a new interval into the intervals (merge if necessary).
Example 1 : Given i n t e r v a l s [ 1 , 3 ] , [ 6 , 9 ] , i n s e r t and merge [ 2 , 5 ] i n a s [ 1 , 5 ] , [ 6 , 9 ] . Example 2 : Given [ 1 , 2 ] , [ 3 , 5 ] , [ 6 , 7 ] , [ 8 , 1 0 ] , [ 1 2 , 1 6 ] , i n s e r t and m erge [ 4 , 9 ] i n a s [ 1 , 2 ] , [ 3 , 1 0 ] , [ 1 2 , 1 6 ] . T hi s i s b ec au se t h e new i nt e r va l [ 4 , 9 ] o ve r la ps wi th [ 3 , 5 ] , [ 6 , 7 ] , [ 8 , 1 0 ] .
9.1
thoughts of this problem
Quickly summarize 3 cases. Whenever there is intersection, created a new interval.
34
9.2.
9.2
JAVA SOLUTION
java solution
/ ∗ ∗ ∗ D e f i n it i o n f o r an i n t e r v a l . p u bl i c c l a s s I n t er v a l { ∗ ∗ int start ; i n t e nd ; ∗ ∗ I n t e r v a l ( ) { s t a r t = 0 ; end = 0 ; } I n t e r v a l ( i n t s , i n t e ) { s t a r t = s ; end = e ; } ∗ ∗ } ∗ / p ub li c c l a s s S o l ut i on { public A r ra yL i st < I n t e r v a l > i n s e r t ( A r ra y Li s t < I n t e r v a l > i n t e r v a l s , I n t e r v a l
newInterval ) { A r ra yL i st < I n t e r v a l > r e s u l t = new ArrayList() ; for ( I n t e r v a l i n t e r v a l : i n t e r v a l s ) { i f ( in te rv al .end < newInterval .
sta rt ) {
r e s u l t . add ( i n t e r v a l ) ; } e ls e i f ( i n t e r v a l . s t a r t > n e w In t e rv a l . end ) { re su lt . add( newInterval ) ; n e wI n te r va l = i n t e r v a l ; } e ls e i f ( i n t e r v a l . end >= n e w In t e rv a l . s t a r t || i n t e r v a l . s t a r t <= newInterval . end) { newInterval = new Int er val (Math. min( in te rv al . st art , newInterval . s t ar t ) , Math .max( newInterval . end , in te rv al .end) ) ; } } re su lt . add( newInterval ) ; return
} }
resul t ;
35
10 LEETCODE - TWO SUM (JAVA)
Given an array of integers, find two numbers such that they add up to a specific target number.
Input : numbers= { 2 , 7 , 1 1 , 1 5 } , t a r g e t = 9 Output : index 1 = 1 , i n d e x 2 =2
This problem is pretty straightforward. We can simply examine every possible pair of numbers in this integer array. When the first number is greater than the target, there is no need to find the number number. Solution: p u bl i c i n t [ ] twoSum( i n t [] numbers , i n t t a r g e t ) { i n t [ ] r et = new in t [ 2 ] ; f o r ( i n t i = 0 ; i
re t [ 0 ]= i + 1 ; re t [ 1 ]= j + 1 ; } } } } return
re t ;
}
36
){
11 LEETCODE - 3SUM
Problem: Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0 ? Find all unique triplets in the array which gives the sum of zero.
Note: Elements in a triplet (a,b,c) must be in non-descending order. (ie, a âL’d’ b âL’d’ c) The solution set must not contain duplicate triplets. F or e xa mp le , g i ve n a r r a y S = { −1
0 1 2
1
−
−
4} ,
A s o l ut i on s e t i s : ( − 1 , 0 , 1 ) ( − 1 , −1 , 2 )
11.1
n ai v e s o lu t io n
Naive solution is 3 loops, and this gives time complexity O(nˆ3). Apparently this is not an acceptable solution, but a discussion can start from here. p ub li c c l a s s S o l ut i on { public A rrayList > / / s o r t a r r a y
threeSum( i n t [] num) {
Arr ays . s o r t (num) ; A r r ay L i st < A r ra y L is t < I n t e g e r >> r e s u l t = new ArrayList >() ; ArrayList each = new ArrayList() ; f o r ( i n t i = 0 ; i 0 ) break ; f o r ( i n t j = i + 1 ; j 0 && num[
j] >
f o r ( i n t k=j + 1 ; k
each . add (num[ i ] ) ; each . add (num[ j ] ) ; each . add (num[k ] ) ;
37
0 ) break ;
{
11.2.
BETTER SOLUTION
r e s u l t . add ( e ac h ) ; each . cle ar ( ) ; } } } } return
resul t ;
} }
* The solution also does not handle duplicates. Therefore, it is not only time inefficient, but also incorrect. Result: Submission Resu lt : Output Limit Exceeded
11.2
b e tt e r s o lu t io n
A better solution is using two pointers instead of one. This makes time complexity of O(n 2ˆ ). To avoid duplicate, we can take advantage of sorted arrays, i.e., move pointers by > 1 to use same element only once. public Ar rayList >
threeSum ( i n t [] num) { A r r ay L i st < A r ra y L is t < I n t e g e r >> r e s u l t = new ArrayList >() ; if
(num. len gth < 3 ) return resul t ;
/ / s o r t a r r a y
Arr ays . s o r t (num) ; for
( i n t i =
0;
i < num. length
−
2;
i++) {
/ / / / a v o i d d u p l i c a t e s o l u t i o n s i f ( i == 0 || num[ i ] > num[ i − 1 ] ) { int negate int s t a r t i n t end = while
= −num[ i ] ;
= i + 1; num. length
−
1;
( s t a r t < end ) { / / c a s e 1 i f (num[ st ar t ] + num[end] == negate ) { ArrayList temp = new ArrayList<
Integer >() ; temp . add (num[ i ]) ; temp. add(num[ s t a r t ] ) ; temp.add(num[end]) ;
38
11.2.
BETTER SOLUTION
re su lt . add( temp) ; s t a r t + +; end −−; / / a v o i d d u p l i c a t e s o l u t i o n s while ( s t a r t < end && num[end] == num[
end + 1 ] ) end −−; ( s t a r t < end && num[ s t a r t ] == num[ start − 1]) s t a r t + +;
while
/ / c a s e 2 } e ls e i f (num[ s ta rt ] + num[end] < negate ) {
s t a r t + +; / / c a s e 3 } else {
end −−; } } } } return
}
resul t ;
39
12 L E E T C O D E - S T R I N G T O I N T E G E R ( A TO I )
Problem: Imp lement a t o i t o c o nv e rt a s t r i n g t o an i n t e g e r . H in t : C a r e f ul l y c o n si d e r a l l p o s s i b l e i n pu t c a s e s . I f you want a c h al l e ng e , p l e a s e do n ot s e e below a nd a sk y o u r s e l f what a r e t h e p o s s i b l e i n pu t c a s e s . N ote s : I t i s i nt en de d f or t h is p ro bl em t o b e s p e c i f i e d v ag ue ly ( i e , no g i ve n i n pu t s p ec s ) . You a r e r e s p o n s i b l e t o g a th e r a l l t h e i np ut r e qu ir e me n ts up fron t .
12.1
thoughts for this problem
The vague description give us space to consider different cases. 1 . null o r empty s t r i n g 2 . w hi te s p a ce s 3 . +/− sign 4 . c a l c u la t e r e a l va lue 5 . handle min & max
12.2
java solution
p u bl i c i n t a t o i ( S t r i n g i f ( s t r == null return 0 ;
str ) { || s t r . l e n g t h ( ) <
/ / t r i m w h i t e s p a c e s
st r = st r . trim () ; char
flag = ’+’ ;
/ / c h e c k n e g a t i v e o r p o s i t i v e int i =0; i f ( st r . charAt ( 0 ) = = ’− ’ ) {
f l a g = ’− ’ ; i ++;
40
1)
12.2.
JAVA SOLUTION
} e ls e i f ( st r . charAt ( 0 ) == ’ + ’ ) { i ++; } / / u s e d o u b l e t o s t o r e r e s u l t double r e s u l t = 0 ; / / c a l c u a l t e v a l u e while ( st r . length () > i && st r . charAt( i ) >= ’ 0 ’ && st r . charAt( i ) <= ’ 9 ’ ) {
r e s ul t = r e s ul t i ++;
∗
10
+ ( st r . charAt( i )− ’ 0 ’ ) ;
} if ( flag
== ’− ’ ) r e s ul t =
−
resul t ;
/ / h a n d l e max an d min i f ( r e s u l t > In t e ge r .MAX_VALUE) r e t u r n Integer .MAX_VALUE; i f ( r e s u l t < In te ge r .MIN_VALUE) r e t u r n Integer .MIN_VALUE; return
}
( i n t ) r e s u l t ;
41
13 LEETCODE - MERGE SORTED ARRAY (JAVA)
Problem: Given two sorted integer arrays A and B, merge B into A as one sorted array.
Note: You may assume that A has enough space to hold additional elements from B. The number of elements initialized in A and B are m and n respectively. 13.1
thoughts for this problem
The key to solve this problem is moving element of A and B backwards. If B has some elements left after A is done, also need to handle that case. The takeaway message from this problem is that the loop condition. This kind of condition is also used for merging two sorted linked list. 13.2
java solution 1
p ub li c c l a s s S o l ut i on { public void merge( i n t
A[] , i n t m, i n t B [ ] , i n t n ) {
while (m > 0 && n > 0 ) { i f (A[m− 1 ] > B [ n − 1 ]) {
A[m+n − 1] = A[m− 1 ]; m−−; } e l s e { A[m+n − 1] = B [ n − 1 ]; n−−; } } while ( n
> 0) { A[m+n − 1] = B [ n − 1 ]; n−−;
} } }
42
13.3.
13.3
java solution 2
The loop condition also can use m+n like the following. p u b li c v oi d merge( i n t A[] , i n t int i = m − 1 ; int j = n − 1 ; int k = m + n − 1 ; while
(k >= if
0)
(j <
m, i n t B [ ] , i n t n ) {
{ 0 ||
( i >= 0 && A[ i ] > B[ j ]) ) A[ k−−] = A[ i − −];
else
A[ k−−] = B [ j −− ]; } }
JAVA SOLUTION 2
43
14 LEETCODE - VALID PARENTHESES (JAVA)
Problem: Given a string containing just the characters ’(’, ’)’, ’’, ’’, ’[’ and ’]’, determine if the input string is valid. The brackets must close in the correct order, “()” and “()[]” are all valid but “(]” and “([)]” are not. 14.1
thoughts about this problem
Character is not a frequently used class, so need to know how to use it. 14.2
java solution
i s V a l id ( S t r i ng s ) { HashMap map = new HashMap( ) ; map. put ( ’ ( ’ , ’ ) ’ ) ; map. put ( ’ [ ’ , ’ ] ’ ) ; map. put ( ’ { ’ , ’ } ’ ) ;
p u bl i c s t a t i c b oo le an
S t a c k < C h a r a c t e r > s t a c k = new Stack() ; for
( i n t i = char
0 ; i < s . l e ng th ( ) ; i ++) { curr = s . charAt ( i ) ;
(map. keySet ( ) . con ta in s ( cur r ) ) { st ac k .push( cur r ) ; } e ls e i f (map. values () . co nt ai ns ( curr ) ) { i f ( ! st ac k . empty ( ) && map. get ( st ac k . peek ( ) ) == cur r ) { stack .pop () ; } else { r e tu r n f a l s e ; } } if
} return
sta ck . empty ( ) ;
}
44
14.3.
14.3
SIMPLIFIED JAVA SOLUTION
simplified java solution
Almost identical, but convert string to char array at the beginning. p u bl i c s t a t i c b oo le an i s V a l id ( S t r i ng s ) { char [] charArray = s . toCharArray ( )
;
HashMap map = new HashMap( ) ; map. put ( ’ ( ’ , ’ ) ’ ) ; map. put ( ’ [ ’ , ’ ] ’ ) ; map. put ( ’ { ’ , ’ } ’ ) ; S t a c k < C h a r a c t e r > s t a c k = new Stack() ; for
( C h ar a ct e r c : c ha rA rr ay ) { i f (map. keySet () . cont ain s ( c ) ) { stack .push( c ) ; } e ls e i f (map. values ( ) . con tai ns ( c ) ) { i f ( ! st ac k . isEmpty ( ) && map. get ( st ac k . peek ( ) ) == c ) { stack .pop () ; } else { r e tu r n f a l s e ; } }
} return
}
st ac k . isEmpty ( ) ;
45
15 L E E T C O D E - I M P L E M E N T S T R S T R ( ) ( J AVA )
Problem: Implement strStr(). Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack. 15.1
t ho ug ht s
First, need to understand the problem correctly, the pointer simply means a sub string. Second, make sure the loop does not exceed the boundaries of two strings. 15.2
java solution
public
S t r i n g s t r S t r ( S t r i n g h ay st ac k , S t r i n g n e ed le ) { int int
needleLen = needle . lengt h ( ) ; haystackLen = haystack . lengt h ( ) ;
if
(needleLen == haystackLen && needleLen == return " " ;
if
(needleLen == 0 ) r e t u r n haystack ;
for
( i n t i =
0;
0)
i < h a ys ta ck Le n ; i ++) {
/ / ma ke s u r e i n b ou n dar y o f n e e d l e i f (haystackLen − i + 1 < needleLen) r e t u rn n u l l ; int k int j
= i; = 0;
( j < needleLen && k < haystackLen && needle . charAt( j ) == haystack . charAt( k) ) { j ++; k++; i f ( j == needleLen )
while
46
15.2.
return
} } r e t u rn n u l l ;
}
hays tack . sub st ri ng ( i ) ;
JAVA SOLUTION
47
16 L E E T C O D E - S E T M A T R I X Z E R O E S ( J A VA )
Given a m x n matrix, if an element is 0 , set its entire row and column to 0 . Do it in place. 16.1
thoughts about this problem
This problem can solve by following 4 steps: •
check if first row and column are zero or not
•
mark zeros on first row and column
•
use mark to set elements
•
set first column and row by using marks in step 1
16.2
java solution
p ub li c c l a s s S o l ut i on { public void setZeroe s ( i n t [ ] [ ] m at ri x ) boolean firstRowZero = f a l s e ; boolean firstColumnZero = f a l s e ;
{
/ / s e t f i r s t ro w an d co lumn z e r o o r n o t f o r ( i n t i = 0 ; i < m at r i x . l e n g t h ; i + +) { i f ( mat rix [ i ] [ 0 ] == 0 ) { firstColumnZero = t r u e ; break ;
} } f o r ( i n t i = 0 ; i < m at r i x [ 0 ] . l e n gt h ; i f ( mat rix [ 0 ] [ i ] == 0 ) { firstRowZero = t r u e ; break ;
i ++) {
} } / / m ar k z e r o s on f i r s t ro w an d colu mn
48
16.2.
f o r ( i n t i = 1 ; i < m at r i x . l e n g t h ; i + +) { f o r ( i n t j = 1 ; j
matri x [ i ][ 0 ] = matri x [ 0 ] [ j ] =
j ++) {
0; 0;
} } } / / u s e ma rk t o s e t e l e m e n t s f o r ( i n t i = 1 ; i < m at r i x . l e n g t h ; i + +) { f o r ( i n t j = 1 ; j
matrix [ i ][ j ] =
0;
} } } / / s e t f i r s t colu mn an d ro w i f ( firstCol umnZero ) { f o r ( i n t i = 0 ; i < m at r i x . l e n g t h ; i + +)
matri x [ i ][ 0 ] =
0;
} i f (
} } }
firstRow Zero ) { f o r ( i n t i = 0 ; i < m at r i x [ 0 ] . l e n g t h ; i + +) matri x [ 0 ] [ i ] = 0 ;
JAVA SOLUTION
49
17 L E E T C O D E S O L U T I O N - A D D T W O N U M B E R S I N J A VA
The problem from Leetcode. You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) Output: 7 -> 0 -> 8
This is a simple problem. Just follow the steps like the following: •
A flag to mark if previous sum is >= 10
•
Handle the situation that one list is longer than the other
•
Correctly move the 3 pointers p1, p 2, and p3
•
Handle the situation of 5 + 5
17.1
s ol ut io n 1
The hard part is how to make the code more readable. Adding some internal comments and refactoring some code are helpful. / ∗ ∗ ∗ D e f in i t i on f o r s in gl y − l i n k e d l i s t . c l a s s L is tN od e { ∗ p u bl i c ∗ int val ; ListNode next ; ∗ ∗ L i st N od e ( i n t x ) { val = x ; ∗ ∗ n e xt = n u l l ; } ∗ ∗ } ∗ / p ub li c c l a s s S o l ut i on { public ListN ode addTwoNumbers ( List Node l 1 , L is tN od e l 2 ) {
ListNode p1 = l 1 ; ListNode p2 = l 2 ; List Node newHead = new ListNode ( 0 ) ; ListNode p3 = newHead;
50
17.1.
i n t val ; / / s t o r e sum boolean f l a g
= f a l s e ; / / f l a g i f g r e a t e r t ha n
while ( p 1 != null || p 2 != null ) { / / b o t h p 1 a nd p 2 h av e v a l u e i f ( p 1 != null && p2 != null ) { i f (
fl ag ) { v al = p 1 . v a l + p 2 . v a l + e l s e } { v al = p 1 . v a l + p 2 . val ; }
1;
/ / i f sum >= 10 i f ( val >= 10 ) { f l a g = t r u e ; / / i f sum < 10 } e l s e { f l a g = f a l s e ;
} p 3 . next = new ListNode ( val %10 ) ; p 1 = p 1 . next ; p 2 = p 2 . next ; / / p 1 i s n ul l , b e ca u se p 2 i s l o ng e r } e ls e i f ( p2 != null ) { i f (
fl ag ) { v al = p 2 . v a l + 1 ; i f ( val >= 1 0 ) { f l a g = t r u e ; } e l s e { f l a g = f a l s e ; } } e l s e { v al = p 2 . val ; f l a g = f a l s e ; } p 3 . next = new ListNode ( val %10 ) ; p 2 = p 2 . next ; / / / / p 2 i s n ul l , b e ca us e p 1 i s l o ng e r } e ls e i f ( p1 != null ) { i f (
fl ag ) { v al = p 1 . v a l + 1 ; i f ( val >= 1 0 ) { f l a g = t r u e ;
10
SOLUTION 1
51
17.2.
SOLUTION 2
} e l s e { f l a g = f a l s e ; } } e l s e { v al = p 1 . val ; f l a g = f a l s e ; } p 3 . next = new ListNode ( val %10 ) ; p 1 = p 1 . next ; } p 3 = p 3 . next ; } / / h a n d l e s i t u a t i o n t h a t sa me l e n g t h f i n a l sum >= 10 i f ( p 1 == null && p2 == null && fl ag ) { p 3 . next = new ListNode ( 1 ) ;
} r e t u r n newHead.
next ;
} }
17.2
s ol ut io n 2
There is nothing wrong with solution 1, but the code is not readable. We can refactor the code to be much shorter and cleaner. p ub li c c l a s s S o l ut i on { public ListN ode addTwoNumbers ( List Node int c a r r y =0;
l 1 , L is tN od e l 2 ) {
List Node newHead = new ListNode ( 0 ) ; ListNode p1 = l 1 , p2 = l 2 , p 3=newHead; while ( p 1 != null || i f ( p 1 != null ) {
p 2 != null ) {
c a r r y + = p 1 . val ; p 1 = p 1 . next ; } i f ( p 2
!= null ) { c a r r y + = p 2 . val ; p 2 = p 2 . next ;
} p 3 . next = new ListNode( carr y% 10 ) ; p 3 = p 3 . next ; carry /= 10; }
52
17.2.
i f (
carry== 1 ) p 3 . next=new ListNode ( 1 ) ;
r e t u r n newHead.
} }
Exactly the same thing!
next ;
SOLUTION 2
53
18 R E O R D E R L I S T I N J A VA
This article shows a Java solution for solving the reorder list problem from Leetcode: Reorder List. ˘ e→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→LnGiven a singly linked list L: L 0→L1→â A˛ ˘ e You must do this in-place without altering the nodes’ values. For example, Given 1,2,3,4, reorder 2→â A˛ it to 1 ,4,2,3.
It is not straightforward because it requires “in-place” operations. 18.1
s ol ut io n
There are three key steps to solve this problem: •
Break list in the middle to two lists
•
Reverse the order of the second list
•
Merge two list back together
The following code is a complete runnable class with testing. / / C l a s s d e f i n i t i o n o f L i s t N o d e c l a s s ListNode { i n t val ;
ListNode next ; ListNode( i n t x ) { v al = x ; n e x t = null ; } } p ub li c c l a s s
R e o r de r L is t { main( Stri ng [] args ) { n1 = new ListNode ( 1 ) ; n2 = new ListNode ( 2 ) ; n3 = new ListNode ( 3 ) ; n4 = new ListNode ( 4 ) ;
p ub li c s t a t i c v oid
ListNode ListNode ListNode ListNode
54
18.1.
SOLUTION
n1 . n e x t = n 2 ; n2 . n e x t = n 3 ; n3 . n e x t = n 4 ; p r i n t L i s t ( n1 ) ; r e o r d e r L i s t ( n1 ) ; p r i n t L i s t ( n1 ) ; } p ub li c s t a t i c v oid if
r e o r d e r L i s t ( L is tN od e head ) {
(head != null && head . next != null ) { ListNode slow = head; L is tN od e f a s t = h ead ; / / u s e a f a s t an d s l o w p o i n t e r t o b r e a k t h e l i n k t o tw o p a r t s . while ( f a s t ! = null && fa st . next != null && f a s t . next . n e x t ! = null ) { / / why n e e d t h i r d / s e c o n d c o n d i t i o n ?
System . out . pri ntl n ( " pre "+slow . val + " " + fa st . val ) ; slow = slow . next ; fa st = fa st . next . next ; System . out . pri ntl n ( " af te r " + slow . val + " " + fa st . val ) ; } ListNode second = slow . next ; slow . next = null ; / / n ee d t o c l o s e f i r s t p a r t / / now s h o u l d h a v e tw o l i s t s : h e a d an d f a s t / / r e v e r s e o r d e r f o r s e c o n d p a r t
second = reverseOrder (second) ; ListNode p1 = head; ListNode p2 = second ; / / merge tw o l i s t s h e r e while ( p 2 != null ) {
ListNode temp1 = p 1 . next ; ListNode temp2 = p 2 . next ; p1 . n e x t = p2 ; p 2 . next = temp1 ; p 1 = temp 1 ; p 2 = temp 2 ;
55
18.2.
TAKEAWAY MESSAGE FROM THIS PROBLEM
} } } p ub li c s t a t i c if
ListNode reverseOrder ( ListNode head) {
(head == null || head. next == null ) { r e t u r n head;
} ListNode pre = head; ListNode curr = head. next ; while
( c u r r ! = null ) { ListNode temp = curr . next ; curr . next = pre ; p re = c u r r ; curr = temp;
} head. next = null ; r e t u r n pre
;
} p r i n t L i s t ( L i st No de n ) { System . out . pr in tl n ( "−−−−−− " ) ; while ( n ! = null ) { System . out . pr i nt (n . val ) ; n = n. next ; } System . out . pr in tl n () ;
p ub li c s t a t i c v oid
} }
18.2
takeaway message from this problem
The three steps can be used to solve other problems of linked list. A little diagram may help better understand them.
56
18.2.
TAKEAWAY MESSAGE FROM THIS PROBLEM
57
18.2.
TAKEAWAY MESSAGE FROM THIS PROBLEM
58
19 LEETCODE - LINKED LIST CYCLE
Leetcode Problem: Linked List Cycle Given a linked list, determine if it has a cycle in it. 19.1
naive approach
c l a s s ListNode i n t val ;
{
ListNode next ; ListNode( i n t x ) { va l = x ; n e x t = null ; } } p ub li c c l a s s S o l ut i on { public boolean hasCycle ( ListNode
head) {
ListNode p = head; i f ( head == null ) r e tu r n f a l s e ; i f (p. next == null ) r e tu r n f a l s e ; while ( p . n e x t ! = null ) { i f (head == p . next ) { r e t u rn t r u e ;
} p = p. next ; } r e tu r n f a l s e ;
} }
Result:
59
19.2.
ACCEPTED APPROACH
Submission Result: Time Limit Exceeded Last executed input: 3,2,0,-4, tail connects to node index 1
19.2
accepted approach
Use fast and low pointer. The advantage about fast/slow pointers is that when a circle is located, the fast one will catch the slow one for sure. p ub li c c l a s s S o l ut i on { public boolean hasCycle ( ListNode
head) {
L is tN od e f a s t = h ead ; ListNode slow = head; i f ( head == null ) r e tu r n f a l s e ; i f (head. next == null ) r e tu r n f a l s e ; while ( f a s t
! = null && fa st . next != null ) { slow = slow . next ; fa st = fa st . next . next ; if
( s l ow == f a s t ) r e t u rn t r u e ;
} r e tu r n f a l s e ;
} }
60
20 LEETCO DE - COPY LIST WITH RANDOM POINTER
Problem: A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. Return a deep copy of the list. 20.1
how to solve this problem ?
•
Copy every node, i.e., duplicate every node, and insert it to the list
•
Copy random pointers for all newly created nodes
•
Break the list to two
20.2
f i r st at t em p t
What is wrong with the following code? / ∗ ∗ ∗ D e f in i t i on f o r s in gl y − l i n k e d l i s t w it h a random p o i n t e r . ∗ cl as s R a nd o mL i st No de { ∗ i nt l a b e l ; ∗ RandomListNode next , random ; ∗ R an do mL is tN od e ( i n t x ) { t h i s . l a b e l = x ; } ∗ }; ∗ / p ub li c c l a s s S o l ut i on { public RandomListNode copyRandomList ( RandomListNode head) { i f ( head == null ) r e t ur n n u ll ;
RandomListNode p = head; / / c op y e v e r y node an d i n s e r t t o l i s t while ( p ! = null ) { RandomListNode copy = new RandomListNode(p. l a b e l ) ;
copy . next = p . next ; p . next = copy ;
61
20.3.
CORRECT SOLUTION
p = copy . next ; } / / c op y ra nd om p o i n t e r f o r e a c h new node
p = head; while ( p ! = null ) { p . next . random = p . random . next ; / / p . r an dom c an b e n u l l , s o n e ed n u l l checking here !
p = p . next . next ; } / / b r e a k l i s t t o tw o
p = head; while ( p ! = null ) { p . next = p . next . next ; p = p. next ; / / p o i n t t o t h e wr on g n od e now ! } return
head . next ;
} }
The code above seems totally fine. It follows the steps designed. But it has run-time errors. Why? The problem is in the parts of copying random pointer and breaking list. 20.3
c o rr e ct s o lu t io n
public
RandomListNode copyRandomList ( RandomListNode head ) { if
(head == null ) r e t u rn n u ll ;
RandomListNode p = head; / / c op y e v e r y node an d i n s e r t t o l i s t while ( p ! = null ) { RandomListNode copy = new RandomListNode(p. l a b e l ) ;
copy . next = p . next ; p . next = copy ; p = copy . next ; } / / c op y ra nd om p o i n t e r f o r e a c h new node
p = head; while ( p ! = null ) { i f (p.random != null ) p . next .random = p . random. next ; p = p . next . next ; }
62
20.3.
CORRECT SOLUTION
/ / b r e a k l i s t t o tw o
p = head; RandomListNode newHead = head . ne xt ; while ( p ! = null ) { RandomListNode temp = p . nex t ; p . next = temp . next ; i f (temp . next != null ) temp. next = temp. next . next ; p = p. next ; } r e t u r n newHead;
}
The break list part above move pointer 2 steps each time, you can also move one at a time which is simpler, like the following: while ( p
! = null && p . next != null ) { RandomListNode temp = p . nex t ; p . next = temp . next ; p = temp;
}
63
21 L E E T C O D E - M E R G E T W O S O R T E D L I S T S ( J AVA )
Problem: Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. 21.1
key to solve this problem
The key to solve the problem is defining a fake head. Then compare the first elements from each list. Add the smaller one to the merged list. Finally, when one of them is empty, simply append it to the merged list, since it is already sorted. 21.2
java solution
/ ∗ ∗ f o r s in gl y − l i n k e d l i s t . ∗ D e f in i t i on ∗ p u bl i c c l a s s L is tN od e { int val ; ∗ ∗ ListNode next ; L i st N od e ( i n t x ) { ∗ ∗ val = x ; n e xt = n u l l ; ∗ ∗ } ∗ } ∗ / p ub li c c l a s s S o l ut i on { public ListNode mergeTwoLists( ListNode l 1 , L is tN od e l 2 ) {
ListNode p1 = l 1 ; ListNode p2 = l 2 ; ListNode fakeHead = new ListNode ( 0 ) ; ListNode p = fakeHead ; while ( p 1 != null && p2 != null ) { i f ( p 1 . val <= p 2 . va l ) {
p . n e xt = p1 ; p 1 = p 1 . next ;
64
21.2.
} e l s e { p . n e xt = p2 ; p 2 = p 2 . next ; } p = p. next ; } i f ( p 1
!= null ) p . n e x t = p1 ; i f ( p 2 != null ) p . n e x t = p2 ; return
} }
fakeHead . ne xt ;
JAVA SOLUTION
65
22 L E E T C O D E - R E M O V E D U P L I C A T E S F R O M S O RT E D L I S T
Given a sorted linked list, delete all duplicates such that each element appear only once. For example, Given Given
22.1
1 − >1 − >2 , r e t u r n 1 − > 2 . 1−>1−>2−>3−>3, r e t u r n 1 −>2 −>3.
t ho ug ht s
The key of this problem is using the right loop condition. And change what is necessary in each loop. You can use different iteration conditions like the following 2 solutions. 22.2
s ol ut io n 1
/ ∗ ∗ ∗ D e f in i t i on f o r s in gl y − l i n k e d l i s t . c l a s s L is tN od e { ∗ p u bl i c ∗ int val ; ListNode next ; ∗ ∗ L i st N od e ( i n t x ) { val = x ; ∗ ∗ n e xt = n u l l ; } ∗ ∗ } ∗ / p ub li c c l a s s S o l ut i on { public ListNode dele teDup lica tes ( ListNode head) { i f ( head == null || head. next == null ) r e t u r n head;
ListNode prev = head; ListNode p = head. next ; while ( p ! = null ) { i f (p . val == prev . val ) {
prev . next = p . next ; p = p. next ;
66
22.3.
/ / n o c h a n g e p r e v } e l s e {
p re v = p ; p = p. next ; } } r e t u r n head;
} } 22.3
s ol ut io n 2
p ub li c c l a s s S o l ut i on { public ListNode dele teDup lica tes ( ListNode i f ( head == null || head. next == null ) r e t u r n head;
ListNode p = head; while ( p ! = null && p. next != null ) { i f (p . val == p . next . val ) {
p . next = p . next . next ; } e l s e { p = p. next ; } } r e t u r n head;
} }
head) {
SOLUTION 2
67
23 L E E T C O D E S O L U T I O N F O R B I N A R Y T R E E P R E O R D E R T R AV E R S A L I N J AVA
Preorder binary tree traversal is a classic interview problem about trees. The key to solve this problem is to understand the following: •
What is preorder? (parent node is processed before its children)
•
Use Stack from Java Core library
It is not obvious what preorder for some strange cases. However, if you draw a stack and manually execute the program, how each element is pushed and popped is obvious. The key to solve this problem is using a stack to store left and right children, and push right child first so that it is processed after the left child. p ub li c c l a s s TreeNode i n t val ;
{
TreeNode l e f t ; TreeNode ri gh t ; TreeNode( i n t x ) { val = x ; } } p ub li c c l a s s S o l ut i on { public ArrayList
> preorde rTraversal (TreeNode root ) { A r r ay L i st < I n t e g e r > r e t u r n L i s t = new ArrayList() ; i f ( root == null ) return returnList
;
Stack stack = new Stack ( ) ; st ac k . push( root ) ; while ( !
stac k .empty( ) ) { TreeNode n = stack .pop() ; ret urn Lis t .add(n . val ) ; if
( n . r i g h t ! = null ) { stack . push(n . righ t ) ;
} if
( n . l e f t != null ) { stack .push(n . le f t ) ;
}
68
69
} return
} }
returnList ;
24 L E E T C O D E S O L U T I O N O F B I N A R Y T R E E I N O R D E R T R AV E R S A L I N J AVA
The key to solve inorder traversal of binary tree includes the following: •
The order of “inorder” is: left child ->parent ->right child
•
Use a stack to track nodes
•
Understand when to push node into the stack and when to pop node out of the stack
/ ∗ ∗ f o r b in ar y t r e e ∗ De f i n it i o n ∗ p u b l ic c l a s s T re eNo de { int val ; ∗ ∗ TreeNode l e f t ; T re eN od e r i g h t ; ∗ ∗ T re eN od e ( i n t x ) { v a l = x ; } ∗ } ∗ / p ub li c c l a s s S o l ut i on { public ArrayList inord erTra versa l (TreeNode root ) { / / IMPORTANT : P l e a s e r e s e t an y me mber d a t a yo u d e c l a r e d , a s / / t h e sa me S o l u t i o n i n s t a n c e w i l l b e r e u s e d f o r e a c h t e s t c a s e . A r ra yL i st < I n t e g er > l s t = new ArrayList() ;
70
71
i f ( root == null ) return l s t ;
Stack stack = new Stack ( ) ; / / d e f i n e a p o i n t e r t o t r a c k n o d e s TreeNode p = root ; while ( !
stack . empty () || p != null ) {
/ / i f i t i s n o t n u l l , push t o s t a c k / / a nd go down t h e t r e e t o l e f t i f ( p ! = null ) {
sta ck .push( p) ; p = p . le ft ; / / i f no l e f t c h i l d / / pop s t a c k , p r o c e s s t h e n ode / / t h e n l e t p p o i n t t o t h e r i g h t } e l s e {
p = s t a c k . p op ( ) ; l s t . add( p . val ) ; p = p. right ; } } return
} }
ls t ;
25 LEETCO DE SOLUTION OF ITERATIVE BINARY TREE POSTORDER TRAVERSAL IN JAVA
The key to to iterative postorder traversal is the following: •
The order of “Postorder” is: left child ->right child ->parent node.
•
Find the relation between the previously visited node and the current node
•
Use a stack to track nodes
As we go down the tree, check the previously visited node. If it is the parent of the current node, we should add current node to stack. When there is no children for current node, pop it from stack. Then the previous node become to be under the current node for next loop. / ∗ ∗ ∗ De f i n it i o n f o r b in ar y t r e e c l a s s T re eNo de { ∗ p u b l ic ∗ int val ; TreeNode left ; ∗ ∗ T re eN od e r i g h t ; T re eN od e ( i n t x ) { v a l = x ; } ∗ ∗ } ∗ / p ub li c c l a s s S o l ut i on { public ArrayList postor derTrave rsal (TreeNode root ) {
A r ra yL i st < I n t e g er > l s t = new ArrayList() ; i f ( root == null ) return l s t ;
Stack stack = new Stack ( ) ; st ac k . push( root ) ; TreeNode prev = null ; while ( ! stac k .empty( ) ) { TreeNode curr = sta ck . peek ( ) ; / / go down t h e t r e e . / / c h e c k i f c u r r e n t no de i s l e a f , i f so , p r o c e s s i t an d pop s t a c k , / / o t h e r w i s e , k e e p g o i n g down i f (prev == null || prev . l e f t == curr || prev . rig ht == curr ) {
72
73
/ / p r e v == n u l l i s t h e s i t u a t i o n f o r t h e r o o t node i f ( c u r r . l e f t ! = null ) {
sta ck .push( curr . l e f t ) ; } e ls e i f ( curr . rig ht != null ) { sta ck . push( curr . rig ht ) ; } e l s e { stack .pop () ; l s t . add( curr . val ) ; } / / g o up t h e t r e e from l e f t node / / n e e d t o c h e c k i f t h e r e i s a r i g h t c h i l d / / i f yes , push i t t o s t a c k / / o t h e r w i s e , p r o c e s s p a r e n t an d pop s t a c k } e ls e i f ( curr . le ft == prev ) { i f ( curr . rig ht != null ) {
sta ck . push( curr . rig ht ) ; } e l s e { stack .pop () ; l s t . add( curr . val ) ; } / / g o up t h e t r e e from r i g h t node / / a f t e r comi ng b a c k from r i g h t nod e , p r o c e s s p a r e n t node an d pop stac k . } e ls e i f ( curr . rig ht == prev ) {
stack .pop () ; l s t . add( curr . val ) ; } prev = curr ; } return
} }
ls t ;
26 LEETCODE - WORD LADDER
The problem: Given two words ( s t a r t and end ) , and a d i c t i on a r y , f i n d t h e l e n gt h o f s h o r t e s t t r a n s f o r m a ti o n s eq ue nc e fro m s t a r t t o end , s uc h t h a t : Only o ne l e t t e r ca n b e c hange d a t a t im e Each i n t e rm e d i at e word m ust e x i s t i n t h e d i c t i o n a r y For example , Given : star t = " hit " end = "cog" dic t = [ " hot" , " dot" , "dog" , " lo t " , " log " ] As o ne s h o r t e s t t r a ns f o rm a t io n i s " h i t " r e t u r n i t s l en gt h 5 .
> "hot"
−
> "dot"
−
> "dog"
−
> "cog" ,
−
Note: Return 0 i f t h e r e i s no s uc h t r a n s f o r m a t i o n s e qu en ce . All words have the same length . A l l words c o n t a i n o nl y l o we r c as e a l p h a b e t i c c h a r a c t e r s .
This problem is a classic problem that has been asked frequently during interviews. The following are two Java solutions. 26.1
naive approach
In a simplest way, we can start from start word, change one character each time, if it is in the dictionary, we continue with the replaced word, until start == end. p ub li c c l a s s S o l ut i on { p u bl i c i n t l a dd e rL e ng t h ( S t r i n g
s t a r t , S t r i n g end , H as hS et < S t r i n g > d i c t ) {
len = 0 ; H as hS et < S t r i n g > v i s i t e d = new HashSet( ) ; int
f o r ( i n t i = 0 ; i < s t a r t . l e n gt h ( ) ; i ++) { char [ ] s t a r t A r r = s t a r t . t oC ha rA rr ay ( )
74
;
26.2.
f o r ( char c = ’ a ’ ; c <= ’ z ’ ; c + +) { i f ( c==s t ar t . toCharArray ( ) continue ;
BREATH FIRST SEARCH
[i ]) {
} sta rtA rr [ i ] = c ; S t r i n g t e m p = new Stri ng ( sta rtA rr ) ; i f ( dic t . cont ai ns (temp) ) { len++; start = temp; i f ( temp. eq uals (end) ) { r e t u r n len ; } } } } r e t u r n len
;
} }
Apparently, this is not good enough. The following example exactly shows the problem. It can not find optimal path. The output is 3 , but it actually only takes 2 . I np ut : " a " , " c " , [ " a " , " b " , " c " ] Output : 3 2 Expecte d :
26.2
breath first search
So we quickly realize that this looks like a tree searching problem for which breath first guarantees the optimal solution. Assuming we have all English words in the dictionary, and the start is “hit” as shown in the diagram below.
75
26.2.
BREATH FIRST SEARCH
We can use two queues to traverse the tree, one stores the nodes, the other stores the step num bers. Before starting coding, we can visualize a tree in mind and come up with the following solution. p ub li c c l a s s S o l ut i on { p u bl i c i n t l a dd e rL e ng t h ( S t r i n g if
( di ct . siz e () == return 0 ;
int
result =
s t a r t , S t r i n g end , H as hS et < S t r i n g > d i c t ) {
0)
0;
Linke dLis t wordQueue = new LinkedList() ; LinkedList distanceQueue = new LinkedList() ; wordQueue . add ( st a r t ) ; distanceQueue . add ( 1 ) ; while ( ! wordQueue . isEmpty () ) {
St ri ng currWord = wordQueue . pop ( ) ; Inte ger currDistanc e = distanceQueue . pop () ; i f ( currWord . equa ls (end) ) { r e t u r n currDistan ce ;
} f o r ( i n t i = 0 ; i
i ++) { toCharArray () ;
currCharArr[ i ] = c ; St ri ng newWord = new Stri ng ( currCharArr ) ;
76
26.3.
i f (
WHAT LEARNED FROM THIS PROBLEM?
di c t . co nt ai ns (newWord) ) { wordQueue . add (newWord) ; distanceQueue . add( currDi stan ce + 1 ) ; d i c t . remove (newWord) ;
} } } } return 0 ;
} }
26.3
what learned from this problem ?
•
Use breath-first or depth-first search to solve problems
•
Use two queues, one for words and another for counting
77
27 LEETCODE - VALIDATE BINARY SEARCH TREE (JAVA)
Problem: Given a binary tree, determine if it is a valid binary search tree (BST).
Assume a BST is defined as follows: •
The left subtree of a node contains only nodes with keys less than the node’s key.
•
The right subtree of a node contains only nodes with keys greater than the node’s key.
•
Both the left and right subtrees must also be binary search trees.
27.1
thoughts about this problem
All values on the left sub tree must be less than root, and all values on the right sub tree must be greater than root. 27.2
java solution
/ / D e fi n i ti o n f o r binary t r e e c l a s s TreeNode { i n t val ;
TreeNode l e f t ; TreeNode ri gh t ; TreeNode( i n t x ) { v al = x ; } } p ub li c c l a s s
S o l ut i on {
p u bl ic s t a t i c b oo le an isValidBST (TreeNode root ) r e t u r n v a l i d at e ( ro ot , In t e ge r .MIN_VALUE,
{ In t e ge r .MAX_VALUE) ;
} p u bl ic s t a t i c b oo le an
val id at e (TreeNode root , i n t min, i n t max) {
78
27.2.
if
JAVA SOLUTION
( root == null ) { r e t u rn t r u e ;
} / / n o t i n r a n g e i f ( root . val <= min || root . val >= max) { r e tu r n f a l s e ;
} / / l e f t s u b t r e e mu st b e < r o o t . v a l && r i g h t s u b t r e e mu st b e > roo t . val r e t u r n val ida te ( root . le ft , min , root . val ) && val ida te ( root . right
, root . val , max) ; } }
79
28 L E E T C O D E - F L AT T E N B I N A R Y T R E E T O L I N K E D L I S T
Given a binary tree, flatten it to a linked list in-place. For example, Given 1
/ \ 2
5
/ \ 3
\ 4
6
The flattened tree should look like: 1
\ 2
\ 3
\ 4
\ 5
\ 6
28.1
t ho ug ht s
Go down through the left, when right is not null, push right to stack. 28.2
java solution
/ ∗ ∗ ∗ De f i n it i o n f o r b in ar y t r e e ∗ p u b l ic c l a s s T re eNo de { ∗ int val ; ∗ TreeNode l e f t ; ∗ T re eN od e r i g h t ;
80
28.2.
T re eN od e ( i n t x ) { v a l = x ; }
∗ ∗
}
/ p ub li c c l a s s S o l ut i on { public void f l a t t e n ( T ree Node r o o t ) { Stack stack = new Stack ( ) ; ∗
TreeNode p = root ; while ( p
! = null || ! st ac k . empty ( ) ) {
i f (p .
r i g h t ! = null ) { stack . push(p. ri ght ) ;
} ( p . l e f t != null ) { p. right = p. le ft ; p . l e f t = null ; } e ls e i f ( ! stack . empty () ) { TreeNode temp = sta ck . pop ( ) ; p . ri gh t=temp ; } if
p = p. right ; } } }
JAVA SOLUTION
81
29 LEETCODE - PATH SUM
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum. For example: Given the below binary tree and sum = 22 , 5
/ \ 4
8
/
/ \
11
13
4
/ \ 7
\ 2
1
return true, as there exist a root-to-leaf path 5 ->4->11->2 which sum is 22 . 29.1
t ho ug ht s
Add all node to a queue and store sum value of each node to another queue. When it is a leaf node, check the stored sum value. For example above, the queue would be: 5 - 4 - 8 and 1 . This is a typical breadth first search(BFS) problem. 29.2
java solution
/ ∗ ∗ ∗ De f i n it i o n f o r b in ar y t r e e ∗ p u b l ic c l a s s T re eNo de { ∗ int val ; ∗ TreeNode l e f t ; ∗ T re eN od e r i g h t ; ∗ T re eN od e ( i n t x ) { v a l = x ; } ∗ } ∗ / p ub li c c l a s s S o l ut i on {
82
11 - 13 - 4 - 7 - 2 - 1.
It will check node 13 , 7, 2
29.2.
public boolean hasPathSum( TreeNode i f ( root == null ) r e tu r n f a l s e ;
JAVA SOLUTION
roo t , i n t sum) {
LinkedList nodes = new LinkedList() ; LinkedList values = new LinkedList() ; nodes . add ( roo t ) ; valu es . add( roo t . val ) ; while ( ! nodes . isEmpty () ) {
TreeNode curr = nodes . po ll () ; i n t sumValue = valu es . po ll ( ) ; i f ( curr . le f t == null && r e t u rn t r u e ;
curr . ri gh t == null && sumValue==sum) {
} i f ( c u r r .
l e f t ! = null ) { nodes . add( curr . l e f t ) ; value s .add( sumValue+cur r . l e f t . val ) ;
} i f ( curr .
ri ght != null ) { nodes . add( curr . r ig ht ) ; value s . add( sumValue+cur r . ri gh t . val ) ;
} } r e tu r n f a l s e ;
} }
83
30 CONSTR UCT BINARY TREE FROM INORDER AND POSTORDER TRAVERSAL
Given inorder and postorder traversal of a tree, construct the binary tree. 30.1
t hr ou gh ts
This problem can be illustrated by using a simple example. 4 in −order : post −order : 4
2 5 5 2
(1)
6 7 3 8 6 7 8 3 (1)
From the post-order array, we know that last element is the root. We can find the root in in-order array. Then we can identify the left and right sub-trees of the root from in-order array. Using the length of left sub-tree, we can identify left and right sub-trees in post-order array. Recursively, we can build up the tree. 30.2
java solution
/ / D e f i n i t i o n f o r b i n a r y t r e e p ub li c c l a s s TreeNode { i n t val ;
TreeNode l e f t ; TreeNode ri gh t ; TreeNode( i n t x ) { val = x ; } } p ub li c c l a s s S o l ut i on { public TreeNode buil dTr ee ( i n t [ ] i n o rd e r int i n S t a r t = 0 ; i n t inEnd = inorder . length − 1 ; int p o s t S t a r t =0; i n t postEnd = postorder . length − 1 ; return
, i n t [ ] p o st o rd e r ) {
buildTree ( inorder , inS tar t , inEnd , postorder , post Star t , postEnd )
; }
84
30.2.
JAVA SOLUTION
TreeNode buil dTr ee ( i n t [ ] i n o rd e r , i n t i n S t a r t , i n t inEnd , i n t [ ] p o s to r de r , i n t p o s t S t a r t , i n t postEnd) { i f ( i n S t a r t > i nE nd || p o s t S t a r t > p os tE nd ) r e t ur n n u ll ;
public
rootValue = postorder [ postEnd ] ; TreeNode root = new TreeNode ( roo tVa lue ) ; int
in t k =0; f o r ( i n t i = 0 ; i < i n o rd e r . l e n gt h ; i f ( ino rde r [ i ]==roo tValue ) {
i ++) {
k = i; break ; } } r o o t . l e f t = b u i l dT r e e ( i n o rd e r , i n S t a r t , k − 1 , p o st or de r , p o s t S t a r t , p o s t S t a r t + k −( i n S t a r t + 1 ) ) ; / / B e c u a s e k i s n o t t h e l e n g t h , i t i t n e e d t o length
−
( i n S t a r t +1) t o g e t t h e
root . rig ht = buildTree ( inorder , k+ 1 , in End , p o s to r de r , p o s t S t a r t +k− inS tar t , postEnd − 1) ; / / p o s t S t a r t +k − i n S t a r t = p o s t S t a r t + k −( i n S t a r t + 1 ) + 1 return
} }
root ;
85
31 L E E T C O D E C L O N E G R A P H J A VA
LeetCode Problem: Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.
31.1
•
key to solve this problem
A queue is used to do breath first traversal.
86
31.1.
•
KEY TO SOLVE THIS PROBLEM
a map is used to store the visited nodes. It is the map between original node and copied node.
It would be helpful if you draw a diagram and visualize the problem.
/ ∗ ∗ ∗ Definition f o r u n d i r ec t e d g ra ph . ∗ cl as s U n di r ec t ed G ra p hN o de { ∗ i nt l a b e l ; ∗ A r r a y L i s t < U n d i r ec t e d G r a p hN o d e > n e i g h b o r s ; ∗ U n di r ec t ed G ra p hN o de ( i n t x ) { l a b e l = x ; n e i g h b o r s = new A r r a yL i s t < U n d i r e c te d G r a p h No d e > ( ) ; } ∗ }; ∗ / p ub li c c l a s s S o l ut i on { public UndirectedGraphNode cloneGraph( UndirectedGraphNode node) { i f (node == null ) r e t ur n n u ll ;
Linke dLis t queue = new LinkedList< UndirectedGraphNode >( ) ; HashMap map = new HashMap( ) ;
87
31.1.
KEY TO SOLVE THIS PROBLEM
UndirectedGraphNode newHead = new UndirectedGraphNode(node. l a b e l ) ; queue . add( node) ; map . put ( node , newHead) ; while ( ! queue . isEmpty () ) {
UndirectedGraphNode cur r = queue. pop ( ) ; ArrayList currNeighbors = curr . neighbors ; f o r (UndirectedGraphNode aNeighbor : currNei ghbors ) { i f ( !map. contai nsKey (aNeighbor) ) { UndirectedGraphNode copy = new UndirectedGraphNode(aNeighbor . l a b e l ) ;
map. put ( aNeighbor , copy) ; map. ge t ( cur r ) . nei ghb ors . add ( copy) ; queue.add(aNeighbor) ; } e l s e { map. g et ( cur r ) . nei ghb ors . add (map. g et ( aNeighbor ) ) ; } } } r e t u r n newHead;
} }
88
32 L E E T C O D E S O L U T I O N - M E R G E S O R T L I N K E D L I S T I N J AVA
LeetCode - Sort List: Sort a linked list in O(n log n) time using constant space complexity. 32.1
keys for solving the problem
•
Break the list to two in the middle
•
Recursively sort the two sub lists
•
Merge the two sub lists
This is my accepted answer for the problem. package
algorithm . so rt ;
c l a s s ListNode i n t val
{ ; ListNode next ; ListNode( i n t x ) { v al = x ; n e x t = null ; }
} p ub li c c l a s s
S o r t L i nk e d L is t {
/ / merge s o r t p ub li c s t a t i c ListNode mergeSortList ( ListNode head) { if
(head == null || head. next == null ) r e t u r n head;
/ / c o u n t t o t a l nu mb er o f e l e m e n t s i n t count = 0 ;
ListNode p = head; while ( p ! = null ) { count++;
89
32.1.
KEYS FOR SOLVING THE PROBLEM
p = p. next ; } / / b r e a k up t o tw o l i s t i n t middle = count / 2 ;
L is tN od e l = h ead , r = null ; ListNode p2 = head; i n t countHalf = 0 ; while ( p 2 != null ) { countHalf++; L is tN od e n e x t = p2 . next ; if
( countHalf == middle) { p 2 . next = null ; r = next ;
} p2 = next ; } / / now we h a v e tw o p a r t s l an d r , r e c u r s i v e l y s o r t the m
ListNode h1 = merge SortLi st ( l ) ; ListNode h2 = merge SortLi st ( r ) ; / / merge t o g e t h e r
ListNode merged = merge(h 1 , h 2 ) ; r e t u r n merged;
} ListNode merge( ListNode l , ListNode r ) { ListNode p1 = l ; ListNode p2 = r ;
p ub li c s t a t i c
ListNode fakeHead = new ListNode( 1 0 0 ) ; ListNode pNew = fakeHead ; while
( p1 != null || p 2 != null ) { ( p1 == null ) { pNew. next = new ListNode(p2 . va l ) ; p 2 = p 2 . next ; pNew = pNew. n ex t ; } e ls e i f ( p 2 == null ) { pNew. next = new ListNode(p1 . va l ) ; p 1 = p 1 . next ; pNew = pNew. n ex t ; } else { i f ( p 1 . v a l < p 2 . val ) { if
/ / i f ( f a k e H e a d )
pNew. next = new ListNode(p1 . va l ) ; p 1 = p 1 . next ;
90
32.1.
KEYS FOR SOLVING THE PROBLEM
pNew = pNew. next ; } e ls e i f ( p1 . val == p 2 . val ) { pNew. next = new ListNode(p1 . va l ) ; pNew. nex t . ne xt = new ListNode(p1 . val ) ; pNew = pNew. next . next ; p 1 = p 1 . next ; p 2 = p 2 . next ; } else { pNew. next = new ListNode(p2 . va l ) ; p 2 = p 2 . next ; pNew = pNew. next ; } } } / / p r i n t L i s t ( f a k e H e a d . n e x t ) ; r e t u r n fakeHead . ne xt ;
} main( Stri ng [] args ) { ListNode n1 = new ListNode ( 2 ) ; ListNode n2 = new ListNode ( 3 ) ; ListNode n3 = new ListNode ( 4 ) ;
p ub li c s t a t i c v oid
ListNode n4 = new ListNode ( 3 ) ; ListNode n5 = new ListNode ( 4 ) ; ListNode n6 = new ListNode ( 5 ) ; n1 . n e x t n2 . n e x t n3 . n e x t n4 . n e x t n5 . n e x t
= = = = =
n2 ; n3 ; n4 ; n5 ; n6 ;
n1 = m e r g e S or t L i s t ( n 1 ) ; p r i n t L i s t ( n1 ) ; } p ub li c s t a t i c v oid p r i n t L i s t i f ( x != null ) {
( L i st No de x ) {
System . out . pri nt (x . val + " " ) ; while ( x . n e xt ! = null ) { System . out . prin t ( x . next . val + " " ) ; x = x . next ; } System . out . pr in tl n () ; } } }
91
32.1.
Output: 233445
KEYS FOR SOLVING THE PROBLEM
92
33 Q U I C K S O R T A R R A Y I N J AVA
Quicksort is a divide and conquer algorithm. It first divides a large list into two smaller sub-lists and then recursively sort the two sub-lists. If we want to sort an array without any extra space, Quicksort is a good option. On average, time complexity is O(n log(n)). The basic step of sorting an array are as follows: •
•
•
Select a pivot, normally the middle one From both ends, swap elements and make all elements on the left less than the pivot and all elements on the right greater than the pivot Recursively sort left part and right part
package
algorithm . so rt ;
p ub li c c l a s s
QuickSort {
p ub li c s t a t i c v oid main( Stri ng [] args ) { i n t [ ] x = { 9 , 2 , 4 , 7 , 3 , 7 , 10
};
printArray (x ) ; i n t low = 0 ; i n t h ig h = x . l e n g t h
−
1;
quickSort (x , low , high ) ; printArray (x ) ; } p ub li c s t a t i c v oid if
if
quickSort ( i n t [ ] a rr , i n t low , i n t hi gh ) {
( arr == null || arr . length == r e t u r n ;
0)
(low >= high) r e t u r n ;
/ / p i c k t h e p i v o t i n t middle = low + (high i n t pivot = arr [ middle ] ;
−
low) /
2;
/ / m ak e l e f t < p i v o t and r i g h t > p i v o t
93
94
int i while
= l ow , j = h ig h ; ( i <= j ) { while ( arr [ i ] < pivot ) { i ++; } while
( arr [ j ] > pivot ) { j −−;
} if
( i <= j ) { i n t temp = arr [ i ] ; arr [ i ] = arr [ j ]; arr [ j ] = temp ; i ++; j −−;
} } / / r e c u r s i v e l y s o r t tw o sub p a r t s i f ( l ow < j )
quick Sort ( arr , low , j ) ; if
( h i gh > i ) quickSort ( arr , i , high ) ;
} printA rray ( i n t [ ] x ) { : x) System . out . pri nt ( a + " " ) ; System . out . pr in tl n () ;
p ub li c s t a t i c v oid f o r ( i n t a
} }
Output: 9 2 4 7 3 7 10 2 3 4 7 7 9 10
The mistake I made is selecting the middle element. The middle element is not (low+high)/2, but low + (high-low)/2. For other parts of the programs, just follow the algorithm.
34 LEETCO DE SOLUTIO N - SORT A LINKED LIST USING INSERTIO N SORT IN J AVA
Insertion Sort List: Sort a linked list using insertion sort.
This is my accepted answer for LeetCode problem - Sort a linked list using insertion sort in Java. It is a complete program. Before coding for that, here is an example of insertion sort from wiki. You can get an idea of what is insertion sort.
Code: package
algorithm . so rt ;
c l a s s ListNode i n t val
{ ; ListNode next ; ListNode( i n t x ) { v al = x ; n e x t = null ; }
95
96
} p ub li c c l a s s S o r t L i nk e d L is t { p ub li c s t a t i c L i st No de if
i n s e r t i o n S o r t L i s t ( L is tN od e h ead ) {
(head == null || head. next == null ) r e t u r n head;
ListNode newHead = new ListNode ( head . val ) ; ListNode pointe r = head. next ; / / l o o p t h r o u g h e a c h e l e m e n t i n t h e l i s t while ( p o i n t e r ! = null ) { / / i n s e r t t h i s e l e m e n t t o t h e new l i s t
ListNode inn erP oin ter = newHead; ListNode next = pointe r . next ; ( po in te r . val <= newHead. val ) { ListNode oldHead = newHead ; newHead = po in te r ; newHead . nex t = oldHead ; } else { while ( inne rPoin ter . next != null ) { if
if
( point er . val > inne rPoin ter . val && pointe r . val <= inner Point er . next . val ) { ListNode oldNext = inner Point er . next ; innerPointer . next = pointer ; poi nte r . next = oldNext ;
} i n n e r P o i n t e r = i n n e r P o i n t e r . n e xt ; } if
( inner Point er . next == null && poi nte r . val > innerPointer . val ) { innerPointer . next = pointer ; pointe r . next = null ;
} } / / f i n a l l y
pointer = next ; } r e t u r n newHead;
} p ub li c s t a t i c v oid
main( Stri ng [] args ) {
97
ListNode n1 = new ListNode ( 2 ) ; ListNode n2 = new ListNode ( 3 ) ; ListNode n3 = new ListNode ( 4 ) ; ListNode n4 = new ListNode ( 3 ) ; ListNode n5 = new ListNode ( 4 ) ; ListNode n6 = new ListNode ( 5 ) ; n1 . n e x t n2 . n e x t n3 . n e x t n4 . n e x t n5 . n e x t
= = = = =
n2 ; n3 ; n4 ; n5 ; n6 ;
n1 = i n s e r t i o n S o r t L i s t ( n 1 ) ; p r i n t L i s t ( n1 ) ; } p ub li c s t a t i c v oid p r i n t L i s t i f ( x != null ) {
( L i st No de x ) {
System . out . pri nt (x . val + " " ) ; while ( x . n e xt ! = null ) { System . out . prin t ( x . next . val + " " ) ; x = x . next ; } System . out . pr in tl n () ; } } }
Output: 233445
35 I T E R A T I O N V S . R E C U R S I O N I N J AVA
35.1
r ec ur si on
Consider the factorial function: n!=n*(n-1)*(n-2)*...*1 There are many ways to compute factorials. One way is that n! is equal to n*(n- 1)!. Therefore the program can be directly written as: Program 1 : int
f a c t o r i a l ( i n t n ) { i f (n == 1 ) { return 1 ; } else { r e t u r n n ∗ f a c t o r i a l ( n− 1 ) ; }
}
In order to run this program, the computer needs to build up a chain of multiplications: factorial(n) ˘ e˛ → factorial(1). Therefore, the computer has to keep track → factorial(n-1) → factorial(n-2) → â A of the multiplications to be performed later on. This type of program, characterized by a chain of operations, is called recursion. Recursion can be further categorized into linear and tree recursion. When the amount of information needed to keep track of the chain of operations grows linearly with the input, the recursion is called linear recursion. The computation of n! is such a case, because the time required grows linearly with n. Another type of recursion, tree recursion, happens when the amount of information grows exponentially with the input. But we will leave it undiscussed here and go back shortly afterwards. 35.2
i te rat io n
A different perspective on computing factorials is by first multiplying 1 by 2, then multiplying the result by 3 , then by 4 , and so on until n. More formally, the program can use a counter that counts from 1 up to n and compute the product simultaneously until the counter exceeds n. Therefore the program can be written as: Program 2 : int
f a c t o r i a l ( i n t n ) {
98
35.3.
i n t product = 1 ; f o r ( i n t i = 2 ; i
product
∗
RECURSION VS ITERATION
i ++) {
= i;
} r e t u r n product
;
}
This program, by contrast to program 2 , does not build a chain of multiplication. At each step, the computer only need to keep track of the current values of the product and i. This type of program is called iteration, whose state can be summarized by a fixed number of variables, a fixed rule that describes how the variables should be updated, and an end test that specifies conditions under which the process should terminate. Same as recursion, when the time required grows linearly with the input, we call the iteration linear recursion. 35.3
recursion vs iteration
Compared the two processes, we can find that they seem almost same, especially in term of mathematical function. They both require a number of steps proportional to n to compute n!. On the other hand, when we consider the running processes of the two programs, they evolve quite differently. In the iterative case, the program variables provide a complete description of the state. If we stopped the computation in the middle, to resume it only need to supply the computer with all variables. However, in the recursive process, information is maintained by the computer, therefore “hidden” to the program. This makes it almost impossible to resume the program after stopping it. 35.4
t r ee r e cu r si o n
As described above, tree recursion happens when the amount of information grows exponentially with the input. For instance, consider the sequence of Fibonacci numbers defined as follows:
By the definition, Fibonacci numbers have the following sequence, where each number is the sum of the previous two: 0, 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , ... A recursive program can be immediately written as: Program 3 : int
f i b ( i n t n ) { i f (n == 0 ) { return 0 ; } e ls e i f (n ==
1)
{
99
35.4.
}
return 1 ; else { r e t u r n fi b (n − 1 )
TREE RECURSION
+ f i b ( n − 2) ;
} }
Therefore, to compute fib( 5), the program computes fib(4) and fib(3). To computer fib(4), it computes fib(3) and fib(2). Notice that the fib procedure calls itself twice at the last line. Two observations can be obtained from the definition and the program: ei/â´LŽ5 rounded to the nearest integer, which • The ith Fibonacci number Fib(i) is equal to β indicates that Fibonacci numbers grow exponentially. •
This is a bad way to compute Fibonacci numbers because it does redundant computation. Computing the running time of this procedure is beyond the scope of this article, but one can easily find that in books of algorithms, which is O(βen). Thus, the program takes an amount of time that grows exponentially with the input.
On the other hand, we can also write the program in an iterative way for computing the Fibonacci numbers. Program 4 is a linear iteration. The difference in time required by Program 3 and 4 is enormous, even for small inputs. Program 4 : int
f i b ( i n t n ) { int f i b = 0; int a = 1; f o r ( i n t i = 0 ; i
}
However, one should not think tree-recursive programs are useless. When we consider programs that operate on hierarchically data structures rather than numbers, tree-recursion is a natural and powerful tool. It can help us understand and design programs. Compared with Program 3 and 4, we can easily tell Program 3 is more straightforward, even if less efficient. After that, we can most likely reformulate the program into an iterative way.
100
36 E D I T D I S T A N C E I N J A VA
From Wiki: In computer science, edit distance is a way of quantifying how dissimilar two strings (e.g., words) are to one another by counting the minimum number of operations required to transform one string into the other.
There are three operations permitted on a word: replace, delete, insert. For example, the edit distance between “a” and “b” is 1, the edit distance between “abc” and “def” is 3. This post analyzes how to calculate edit distance by using dynamic programming. 36.1
k e y a na ly s is
Let dp[i][j] stands for the edit distance between two strings with length i and j, i.e., word 1[0,...,i-1] and word2[0,...,j-1]. There is a relation between dp[i][j] and dp[i-1][j-1]. Let’s say we transform from one string to another. The first string has length i and it’s last character is “x”; the second string has length j and its last character is “y”. The following diagram shows the relation.
101
36.2.
•
if x == y, then dp[i][j] == dp[i- 1][j-1]
•
if x != y, and we insert y for word1, then dp[i][j] = dp[i][j-1] + 1
•
if x != y, and we delete x for word1, then dp[i][j] = dp[i-1][j] + 1
•
if x != y, and we replace x with y for word 1, then dp[i][j] = dp[i-1][j-1] + 1
•
When x!=y, dp[i][j] is the min of the three situations.
Initial condition: dp[i][0] = i, dp[0][j] = j 36.2
java code
After the analysis above, the code is just a representation of it. p ub li c s t a t i c i n t minDistance ( Str ing i n t len 1 = word 1 . length () ; i n t len 2 = word 2 . length () ;
word1 , S t r i n g word2 ) {
/ / l e n 1 + 1 , l e n 2 + 1 , b e c au s e f i n a l l y r e tu r n dp [ l e n 1 ] [ l e n 2 ] i n t [ ] [ ] dp = new in t [ len 1 + 1 ][ len 2 + 1 ] ; for
( i n t i = 0 ; i <= l en 1 ; i ++) { dp[ i ] [ 0 ] = i ;
} for
( i n t j = 0 ; j <= l en 2 ; j ++) { dp [ 0 ] [ j ] = j ;
JAVA CODE
102
36.2.
JAVA CODE
} / / i t e r a t e though , an d c h e c k l a s t c h a r f o r ( i n t i = 0 ; i < l e n 1 ; i ++) { char c 1 = word 1 . charAt ( i ) ; f o r ( i n t j = 0 ; j < l e n 2 ; j ++) { char c 2 = word2 . charAt ( j ) ; / / i f l a s t tw o c h a r s e q u a l i f ( c 1 == c 2 ) { / / u p d a t e dp v a l u e f o r + 1 l e n g t h
dp[ i +
1][
j +
1]
= dp[ i ][ j ] ;
} else { int int int
r e p l a c e = dp [ i ] [ j ] + 1 ; i n s e r t = dp [ i ] [ j + 1 ] + d e l e t e = dp [ i + 1 ] [ j ] +
1; 1;
i n t min = r e p la ce > i n s e r t ? i n s e r t : r e pl a ce ; min = d e l e t e > min ? min : d e l e t e ; dp[ i + 1 ] [ j + 1 ] = m i n ;
} } } r e t u r n dp[
}
len 1 ] [ len 2 ] ;
103
37 L E E T C O D E S O L U T I O N O F L O N G E S T PA L I N D R O M I C S U B S T R I N G I N J AVA
Finding the longest palindromic substring is a classic problem of coding interview. In this post, I will summarize 3 different solutions for this problem. 37.1
naive approach
Naively, we can simply examine every substring and check if it is palindromic. The time complexity is O(nˆ3). If this is submitted to LeetCode onlinejudge, an error message will be returned - “Time Limit Exceeded”. Therefore, this approach is just a start, we need better algorithm. p ub li c s t a t i c
S t r i n g l o n ge s t Pa l i nd r o me 1 ( S t ri n g s ) {
i n t maxPalinLength
= 0; S t r i n g l o n ge s t Pa l i nd r o me = null ; i n t length = s . length () ; / / c h e c k a l l p o s s i b l e sub s t r i n g s f o r ( i n t i = 0 ; i < l e ng th ; i + +) { f o r ( i n t j = i + 1 ; j < l en gt h ; j ++) { i n t l en = j − i ;
S t r i n g c u rr = s . s u b s t ri n g ( i , j + 1 ) ; i f ( isPal indro me ( cur r ) ) { i f ( len > maxPalinLength) { longestPalindrome = curr ; maxPalinLength = len ; } } } } return
longestPalindro me ;
} p u bl i c s t a t i c b oo le an for
i s Pa l i nd r o me ( S t r i n g s ) {
( i n t i = 0 ; i < s . l e n gt h ( ) − 1 ; i + + ) { i f ( s . charAt ( i ) != s . charAt ( s . lengt h () r e tu r n f a l s e ;
104
−
1
−
i)) {
37.2.
DYNAMIC PROGRAMMING
} } r e tu r n t r ue ;
}
37.2
dynamic programming
Let s be the input string, i and j are two indices of the string. Define a 2-dimension array “table” and let table[i][j] denote whether substring from i to j is palindrome. Start condition: ta bl e [ i ][ i ] == 1 ; ta bl e [ i ] [ i + 1 ] == 1
=> s . charAt ( i ) == s . charAt ( i + 1 )
Changing condition: ta bl e [ i ] [ j ] ==
=> ta bl e [ i + 1 ] [ j − 1] ==
1
1
&& s . charAt ( i ) == s . charAt ( j )
Time O(nˆ2) Space O(nˆ2) p ub li c s t a t i c if (s
i f ( s
S t r i n g l o n ge s t Pa l i nd r o me 2 ( S t ri n g s ) { = = null ) r e t u rn n u ll ;
. l e n g t h ( ) < =1 ) return s ;
i n t maxLen
= 0; S t r i ng l o n g e s t S tr = null ; int int
length = s . length () ; [ ] [ ] t ab le = new in t [ lengt h ][ length ] ;
/ / e v e r y s i n g l e l e t t e r i s p a l i n d r o m e f o r ( i n t i = 0 ; i < l e ng th ; i + +) {
tab le [ i ][ i ] =
1;
} printT able ( tab le ) ; / / e . g . b c b a / / t wo c o n s e c u t i v e sam e l e t t e r s a r e p a l i n d r o m e f o r ( i n t i = 0 ; i <= l e n gt h − 2 ; i + + ) { i f ( s . charAt ( i ) == s . charAt ( i + 1 ) ) {
tab le [ i ][ i + 1 ] = 1 ; l o n g e s t S t r = s . s u b s t ri n g ( i , i + } }
2) ;
105
37.3.
SIMPLE ALGORITHM
printT able ( tab le ) ; / / c o n d i t i o n f o r c a l c u l a t e wh o le t a b l e f o r ( i n t l = 3 ; l <= l e n gt h ; l ++) { f o r ( i n t i = 0 ; i <= l e ng th − l ; i + +) { int j = i + l − 1 ; i f ( s . charAt ( i ) == s . charAt( j ) ) {
table [ i ][ j ] = table [ i + 1 ][ j − 1 ] ; i f ( ta bl e [ i ] [ j ] == 1 && l > maxLen) l o n g e s t S t r = s . s u b s t ri n g ( i , j +
1) ;
} else { tab le [ i ][ j ] =
0;
} print Table ( tab le ) ; } } return
longestStr ;
} p ub li c s t a t i c v oid print Table ( i n t [ ] [ ] f o r ( i n t [ ] y : x ) { f o r ( i n t z : y) {
x){
System . out . pri nt ( z + " " ) ; } System . out . pr in tl n () ; } System . out . pr i nt l n ( "−−−−−− " ) ; }
Given an input, we can use printTable method to examine the table after each iteration. For example, if input string is “dabcba”, the final matrix would be the following: 1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 1 0 1 0
0 1 0 0 0 1
From the table, we can clear see that the longest string is in cell table[ 1][5]. 37.3
s i m pl e a l go r it h m
Time O(nˆ2), Space O(1) public
S t r i n g l o n ge s t Pa l i nd r o me ( S t r i n g s ) { i f ( s . isEmpty ( ) ) { r e t u rn n u ll ; } if
( s . length () == return s ;
1)
{
106
37.4.
MANACHER’S ALGORITHM
} S t r i n g l o n g e s t = s . s u b s t r i n g ( 0 , 1 ) ; f o r ( i n t i = 0 ; i < s . l e ng th ( ) ; i ++) { / / g e t l o n g e s t p a l i n d r o m e w i th c e n t e r o f i
S t r i n g tmp = h e l p e r ( s , i , i ) ; i f (tmp. length () > longe st . length () ) { longest = tmp; } / / g e t l o n g e s t p a l i n d r o m e w i th c e n t e r o f i , i + 1
tmp = h e l pe r ( s , i , i + 1 ) ; i f (tmp. length () > longe st . length () ) { longest = tmp; } } return
longe st ;
} / / Given a c e n t e r , e i t h e r one l e t t e r o r tw o l e t t e r , / / Find l o n g e s t p a l i n d r o m e public S t r i n g h e l p e r ( S t r i n g s , i n t begin , i n t end ) { while ( begin >= 0 && end <= s . len gth ( ) − 1 && s . charAt ( begin ) == s .
charAt( end) ) { begi n −−; end++; } return
s . subs tri ng ( begin +
1,
end) ;
}
37.4
m ana ch er ’ s algorithm
Manacher’s algorithm is much more complicated to figure out, even though it will bring benefit of time complexity of O(n). Since it is not typical, there is no need to waste time on that.
107
38 LEETCO DE SOLUTION - WORD BREAK
This is my solution for Word Break in Java. 38.1
t he p ro bl em
Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words. For example, given s = “leetcode”, dict = ["leet", "code"]. Return true because “leetcode” can be segmented as “leet code”. 38.2
naive approach
This problem can be solve by using a naive approach, which is trivial. A discussion can always start from that though. p ub li c c l a s s S o l ut i on { public boolean wordBreak( Stri ng s , r e t u r n wordBreakHelper ( s ,
Set di ct ) { dict , 0 ) ;
} public boolean wordBreakHelper ( Stri ng i f ( st ar t == s . length () ) r e t ur n t r u e ; for ( String a : i n t len = i n t end =
s , Set dict , i n t s t a r t ) {
dict ) { a . length () ; sta rt+len ;
/ / e nd i n d e x s h o u l d b e <= s t r i n g l e n g t h i f (end > s . lengt h () ) continue ; i f (s
. substr ing ( st art , st ar t+len ) . equals (a ) ) i f ( wordBreakHelper (s , dict , st ar t+len ) ) r e t u rn t r u e ;
}
108