-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 215 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 215 KB
1
{"pages":[],"posts":[{"title":"A Brief Introduction to Dynamic Programming","text":"Dynamic Programming is mainly an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of subproblems, so that we do not have to re-compute them when needed later. example 123456789101112131415161718192021// recursionpublic int fib(int n) { if(n<=1) return n; return fib(n-1)+fib(n-2);}// dynamic programmingpublic int fibDP(int n) { if (n>1) { int fib=0, p1=1,p2=0; for (int i=2;i<=n;i++) { fib = p1+p2; p2 = p1; p1 = fib; } return fib; } else if (n==1) { return 1; } else { return 0; }} Two pattern of solving DP problem: Tabulation: bottom up Memoization: top down Four steps to solve DP problems Determine the meaning of each DP element. It also determines the dimensions, the element type, and the size of the DP cache. Initialize DP cache. Initialization is based on a small number of known states, such as the first two Fibonacci numbers. Transition. We deduce the values of unknown DP elements from known states. For bottom-up DP, the transition process is usually based on loops. The order of loops depends on the transition equation. Return the result. It may require additional traversals on the DP cache.","link":"/2021/12/23/A-Brief-Introduction-to-Dynamic-Programming/"},{"title":"Coding Math","text":"Modular arithmetic$a\\equiv b\\space(\\text{mod}\\space n)$ means a mod n = b mod n. The parentheses mean that $(\\text{mod}\\space n)$ applies to the entire equation, not just to the right-hand side. Reflexivity, Symmetry and Transitivity: $a\\equiv a\\space(\\text{mod}\\space n)$ $a\\equiv b\\space(\\text{mod}\\space n) \\Leftrightarrow b\\equiv a\\space(\\text{mod}\\space n)$ If $a\\equiv b\\space(\\text{mod}\\space n)$ and $b\\equiv c\\space(\\text{mod}\\space n)$, then $a\\equiv c\\space(\\text{mod}\\space n)$ If $a_1\\equiv b_1\\space(\\text{mod}\\space n)$ and $a_2\\equiv b_2\\space(\\text{mod}\\space n)$, or if $a\\equiv b\\space(\\text{mod}\\space n)$, then: $a+k\\equiv b+k\\space(\\text{mod}\\space n)$, for any integer $k$ $ka\\equiv kb\\space(\\text{mod}\\space n)$, for any integer $k$ $a_1+a_2\\equiv b_1+b_2\\space(\\text{mod}\\space n)$ $a_1-a_2\\equiv b_1-b_2\\space(\\text{mod}\\space n)$ $a_1a_2\\equiv b_1b_2\\space(\\text{mod}\\space n)$ $a^k\\equiv b^k\\space(\\text{mod}\\space n)$, for any non-negative integer $k$ $p(a)\\equiv p(b)\\space(\\text{mod}\\space n)$, for any polynomial $p(x)$ with integer coefficients Compatibility with translation, scaling, addition, subtraction, multiplication, exponentiation and polynomial evaluation. Read exponential expressions2^16: two to the power of sixteen. 3^4: three to the power of four. x^2: x squared. x^3: x cubed.","link":"/2022/01/10/Coding-Math/"},{"title":"Common Algorithm Implementation Templates","text":"Traverse an array by groups123456for (int i = 0; i < arr.length; i += interval) { for (int j = i; j < i + interval && j < arr.length; j++) { // do something } // do something} Calculate the submatrix sums fastWe can use a 2D prefix sum array. Each row of the 2D prefix sum array is a 1D prefix sum array for the corresponding row in the original matrix. After initialization which takes $O(mn)$, calculating a submatrix sum takes $O(k)$, where k is the number of rows in the submatrix. 12345678// given matrix[m][n]int[][] prefixSum = new int[m][n];// initfor (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { // initialize each element in prefixSum[i] }}","link":"/2022/01/05/Common-Algorithm-Implementation-Templates/"},{"title":"Java Programming Tips","text":"Convert char to int12char c = '9';int i = Character.getNumericValue(c); 12char c = '9';int i = c - '0'; Add zeros at the beginning of a number12int i = 640;System.out.printf("%05d\\n", i); 100640 Print an array12int[] a = {1,2,3,4,5,6};System.out.println(Arrays.toString(a)); Fill an arrayUse Arrays.fill(array, val) to assign values to an array. 123int[] a = new int[10];Arrays.fill(a, 7);System.out.println(Arrays.toString(a)); 1[7, 7, 7, 7, 7, 7, 7, 7, 7, 7] Convert between string and char array123char[] arr = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};String s = new String(arr);char[] arr2 = s.toCharArray(); Convert integer to string123String s1 = String.valueOf(i);String s2 = Integer.toString(i, 10); // radix = 10String s3 = String.format("%d", i); Parse integer from stringDo not forget to catch the possible exception thrown by parseInt(). parseInt() ignores leading zeros. 12345try { Integer.parseInt(s, 10);} catch (Exception ignored) {} 1int i = Integer.parseInt("000015640", 10); // 15640 Default initialization of an array in JavaEverything in a Java program not explicitly set to something by the programmer, is initialized to a zero value. For references (anything that holds an object) that is null. For int/short/byte/long that is a 0. For float/double that is a 0.0 For booleans that is a false. For char that is the null character '\\u0000' (whose decimal equivalent is 0). Initialize the capacity of a List1List<Integer> list = new ArrayList<>(10); In this line of code, we initialize an array list with initial capacity of 10. Note that 10 is its capacity, not its size. So we still need to add elements one by one to this list. Assessing directly without adding any elements, such as directly calling list.get(1), will cause out-of-bound exceptions. Reverse a List1Collections.reverse(list); Create a List from an arrayTake the return of Array.asList() as the parameter of ArrayList constructor. 12int[] array = new int[10];ArrayList<Integer> arraylIST = new ArrayList<>(Arrays.asList(array)); Sort Primitive array: Arrays.sort(arr) Object array: Arrays.sort(integerArr, comparator) List: Collections.sort(list, comparator) 12345678910111213141516int[] arr = new int[]{4, 1, 5, 4, 4, 0, 6, 1, 9};Integer[] integerArr = new Integer[]{4, 1, 5, 4, 4, 0, 6, 1, 9};List<Integer> list = new ArrayList<Integer>(Arrays.asList(integerArr));Arrays.sort(arr);Arrays.sort(integerArr, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); }});Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); }}); Traverse a mapUse Map.Entry or HashMap.Entry. 1234HashMap<Integer, Integer> hashMap = new HashMap<>();for (HashMap.Entry<Integer, Integer> e:hashMap.entrySet()) { // do something} isLetter() and isAlphabetic()isLetter() and isAlphabetic() are two methods of Character. The difference between them is: isAlphabetic() checks UPPERCASE_LETTER && LOWERCASE_LETTER && TITLECASE_LETTER && MODIFIER_LETTER && OTHER_LETTER && LETTER_NUMBER. isLetter() checks UPPERCASE_LETTER && LOWERCASE_LETTER && TITLECASE_LETTER && MODIFIER_LETTER && OTHER_LETTER. The point is that isLetter() will return false given a letter number. For example, roman numeral five (the letter looks like “V”). Certainly, for the English language, the distinction makes no difference. Use Deque over Stack for LIFO stacksWe should use Deque rather than Stack for LIFO stacks. 12Deque<Integer> dstack = new ArrayDeque<>();Stack<Integer> sstack = new Stack<>(); // not recommended There are many reasons to prefer Deque: Deque is an interface but Stack is a class. So using Deque brings us more flexibility for future extension. Stack is synchronized but Deque is not thread-safe. In the case of no need to ensure thread safety, Deque is more efficient. Deque iterates elements from top to bottom. Stack iterates elements from bottom to top. Most important: with Stack, we can access/insert/remove arbitrary elements in the LIFO stack by indexes, stack.get(index); stack.add(index, e); stack.remove(index), which breaks the LIFO rule. Although Deque does not absolutely obey the LIFO rule, it can only access/insert/remove the first/last element in the LIFO stack. Print multi-dimensional arrays12System.out.println(Arrays.toString(arr)); // print a 1D arraySystem.out.println(Arrays.deepToString(marr)); // print a multi-dimensional array Get ceiling/floor value of a number12double ceilingValue = Math.ceil(3.1); // 4.0double floorValue = Math.floor(3.1); // 3.0 Note that these twoMath functions return double. Or if we need to get the ceiling value of a fraction X/Y, we can also do as the following: 1int res = (X + Y - 1) / Y; Sort an array/list Primitive array int[]: Arrays.sort Objective array Integer[]: Arrays.sort and optional comparator Objective list List<Integer>: Collections.sort and optional comparator 12345678910111213141516int[] arr = new int[]{4, 1, 5, 4, 4, 0, 6, 1, 9};Integer[] integerArr = new Integer[]{4, 1, 5, 4, 4, 0, 6, 1, 9};List<Integer> list = new ArrayList<Integer>(Arrays.asList(integerArr));Arrays.sort(arr);Arrays.sort(integerArr, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); }});Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); }}); Common time complexity calculation$$\\sum_{1}^{n}{k^2}=\\frac{1}{n}{n(n+1)(2n+1)}=O(n^3)\\\\sum_{1}^{n}{k^3}=(\\frac{1}{2}{n(n+1)})^2=O(n^4)$$ String manipulation1234StringBuilder builder1 = new StringBuilder("abcdefg");// if we want to delete c and add it backbuilder1.deleteCharAt(2); // "abdefg"builder1.insert(2, 'c'); // "abcdefg" toCharArray() and getBytes()toCharArray() is better if we are not dealing with characters outside of the Unicode BMP, which are encoded as two surrogate characters. getBytes() needs to specify encoding, which is prone to mistakes. Also, toCharArray() is faster because for Java 7 and newer, a String object contains a char array internally. toCharArray() just need to complete a copy operation. Integer object comparisonFor two Integer objects, == compares their references while < and > compare their values. To examinate whether they have the same value, we should use equals() or unbox them by intValue(). And, it is safe to use comparison operators (==, <, >) for comparing values between an int and an Integer object. 1234567Integer i1 = new Integer(15640);Integer i2 = new Integer(15640);Integer i3 = new Integer(15440);System.out.printf("i1 == i2 : %b\\n" + "i1.value == i2.value : %b\\n" + "i1.equals(i2) : %b\\n" + "i2 > i3 : %b\\n", i1 == i2, i1.intValue() == i2.intValue(), i1.equals(i2), i2 > i3); 1234i1 == i2 : falsei1.value == i2.value : truei1.equals(i2) : truei2 > i3 : true substring()substring(beginIndex, endIndex) returns the substring in [beginIndex, endIndex). And it is safe for beginIndex == endIndex == 0 or length. 123String s = "hello";System.out.println(s.substring(0, 0)); // return ""System.out.println(s.substring(s.length(), s.length())); // return "" indexOf()s.indexOf(pattern, fromIndex) returns the first index of given pattern from fromIndex (inclusive). If such pattern does not exist, it returns -1. abstract keywordIf a class is declared abstract, it means that this class may be partially implementd. Thus, Java does not allow us to instantiate an abstract class. An abstract class can implement its constructor, functions (abstract or non-abstract) and have its variables. An abstract function cannot have body. Any classes that declare any abstract functions must also be declared abstract. Any non-abstract classes that inherit abstract functions must implement all of them. 123456789101112131415161718192021222324252627282930313233343536static abstract class Node { int id; String name; public Node(int _id, String _name) { id = _id; name = _name; } public void printHello() { System.out.println("hello"); } // an abstract function cannot have body public abstract void printWorld();}static class ANode extends Node { public ANode(int _id, String _name) { super(_id, _name); } // the non-abstract subclass of an abstract class must implement all inherited abstract functions @Override public void printWorld() { System.out.println("world"); }}public static void main(String[] args) { // cannot instantiate an abstract class // Node n = new Node(); Node n = new ANode(1, "node1"); n.printHello(); n.printWorld();} Immutable objectsString and all primitive wrapper classes (Integer, Double, Long, Character, etc.) are immutable. Inner class and nested classAn instance of inner class can exist only within an instance of its outerClass. The instance of the inner class has direct access to the methods and fields of its outer instance. A nested class can access all static resources of its outer class. The difference between inner class and nested class is straightforward. Inner class has non-static features. Nested class has static features. 123456789101112131415161718192021222324252627282930313233343536public class TestInnerNested { class InnerClass { final String name = "inner"; public void print() { printHello(); } } static class NestedClass { NestedClass next; public void print() { System.out.println(id); } } static final String id = "15640"; private void printHello() { System.out.println("hello"); } public static void main(String[] args) { // an instance of InnerClass can exist only within an instance of OuterClass // the instance of the inner class has direct access to the methods and fields of its outer instance TestInnerNested test = new TestInnerNested(); TestInnerNested.InnerClass inner = test.new InnerClass(); inner.print(); // a nest class can access all static resources of its outer class TestInnerNested.NestedClass nested = new TestInnerNested.NestedClass(); nested.print(); }} Round a float number to n decimal placesUse DecimalFormat. 123DecimalFormat df = new DecimalFormat("#.####");df.setRoundingMode(RoundingMode.CEILING);System.out.println(df.format(15640.123456)); // 15640.1235 Traverse key-value pairs in a mapUse nested class Map.Entry<>. 12345Map<Integer, String> map = new HashMap<>();for (Map.Entry<Integer, String> entry : map.entrySet()) { int key = entry.getKey(); String value = entry.getValue();} assert keywordDo Not use assert in test. They can be activated at run-time by way of the -ea option on the java command, but are not turned on by default. Split a string into at most 2 pieces1String[] arr = str.split(" ", 2); Difference between Exception and RuntimeExceptionAny exception that derives from Exception is a checked exception. If a function throws an Exception in its body, it should also declare this behavior in this function declaration. Whereas a class that derives from RuntimeException is un-checked. RuntimeExceptions do not need to be explicitly handled by the calling code. Generally RuntimeExceptions are exceptions that can be prevented programmatically. floorEntry() and lowerEntry() of TreeMapfloorEntry(key) gets the entry corresponding to the specified key; if no such entry exists, returns the entry for the greatest key less than the specified key; if no such entry exists, returns null. lowerEntry(key) returns the entry for the greatest key less than the specified key; if no such entry exists (i.e., the least key in the Tree is greater than the specified key), returns null. Using a custom class as a keyTo use a custom class as a key in Java Map, the custom class must define its equals and hashCode methods. Though Java provides an implicit implementation of these two methods for every class, the default implementation may lead to unexpected behaviors of Map (such as failing to find an existing key when inquiring with a new object with the same fields). 123456789101112131415@Overridepublic boolean equals(Object obj) { if (obj == null) { return false; } if (obj.getClass() != this.getClass()) { return false; } // return ...}@Overridepublic int hashCode() { return Objects.hash(...);} ShiftSigned shift: <<, >> Unsigned shift: <<<, >>>","link":"/2021/12/22/Java-Programming-Tips/"},{"title":"Go Programming Tips","text":"Add 0x prefix when printing hex numbersUse %#x placeholder. 12345func TestPrintHex() { var i int = 0x123456 fmt.Printf("%x\\n", i) fmt.Printf("%#x\\n", i)} 121234560x123456 Print the type of a variable123fmt.Printf("%T\\n", i)fmt.Println(reflect.Typeof(i))fmt.Prinft("%s\\n", reflect.Typeof(i).String()) Get random numbersIntn() returns the next integer in the current random sequence. UnixNano() returns the number of nanoseconds elapsed since January 1, 1970 UTC. And the result of UnixNano() does not depend on the location associated with t. We only have to set the seed once. But before we set the seed, rand will use the default seed. For example, the first call of Intn(100) always returns 81 without setting the seed. 12rand.Seed(time.Now().UnixNano())fmt.Println(rand.Intn(100)) Temporary variables in if-else blockIn Go, we can define variables in if-else condition statement. The scope of these temporary variables is limited within if-else block. 12345if v, p := math.Sqrt(float64(sum)), math.Sin(float64(sum)); v < 10 && p < 0.5 { fmt.Println("True")} else { fmt.Println(v, p)} switchIn Go, the execution does not fall through the switch structure. Switch cases evaluate cases from top to bottom, stopping when a case succeeds. Also, if we want to take the same action on multiple cases, we can write case content1, content2:. In the following example, although without any break, the control flow jumps out of the switch block after executing the first case. 123456789var i int = 15640switch {case i > 1: fmt.Println(i)case i > 2: fmt.Println(i)case i > 99999: fmt.Println("false")} 115640 SliceSlices share the same underlying memory with the original array. The type of slice is []T. len() returns the number of elements a slice contains. cap() returns the number of elements in the underlying array, counting from the first element in the slice. append(slice, e1, e2, ...) overwrites the elements in the underlying array if slice has enough capacity to contain these elements. Otherwise, it returns a new allocated slice and keeps the underlying array intact. 123456789101112131415161718192021222324func TestSlice() { var arr [5]int = [5]int{0, 1, 2, 3, 4} slice1 := arr[0:4] slice2 := arr[1:4] fmt.Println(arr) fmt.Println(slice1) fmt.Println(slice2) fmt.Printf("type: %s, len: %d, cap: %d\\n", reflect.TypeOf(arr).String(), len(arr), cap(arr)) fmt.Printf("type: %s, len: %d, cap: %d\\n", reflect.TypeOf(slice1).String(), len(slice1), cap(slice1)) fmt.Printf("type: %s, len: %d, cap: %d\\n", reflect.TypeOf(slice2).String(), len(slice2), cap(slice2)) slice1[0] = 9 fmt.Println(arr) fmt.Println(slice1) fmt.Println(slice2, "\\n") // Test slice append slice3 := append(slice2, 8) // change the underlying array slice4 := append(slice2, 7, 6) // return a new allocated slice fmt.Println(arr, &arr[0], cap(arr)) fmt.Println(slice1, &slice1[0], cap(slice1)) fmt.Println(slice2, &slice2[0], cap(slice2)) fmt.Println(slice3, &slice3[0], cap(slice3)) fmt.Println(slice4, &slice4[0], cap(slice4))} 123456789101112131415[0 1 2 3 4][0 1 2 3][1 2 3]type: [5]int, len: 5, cap: 5type: []int, len: 4, cap: 5type: []int, len: 3, cap: 4[9 1 2 3 4][9 1 2 3][1 2 3] [9 1 2 3 8] 0xc000018270 5[9 1 2 3] 0xc000018270 5[1 2 3] 0xc000018278 4[1 2 3 8] 0xc000018278 4[1 2 3 7 6] 0xc00001e080 8 Variable Length ArrayGo only allows constants to be used as array size. To create an array whose size is defined by a variabl, we can use make([]T, length). It creates an underlying array with size of length and returns a slice of it. 12345678910111213const conLen = 8func TestVLA(length int) { var l int = 5 // illegal //var arr0 [len]int //var arr1 [l]int var arr2 = make([]int, l) var arr3 = make([]int, length) var arr4 [conLen]int // do something} String comparisonIn Go, we can compare strings by using comparison operators (==, <, >, etc) or by strings.Compare(s1, s2). File naming conventionsnake_case is the convention across the most of the standard library and most third party libraries for Go. deferdefer FunctionCall() defers the execution of FunctionCall() until the surrounding function (the parent function call of FunctionCall()) returns. Multiple deferred calls are stacked, so that they will be callled in a LIFO order. 123456func TestDefer() { for i:= 0; i < 5; i++ { defer fmt.Println("deferred call", i) } fmt.Println("ready to return")} 123456ready to returndeferred call 4deferred call 3deferred call 2deferred call 1deferred call 0 HashmapIn Go, we can declaring a hashmap by using key word map. map declares a non-thread-safe hashmap. Create a hash map: hashmap := make(map[string]int), string is key type and int is value type. Insert a key-value pair: hashmap[key] = value. Delete a key-value pair: delete(hashmap, key). Check whether a key exists: v, ok := hashmap[key]. If the key exists, ok == true. Otherwise, v equals to the zero value of the value type, ok == false. Traverse the hashmap: for k, v := range hashmap {// do someting} Difference between make and new make returns a value of T; new returns a value of *T. make returns an initialized value; new returns a zeroed value. make can only create and initialize slices, maps and channels; new can allocate zeroed memory for any types. Take hashmap as an example. Since we need to initialize structure of hashmap itself before using it (it means that we cannot just zero the hashmap memory), we have to create a hashmap by hashmap := make(map[string]int) or hashmap := map[string]int{}. Format go files recursively1go fmt path/... MethodIn Go, methods take either a value or a pointer as the receiver when they are called. Go interprets s.Print() as (&s).Print(), ps.SwapPrint() as (*ps).SwapPrint(). But this is just syntactic sugar. Also, we cannot define new methods for existing types in another package (including built-in types, such as int, map). But it is fine to define a normal function that takes a non-local-package type argument. Or we can define our own alias for that type in current package type Alias ExistingTypeName, or define a wrapper structure containing that type type NewStruct struct {n ExistingTypeName} 1234567891011121314151617181920212223type MyStruct struct { x int y int}func (ps *MyStruct) Print() { fmt.Printf("(%d, %d)\\n", ps.x, ps.y)}func (s MyStruct) SwapPrint() { fmt.Printf("(%d, %d)\\n", s.y, s.x)}func TestMethod() { ps := new(MyStruct) ps.x = 12 ps.y = 24 s := MyStruct{12, 24} ps.Print() ps.SwapPrint() s.Print() s.SwapPrint()} InterfaceAny concrete types that implement all methods of a interface type satisfy implicitly that interface type. In other words, a interface type specifies a set of methods that a concrete type must possess to be considered an instance of that interface. Note that T and *T are different when it comes to the relationship between interfaces and concrete types. And we cannot define methods for types under another package. 12345678910111213141516171819202122232425type IntTask inttype FloatTask float64type Runner interface { Run()}func (pit *IntTask) Run() { fmt.Printf("running task id = %d\\n", int(*pit))}func TestInterface() { var task Runner var pit *IntTask = new(IntTask) *pit = 1 var it IntTask = 2 fmt.Println(int(*pit)) fmt.Println(int(it)) task = pit // illegal //task = id task.Run()} Since only *ID implements Run() method, *ID satisfies interface Runnable but ID does not. 12312running task id = 1 Empty interfaceinterface{} is empty interface. Because it has no methods, all types satisfy the empty interface. Interface PitfallAn interface value with a nil dynamic value is NOT the same as a nil interface for any non-empty interfaces. If we assign nil to an empty interface variable, that variable is still equal to nil. If you want to keep the interface value consistent with its concrete value, please do not define a concrete value first then assign it to the interface value, but define the interface value directly (allocate a concrete value by new). 1234567891011func TestPitfall() { var pit *IntTask // zeroed as nil var run1 Runner = pit // warning: run1 is not nil var run2 Runner // zeroed as nil var run3 Runner = new(IntTask) var emptyInterface interface{} = nil fmt.Printf("run1 == nil ? %t\\n", run1 == nil) fmt.Printf("run2 == nil ? %t\\n", run2 == nil) fmt.Printf("run3 == nil ? %t\\n", run3 == nil) fmt.Printf("emptyInterface == nil ? %t\\n", emptyInterface == nil)} 1234run1 == nil ? falserun2 == nil ? truerun3 == nil ? falseemptyInterface == nil ? true Sortsort.Ints() and sort.Strings() only accept slice as argument. sort.Sort() provide a customizable method that requires the data type to be sorted should implement Len(), Swap() and Less(). 12var arr = [10]int{2, 3, 4, 6, 7, 9, 2, 3, 4, 5}sort.Ints(arr[:]) Type AssertionThe general form of type assertion is x.(T), while x is an interface to be tested (asserted), T can be a concrete type or another interface type. There are two usages of type assertion. T is a concrete type. The type assertion checks whether the concrete type of x is identical to T. T is an interface type. The type assertion checks whether the concrete type of x satisfies interface T. A successful type assertion returns the dynamic value in type T, and an optional assertion flag. A failed type assertion returns nil and flag false, if the statement only accepts one return value, it will cause a panic. 123456789101112131415161718192021func TestTypeAssert() { var itRunner Runner = new(IntTask) var ftRunner Runner = new(FloatTask) itRunner.Run() ftRunner.Run() // if without ok flag, such as pit2 := ftRunner.(*IntTask) // false assertions will cause panic pit, okInt := itRunner.(*IntTask) pit2, okInt2 := ftRunner.(*IntTask) pft, okFloat := ftRunner.(*FloatTask) pft2, okFloat2 := itRunner.(*FloatTask) fmt.Printf("assert itRunner to *IntTask: %t %T %p\\n", okInt, pit, pit) fmt.Printf("assert ftRunner to *IntTask: %t %T %p\\n", okInt2, pit2, pit2) fmt.Printf("assert ftRunner to *FloatTask: %t %T %p\\n", okFloat, pft, pft) fmt.Printf("assert itRunner to *FloatTask: %t %T %p\\n", okFloat2, pft2, pft2) *pit = 15640 *pft = 15.64 itRunner.Run() ftRunner.Run()} Type SwitchWe can use a type switch statement to replace an if-else chain of type assertions. 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748func TestTypeSwitch() { var i int = 15640 var j float64 = 15.64 var k string = "DS" TypeSwitch1(i) TypeSwitch1(j) TypeSwitch1(k) fmt.Println("") TypeSwitch2(i) TypeSwitch2(j) TypeSwitch2(k) fmt.Println("") TypeSwitch3(i) TypeSwitch3(j) TypeSwitch3(k) fmt.Println("")}func TypeSwitch1(x interface{}) { switch x := x.(type) { case int: fmt.Printf("case int: value = %d, type = %T\\n", x, x) case float64: fmt.Printf("case float64: value = %f, type = %T\\n", x, x) default: fmt.Printf("Unexpected Type\\n") }}func TypeSwitch2(x interface{}) { switch x.(type) { case int: fmt.Printf("case int: value = %d, type = %T\\n", x, x) case float64: fmt.Printf("case float64: value = %f, type = %T\\n", x, x) default: fmt.Printf("Unexpected Type\\n") }}func TypeSwitch3(x interface{}) { switch x := x.(type) { case int, float64: fmt.Printf("Valid Type: %T\\n", x) default: fmt.Printf("Unexpected Type\\n") }} 1234567891011case int: value = 15640, type = intcase float64: value = 15.640000, type = float64Unexpected Typecase int: value = 15640, type = intcase float64: value = 15.640000, type = float64Unexpected TypeValid Type: intValid Type: float64Unexpected Type map, channel and reference in GoFirst of all, there is NO strict reference (alias) in Go, which means two variables refer (not point) to the same memeory location. In Go, everything is passed by value. But we can see that in Go map and channel can be passed by reference in function. This is because map and channel are pointers naturally, while do not look like pointers. For example, a map variable is actually a pointer to a runtime.hmap structure. By the way, channels act as FIFO queues. Closed and drained channelRead requests (not matter how many times) to a closed and drained channel always immediately returns a zero value. But we can examine receives on a closed and drained channel by using optional flag. 1234567891011func TestChannel() { channel := make(chan int, 10) close(channel) for { x, ok := <- channel if !ok { break } fmt.Println(x) }} Or we can read elements from a channel by range, which will terminate the loop after reading the last element in a closed channel. 12345678910func TestChannel() { channel := make(chan int, 10) channel <- 1 channel <- 2 channel <- 3 close(channel) for x := range channel { fmt.Println(x) }} Unidirectional channel typeschan<- T: send-only channel; <-chan T: receive-only channel. They can be used in function arguments to restrict the opertions to channels. Conversions from bidirectional channel to unidirectional channel types are permitted in any assignment. But there is no going back. nil channelSend and receive operations on a nil channel block forever. selectIn select, each case specifies a communication (a send or receive operation on some channel) and an associated block of statements. If there is only one runnable case, execute that case. If there is multiple runnable cases, randomly execute one of them. If there is no runnable cases, block. The default case in a select is run if no other case is ready. We can use select and a closed channel to terminate goroutines politely. Terminate a goroutineFirst, there is no way for one goroutine to terminate another directly. If we want to terminate a goroutine, we should send a “signal” to that goroutine and let it handles the signal by executing return. This signal can be implemented by a closed and drained channel. Because each receive operations to a closed and drained channel always immediately returns a zero value. It sounds great, right? But the problem is that if a goroutine is blocked, it cannot notice the signal at the same time. Thus, we need to ensure two things: If this routine may be blocked by other methods (listener.Accept(), etc), unblock these methods in Close(), for example, by listener.Close(). If this routine may be blocked by channel operations, always wrap every channel operation with a select-done structure. In other words, every channel operation should have a done case in parallel. 1234567891011121314151617181920212223242526272829var done = make(chan bool) // always emptyvar workChan = make(chan int, 10) // a buffered channel storing datafunc Close() { // broadcast termination signal close(done)}func sampleRoutine() { // ... select { case workChan <- data: // do some non-blocking tasks case <-done: return } // ...}func sampleRoutine2() { // ... select { default: // do some non-blocking tasks case <-done: return } // ...} If we use for range to receive data from a channel in a goroutine function, which makes it hard to receive the termination signal, it can be rewritten by for{} and select. 123456789101112131415161718192021func sampleForRange() { // terminate loop automatically when workChan is closed and drained // cannot receive for res := range workChan { // ... }}func sampleForSelect() { for { select { case res, ok := <-workChan: if !ok { // workChan is closed and drained return } // ... case <-done: return } }} An ingenious example of decoupling on channel operation12345678910111213141516171819202122// work routine pulls integers from input channel// squares them and pushes results to output channelfunc (sq *SquarerImpl) work() { var toPush int dummy := make(chan int) pushOn := dummy pullOn := sq.input for { select { case unsquared := <-pullOn: toPush = unsquared * unsquared pushOn = sq.output pullOn = nil case pushOn <- toPush: pushOn = dummy pullOn = sq.input case <-sq.close: sq.closed <- true return } }} The constant generator iotaIn a const declaration, the value of iota begins at zero and increments by one for each item in the sequence. 1234567891011121314151617181920const ( zero = iota // 0 one // 1 two // 2 three // 3 four // 4)const ( first = 1 + iota // 1 second // 2 third // 3)const ( _ = 1 << (10 * iota) KB // 1024 MB // 1024 * 1024 GB // 1024 * 1024 * 1024) Asynchronous Request Implementation12345type Request struct { reqType ReqType retChan chan interface{} reqBody []interface{}} Remove the first element of a sliceIt also applies on slices with length of 1. 1slice = slice[1:] json.Marshaljson.Marshal(v) takes a memory object (argument type interface{}) and return its json encoding. In network programming, it can conveniently convert a user-defined object into json payload of the packet. Flexible array members in a structureGo allows us to define flexible array members (slices) in structures. Different from C, we can define any number of slices at any places in a structure. 123456789101112131415type Struct1 struct { i int body []interface{}}type Struct2 struct { body []interface{}}type Struct3 struct { i int body1 []interface{} body2 []interface{} j int} Pop from a slices = s[1:]. It removes the first element of s. Note that when using this syntax, the length of s can be 1 or greater but cannot be 0. 12345678func TestSlicePop() { s := make([]int, 1) s[0] = 16540 s = s[1:] // remove the first element fmt.Println(len(s)) // print 0 s1 := make([]int, 0) s1 = s1[1:] // ERROR!} Delete from a mapDeleting a non-existing key from a map through delete() makes no effect. 12345678func TestMapDelete() { hashmap := make(map[int]int) hashmap[1] = 1 delete(hashmap, 0) for k, v := range hashmap { fmt.Printf("[%d, %d]\\n", k, v) }} 1[1, 1] Constants in GoGo does NOT support constant members in structures or constant structures. Implement timing events by tickerWe can create a ticker through time.NewTicker(interval), which generates ticks in its channel ticker.C at specified time intervals (obviously, the size of channel C is one. We do not want ticks to be accumulated). We can place the handling functions of timing events and other events in a for-select block, so that timing events will not affect the execution of normal events. Note that Stop() will stop sending ticks to the channel C but will not close it, to prevent a closed drained channel issue. 123456789101112func TestTicker() { ticker := time.NewTicker(10 * time.Second) //ticker.Stop() for { select { case t := <-ticker.C: // timing event fmt.Println(t) // case e := <-normalEventChan: } }} Implicit type conversions and implicit numeric conversionsGo does not allow implicit type conversions, and does not allow implicit numeric conversions except numeric conversions across constants. 12345678910111213141516171819202122232425262728func TestType() { var i int = 10 var j int64 = 10 var k int = 10 // 1/10 == 0 => 0.0 var p float64 = 1 / 10 // 1.0/10 => 1.0/10.0 => 0.1 var q float64 = 1.0 / 10 // illegal // var m float64 = 1.0 / i _ = j // to keep compiler happy // mismatched types // j = i // mismatched types /* if i == j { fmt.Println("i == j") } */ if i == k { fmt.Println("i == k") } fmt.Println(p) fmt.Println(q) // Go does not support implicit numeric conversion // p = i / k} 123i == k00.1","link":"/2021/12/22/Go-Programming-Tips/"},{"title":"LeetCode 1. Two Sum","text":"QuestionGiven an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would have *exactly* one solution, and you may not use the same element twice. You can return the answer in any order. Example 1: 123Input: nums = [2,7,11,15], target = 9Output: [0,1]Explanation: Because nums[0] + nums[1] == 9, we return [0, 1]. Example 2: 12Input: nums = [3,2,4], target = 6Output: [1,2] Example 3: 12Input: nums = [3,3], target = 6Output: [0,1] Constraints: 2 <= nums.length <= 104 -109 <= nums[i] <= 109 -109 <= target <= 109 Only one valid answer exists. Source: https://leetcode.com/problems/two-sum/ Solution123456789101112131415public int[] twoSum(int[] nums, int target) { // nums[i] -> i Map<Integer, Integer> map = new HashMap<>(); // fix the order within each pair as (left, right), so that avoid duplicates for (int right = 0; right < nums.length; right++) { int n = nums[right]; if (map.containsKey(target - n)) { return new int[]{map.get(target - n), right}; } else { map.put(n, right); } } // cannot find such pair return null;}","link":"/2022/03/10/LeetCode-1-Two-Sum/"},{"title":"LeetCode 102. Binary Tree Level Order Traversal","text":"QuestionGiven the root of a binary tree, return the level order traversal of its nodes’ values. (i.e., from left to right, level by level). Example 1: 12Input: root = [3,9,20,null,null,15,7]Output: [[3],[9,20],[15,7]] Example 2: 12Input: root = [1]Output: [[1]] Example 3: 12Input: root = []Output: [] Constraints: The number of nodes in the tree is in the range [0, 2000]. -1000 <= Node.val <= 1000 Source: https://leetcode.com/problems/binary-tree-level-order-traversal/ SolutionUse queue size in BFS to implement layer-ordre traversal of a tree. 123456789101112131415161718192021222324252627282930313233343536373839404142class TreeNode { int val; TreeNode left; TreeNode right; TreeNode() { } TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; }}public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> result = new ArrayList<>(); Queue<TreeNode> queue = new LinkedList<>(); if (root != null) { queue.add(root); } while (!queue.isEmpty()) { List<Integer> currLevel = new ArrayList<>(); int numThisLevel = queue.size(); for (int i = 0; i < numThisLevel; i++) { TreeNode curr = queue.poll(); currLevel.add(curr.val); if (curr.left != null) { queue.add(curr.left); } if (curr.right != null) { queue.add(curr.right); } } result.add(currLevel); } return result;}","link":"/2022/01/25/LeetCode-102-Binary-Tree-Level-Order-Traversal/"},{"title":"LeetCode 1044. Longest Duplicate Substring","text":"QuestionGiven a string s, consider all duplicated substrings: (contiguous) substrings of s that occur 2 or more times. The occurrences may overlap. Return any duplicated substring that has the longest possible length. If s does not have a duplicated substring, the answer is "". Example 1: 12Input: s = "banana"Output: "ana" Example 2: 12Input: s = "abcd"Output: "" Constraints: 2 <= s.length <= 3 * 104 s consists of lowercase English letters. Source: https://leetcode.com/problems/longest-duplicate-substring/ SolutionOne intuitive idea is 2D DP, which has a O(n^2) time complexity. Finding duplicate substring in one string can be regarded as a variant of the problem of finding common substrings between two strings, like LeetCode 718. Maximum Length of Repeated Subarray. How to solve this problem faster? The key point is how to determine whether two substring are identical. To prove that two substring are identical, we have to compare their characters one by one. However, we do not have to do so to be sure that two substring are not identical. Instead, if the hash values of two substrings are not the same, these two substrings must be different. Computing the hash value of each substring is still time consuming. We use rolling hash function, which treats character in a string as digits and takes the numeric value of these digits as the hash value. Thus, for a new string that only changes a few characters (in this problem, the first and the last), rolling hash function can compute the new hash value quickly. In the rolling hash function, we use a large prime number 10^9+7 to avoid overflow. A long duplicate string must contain shorter duplicate strings. Also, we only care about the longest duplicate substring. We can use binary search to find the largest length of duplicate substring. 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081/** * @param L length of duplicate strings * @param base base of numeric strings * @param N length of the original string * @param nums numeric array of the original string * @return the start index of a duplicate string, if not exist, return -1 */private int searchDuplicateStrings(int L, int base, long modulus, int N, int[] nums) { long hash = 0; // hash code long highest = 1; // base^(L-1) for (int i = 0; i < L; i++) { hash = (hash * base + nums[i]) % modulus; } for (int i = 1; i < L; i++) { highest = (highest * base) % modulus; } // hash code -> a list of start indexes Map<Long, List<Integer>> map = new HashMap<>(); // add the first substring map.put(hash, new ArrayList<>()); map.get(hash).add(0); for (int left = 1; left <= N - L; left++) { // update hash value while avoiding overflow hash = (hash - nums[left - 1] * highest % modulus + modulus) % modulus; hash = (hash * base + nums[left + L - 1]) % modulus; if (map.containsKey(hash)) { List<Integer> starts = map.get(hash); // compare substrings with the same hash code for (int start : starts) { boolean isSame = true; for (int i = 0; i < L; i++) { if (nums[i + left] != nums[i + start]) { isSame = false; break; } } if (isSame) { return left; } } starts.add(left); } else { map.put(hash, new ArrayList<>()); map.get(hash).add(left); } } return -1;}// average O(nlog(n))public String longestDupSubstring(String s) { int sLen = s.length(); int[] nums = new int[sLen]; // modulus for rolling hash function // a large prime number that fits into 32-bit integer final long modulus = (long) Math.pow(10, 9) + 7; // base of numeric strings final int a = 26; for (int i = 0; i < sLen; i++) { nums[i] = s.charAt(i) - 'a'; } // binary search int low = 1, high = sLen; int resultLen = 0, resultIndex = -1; while (low <= high) { int mid = low + (high - low) / 2; int index = searchDuplicateStrings(mid, a, modulus, sLen, nums); if (index == -1) { // no duplicate substrings with length of mid high = mid - 1; } else { // duplicate substrings with length of mid exist resultIndex = index; resultLen = mid; low = mid + 1; } } return resultLen == 0 ? "" : s.substring(resultIndex, resultIndex + resultLen);}","link":"/2022/01/10/LeetCode-1044-Longest-Duplicate-Substring/"},{"title":"LeetCode 1048. Longest String Chain","text":"QuestionYou are given an array of words where each word consists of lowercase English letters. wordA is a predecessor of wordB if and only if we can insert exactly one letter anywhere in wordA without changing the order of the other characters to make it equal to wordB. For example, "abc" is a predecessor of "abac", while "cba" is not a predecessor of "bcad". A word chain is a sequence of words [word1, word2, ..., wordk] with k >= 1, where word1 is a predecessor of word2, word2 is a predecessor of word3, and so on. A single word is trivially a word chain with k == 1. Return the length of the longest possible word chain with words chosen from the given list of words. Example 1: 123Input: words = ["a","b","ba","bca","bda","bdca"]Output: 4Explanation: One of the longest word chains is ["a","ba","bda","bdca"]. Example 2: 123Input: words = ["xbc","pcxbcf","xb","cxbc","pcxbc"]Output: 5Explanation: All the words can be put in a word chain ["xb", "xbc", "cxbc", "pcxbc", "pcxbcf"]. Example 3: 1234Input: words = ["abcd","dbqca"]Output: 1Explanation: The trivial word chain ["abcd"] is one of the longest word chains.["abcd","dbqca"] is not a valid word chain because the ordering of the letters is changed. Constraints: 1 <= words.length <= 1000 1 <= words[i].length <= 16 words[i] only consists of lowercase English letters. Source: https://leetcode.com/problems/longest-string-chain/ SolutionBottom-Up DP For words, the length of longest string chain that ends with word equals $max(pre_0(word),pre_1(word),…,pre_{l-1}(word))+1$ 1234567891011121314151617181920212223242526272829303132public int longestStrChain(String[] words) { // parameter validation if (words == null || words.length < 1) { return 0; } // s -> max length of string chain ending with s Map<String, Integer> cache = new HashMap<>(); // sort words by length of each word Arrays.sort(words, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } }); int len = words.length; int maxLen = 0; for (int i = 0; i < len; i++) { int currLen = 1; StringBuilder temp = new StringBuilder(words[i]); for (int k = 0; k < words[i].length(); k++) { char c = temp.charAt(k); temp.deleteCharAt(k); String predecessor = temp.toString(); int prevLen = cache.getOrDefault(predecessor, 0); currLen = Math.max(currLen, prevLen + 1); temp.insert(k, c); // restore the change } cache.put(words[i], currLen); maxLen = Math.max(maxLen, currLen); } return maxLen;}","link":"/2021/12/30/LeetCode-1048-Longest-String-Chain/"},{"title":"LeetCode 121. Best Time to Buy and Sell Stock","text":"QuestionYou are given an array prices where prices[i] is the price of a given stock on the ith day. You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock. Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0. Example 1: 1234Input: prices = [7,1,5,3,6,4]Output: 5Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell. Example 2: 123Input: prices = [7,6,4,3,1]Output: 0Explanation: In this case, no transactions are done and the max profit = 0. Constraints: 1 <= prices.length <= 105 0 <= prices[i] <= 104 Source: https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ SolutionThe best time to buy and sell can be interpreted as two forms: the lowest price we have ever seen to buy + current price to sell current price to buy + the highest price in the future to sell To solve these problems of a flexible window, one key point is to get a reliable value of one end first and traverse the other end. In the following implementation, we choose the first form so we traverse the array from the left to the right. 1234567891011121314public int maxProfit(int[] prices) { if (prices == null || prices.length == 0) { return 0; } int maxProfit = 0; // min price that has been seen int minPrice = Integer.MAX_VALUE; for (int price : prices) { minPrice = Math.min(minPrice, price); // the max profit if selling the stock on the ith day maxProfit = Math.max(maxProfit, price - minPrice); } return maxProfit;}","link":"/2022/01/18/LeetCode-121-Best-Time-to-Buy-and-Sell-Stock/"},{"title":"LeetCode 123. Best Time to Buy and Sell Stock III","text":"QuestionYou are given an array prices where prices[i] is the price of a given stock on the ith day. Find the maximum profit you can achieve. You may complete at most two transactions. Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again). Example 1: 1234Input: prices = [3,3,5,0,0,3,1,4]Output: 6Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3. Example 2: 1234Input: prices = [1,2,3,4,5]Output: 4Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again. Example 3: 123Input: prices = [7,6,4,3,1]Output: 0Explanation: In this case, no transaction is done, i.e. max profit = 0. Constraints: 1 <= prices.length <= 105 0 <= prices[i] <= 105 Source: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ SolutionleftProfits[i] means the max profit of all transactions in prices[:i] (they can sell on day i or earlier). Similar for rightProfits[i]. The overlap point of day i for leftProfits[i] and rightProfits[i] is necessary. Because the question requires a result with at most two transactions. The overlap point can help two transactions to be merged into one. We find seen min price and max price from two different ends to fill two cache arrays. 1234567891011121314151617181920212223242526public int maxProfit(int[] prices) { if (prices == null || prices.length <= 1) { return 0; } int len = prices.length; // leftProfits[i] is the max profit of the first transaction in prices[:i] (inclusive) // rightProfits[i] is the max profit of the second transaction in prices[i:] (inclusive) // when two transactions overlap on i, they can be merged into one int[] leftProfits = new int[len]; int[] rightProfits = new int[len]; int leftMin = prices[0]; int rightMax = prices[len - 1]; // k is the distance to the end, to merge two loops into one for (int k = 1; k < len; k++) { int l = k, r = len - 1 - k; leftMin = Math.min(leftMin, prices[l]); rightMax = Math.max(rightMax, prices[r]); leftProfits[l] = Math.max(leftProfits[l - 1], prices[l] - leftMin); rightProfits[r] = Math.max(rightProfits[r + 1], rightMax - prices[r]); } int maxProfit = 0; for (int i = 0; i < len; i++) { maxProfit = Math.max(maxProfit, leftProfits[i] + rightProfits[i]); } return maxProfit;}","link":"/2022/01/18/LeetCode-123-Best-Time-to-Buy-and-Sell-Stock-III/"},{"title":"LeetCode 1235. Maximum Profit in Job Scheduling","text":"QuestionWe have n jobs, where every job is scheduled to be done from startTime[i] to endTime[i], obtaining a profit of profit[i]. You’re given the startTime, endTime and profit arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range. If you choose a job that ends at time X you will be able to start another job that starts at time X. Example 1: 1234Input: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]Output: 120Explanation: The subset chosen is the first and fourth job. Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70. Example 2: 1234Input: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]Output: 150Explanation: The subset chosen is the first, fourth and fifth job. Profit obtained 150 = 20 + 70 + 60. Example 3: 12Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]Output: 6 Constraints: 1 <= startTime.length == endTime.length == profit.length <= 5 * 104 1 <= startTime[i] < endTime[i] <= 109 1 <= profit[i] <= 104 Source: https://leetcode.com/problems/maximum-profit-in-job-scheduling/ SolutionFirst, jobs are obviously a kind of interval. Each job has start and end. So as a common operation for solving interval problems, we need to sort intervals. And in this problem, we care more about the end time of jobs because the end time limits the selection of next job. Thus, we choose to sort jobs by end time. Then, this problem is also a scheduling problem. If we try to enumerate all possible schedules, the time complexity will be unacceptably high. So, natually we think of Dynamic Programming, which caches the result of subproblems. Each DP element represents the max profit when that schedule ends at time e. The state transition equation is DP[new_end] = curr_job.profit + DP[end_just_before_curr_start] . Thus, we need to search a previous DP element whose end is before the start of current job. Binary search is much faster than linear search, that is one reason why we use TreeMap. Also, we use greedy thought in following solution. According to the state transition equation, a new schedule with higher end has its meaning only if its profit is also higher. If not, it makes no good but intervene the binary search. So schedules is monotonic. If one entry in schedules has a higher end than another entry, it must has a higher profit. 123456789101112131415161718192021222324252627282930313233343536373839404142434445static class Job { public int start; public int end; public int profit; public Job(int s, int e, int p) { this.start = s; this.end = e; this.profit = p; }}// Time Complexity: O(n*log(n))// the sort process costs O(n*log(n)), each search in TreeMap costs O(log(n))public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { List<Job> jobs = new ArrayList<>(); for (int i = 0; i < startTime.length; i++) { jobs.add(new Job(startTime[i], endTime[i], profit[i])); } // sort by the end time, ascending Collections.sort(jobs, new Comparator<Job>() { @Override public int compare(Job o1, Job o2) { return o1.end - o2.end; } }); // key-value <e, p> means before end time e, the max profit we can get is p // we deduce kv pairs from low end to high end by DP // like a "non-fixed-size" DP // it has 3 features: binary search, DP, monotonic TreeMap<Integer, Integer> schedules = new TreeMap<>(); schedules.put(0, 0); // DP, enumerate each schedule ending with each job for (Job job : jobs) { int currProfit = schedules.floorEntry(job.start).getValue() + job.profit; // during traversal, the end of job becomes higher and higher // greedy thought, a new schedule with higher end has its meaning only if its profit is also higher // compare with the last kv value, because we sort jobs by end time if (currProfit > schedules.lastEntry().getValue()) { schedules.put(job.end, currProfit); } } return schedules.lastEntry().getValue();}","link":"/2022/03/11/LeetCode-1235-Maximum-Profit-in-Job-Scheduling/"},{"title":"LeetCode 1306. Jump Game III","text":"QuestionGiven an array of non-negative integers arr, you are initially positioned at start index of the array. When you are at index i, you can jump to i + arr[i] or i - arr[i], check if you can reach to any index with value 0. Notice that you can not jump outside of the array at any time. Example 1: 123456Input: arr = [4,2,3,0,3,1,2], start = 5Output: trueExplanation: All possible ways to reach at index 3 with value 0 are: index 5 -> index 4 -> index 1 -> index 3 index 5 -> index 6 -> index 4 -> index 1 -> index 3 Example 2: 12345Input: arr = [4,2,3,0,3,1,2], start = 0Output: true Explanation: One possible way to reach at index 3 with value 0 is: index 0 -> index 4 -> index 1 -> index 3 Example 3: 123Input: arr = [3,0,2,1,2], start = 2Output: falseExplanation: There is no way to reach at index 1 with value 0. Constraints: 1 <= arr.length <= 5 * 104 0 <= arr[i] < arr.length 0 <= start < arr.length Source: https://leetcode.com/problems/jump-game-iii/ SolutionFor jump games, if one path reaches a visited point, we can terminate that path (pruning). Because it can be replaced by a previous path. 123456789101112131415161718192021222324252627282930313233343536373839404142434445// BFS, Time Complexity O(n)public boolean canReach(int[] arr, int start) { int len = arr.length; boolean[] visited = new boolean[len]; // initialized with false Queue<Integer> queue = new LinkedList<>(); // store indexes queue.add(start); while (!queue.isEmpty()) { int currIndex = queue.poll(); // if current path includes a visited index, it can be replaced if (visited[currIndex]) { continue; } if (arr[currIndex] == 0) { return true; } visited[currIndex] = true; if (currIndex + arr[currIndex] < len) { queue.add(currIndex + arr[currIndex]); } if (currIndex - arr[currIndex] >= 0) { queue.add(currIndex - arr[currIndex]); } } return false;}private boolean dfs(int[] arr, boolean[] visited, int start, int len) { if (start < 0 || start >= len || visited[start]) { return false; } if (arr[start] == 0) { return true; } visited[start] = true; return dfs(arr, visited, start - arr[start], len) || dfs(arr, visited, start + arr[start], len);}// DFS, Time Complexity O(n)public boolean canReach2(int[] arr, int start) { int len = arr.length; boolean[] visited = new boolean[len]; // initialized with false return dfs(arr, visited, start, len);}","link":"/2022/01/25/LeetCode-1306-Jump-Game-III/"},{"title":"LeetCode 1335. Minimum Difficulty of a Job Schedule","text":"QuestionYou want to schedule a list of jobs in d days. Jobs are dependent (i.e To work on the ith job, you have to finish all the jobs j where 0 <= j < i). You have to finish at least one task every day. The difficulty of a job schedule is the sum of difficulties of each day of the d days. The difficulty of a day is the maximum difficulty of a job done on that day. You are given an integer array jobDifficulty and an integer d. The difficulty of the ith job is jobDifficulty[i]. Return the minimum difficulty of a job schedule. If you cannot find a schedule for the jobs return -1. Example 1: 12345Input: jobDifficulty = [6,5,4,3,2,1], d = 2Output: 7Explanation: First day you can finish the first 5 jobs, total difficulty = 6.Second day you can finish the last job, total difficulty = 1.The difficulty of the schedule = 6 + 1 = 7 Example 2: 123Input: jobDifficulty = [9,9,9], d = 4Output: -1Explanation: If you finish a job per day you will still have a free day. you cannot find a schedule for the given jobs. Example 3: 123Input: jobDifficulty = [1,1,1], d = 3Output: 3Explanation: The schedule is one job per day. total difficulty will be 3. Constraints: 1 <= jobDifficulty.length <= 300 0 <= jobDifficulty[i] <= 1000 1 <= d <= 10 Source: https://leetcode.com/problems/minimum-difficulty-of-a-job-schedule/ SolutionIn this problem, jobs are dependent so we can only complete jobs sequentially. For a day, we only need to consider the start job index m and the end job index n, which means on that day we complete all jobs between job m and job n (inclusive). cache[i][j] represents the minimum sum of difficulties when completing the first j+1 jobs in i+1 days. In the following code block, we traverse the start job index from high to low. It gets the max difficulty of jobs on that day more efficiently. 1234for (int k = j; k >= i; k--) { dayMax = Math.max(dayMax, jobDifficulty[k]); cache[i][j] = Math.min(cache[i][j], cache[i - 1][k - 1] + dayMax);} 12345678910111213141516171819202122232425262728// bottom-up 2D DP, time complexity O(nnd), space complexity O(nd)public int minDifficulty(int[] jobDifficulty, int d) { // parameter validation if (d < 1 || jobDifficulty == null || jobDifficulty.length < 1 || jobDifficulty.length < d) { return -1; } int n = jobDifficulty.length; // number of jobs // cache for DP // cache[i][j] represents the min sum of difficulties when completing jobs up to j job on i day int[][] cache = new int[d][n]; cache[0][0] = jobDifficulty[0]; for (int j = 1; j < n; j++) { cache[0][j] = Math.max(cache[0][j - 1], jobDifficulty[j]); } for (int i = 1; i < d; i++) { // only use a half of the cache because we must complete at least one job per day for (int j = i; j < n; j++) { cache[i][j] = Integer.MAX_VALUE; int dayMax = Integer.MIN_VALUE; // k is the index of start job on i day for (int k = j; k >= i; k--) { dayMax = Math.max(dayMax, jobDifficulty[k]); cache[i][j] = Math.min(cache[i][j], cache[i - 1][k - 1] + dayMax); } } } return cache[d - 1][n - 1];}","link":"/2022/01/08/LeetCode-1335-Minimum-Difficulty-of-a-Job-Schedule/"},{"title":"LeetCode 134. Gas Station","text":"QuestionThere are n gas stations along a circular route, where the amount of gas at the ith station is gas[i]. You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from the ith station to its next (i + 1)th station. You begin the journey with an empty tank at one of the gas stations. Given two integer arrays gas and cost, return the starting gas station’s index if you can travel around the circuit once in the clockwise direction, otherwise return -1. If there exists a solution, it is guaranteed to be unique Example 1: 12345678910Input: gas = [1,2,3,4,5], cost = [3,4,5,1,2]Output: 3Explanation:Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4Travel to station 4. Your tank = 4 - 1 + 5 = 8Travel to station 0. Your tank = 8 - 2 + 1 = 7Travel to station 1. Your tank = 7 - 3 + 2 = 6Travel to station 2. Your tank = 6 - 4 + 3 = 5Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3.Therefore, return 3 as the starting index. Example 2: 123456789Input: gas = [2,3,4], cost = [3,4,3]Output: -1Explanation:You can't start at station 0 or 1, as there is not enough gas to travel to the next station.Let's start at station 2 and fill up with 4 unit of gas. Your tank = 0 + 4 = 4Travel to station 0. Your tank = 4 - 3 + 2 = 3Travel to station 1. Your tank = 3 - 3 + 3 = 3You cannot travel back to station 2, as it requires 4 unit of gas but you only have 3.Therefore, you can't travel around the circuit once no matter where you start. Constraints: gas.length == n cost.length == n 1 <= n <= 105 0 <= gas[i], cost[i] <= 104 Source: https://leetcode.com/problems/gas-station/ SolutionWe can abstract this problem as following: Given a delta array int[] delta, find a routine that sequentially traverses the array and makes the sum of $\\delta$ always above or equal to zero during traversal, and return the start index of this routine. If there is no such routine, return -1. There are two critical properities, we will prove and explain them later: If $\\sum_{i=0}^{n-1}{\\delta_i} \\ge0$, there must be at least one valid routine. If $\\sum_{i=p}^{q}{\\delta_i} <0$ and $\\delta_p \\ge0$, any indice between $p$ and $q$ (inclusive) are not the start index of a valid routine. According to these two properties, we just need to traverse the array twice, one for checking whether a solution exists, the other for finding the first solution. Proof for the first property (Mathematical induction): Proof for the second property (better to understand by intuition): Note that just from the sum of the whole delta array, we cannot determine the number of solutions. Multiple solutions may exist when the sum is zero or greater than zero. For example, [1, 2, 3, 4] and [1, -1, 1, -1]. These two properties can also be used on many other situations. 1234567891011121314151617181920212223// two-pass, can be merged into one passpublic int canCompleteCircuit(int[] gas, int[] cost) { int len = gas.length; int totalTank = 0; // check if there is a solution for (int i = 0; i < len; i++) { totalTank += gas[i] - cost[i]; } if (totalTank < 0) { return -1; } // if solution exists, find a solution int currTank = 0; int startIndex = 0; for (int i = 0; i < len; i++) { currTank += gas[i] - cost[i]; if (currTank < 0) { // set i+1 as the start station and reset tank to zero startIndex = i + 1; currTank = 0; } } return startIndex;}","link":"/2021/12/29/LeetCode-134-Gas-Station/"},{"title":"LeetCode 1345. Jump Game IV","text":"QuestionGiven an array of integers arr, you are initially positioned at the first index of the array. In one step you can jump from index i to index: i + 1 where: i + 1 < arr.length. i - 1 where: i - 1 >= 0. j where: arr[i] == arr[j] and i != j. Return the minimum number of steps to reach the last index of the array. Notice that you can not jump outside of the array at any time. Example 1: 123Input: arr = [100,-23,-23,404,100,23,23,23,3,404]Output: 3Explanation: You need three jumps from index 0 --> 4 --> 3 --> 9. Note that index 9 is the last index of the array. Example 2: 123Input: arr = [7]Output: 0Explanation: Start index is the last index. You do not need to jump. Example 3: 123Input: arr = [7,6,9,6,9,6,9,7]Output: 1Explanation: You can jump directly from index 0 to index 7 which is last index of the array. Constraints: 1 <= arr.length <= 5 * 104 -108 <= arr[i] <= 108 Source: https://leetcode.com/problems/jump-game-iv/ SolutionThe idea of using queue size is to achieve layer-order traversal, like LeetCode 102. Binary Tree Level Order Traversal. So we can record the length of current search paths easily. Repeated visits to nodes with the same value are not helpful in getting the min number of steps to the end. Thus, we can prune the BFS tree. Note that marking those nodes as visited is not enough. We need to clear corresponding key-value pair in the valueIndexMap. Because as long as these nodes are pushed to the queue, they will increase time cost. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152public int minJumps(int[] arr) { int len = arr.length; // value -> list of indexes that have the same value arr[i] Map<Integer, List<Integer>> valueIndexMap = new HashMap<>(); for (int i = 0; i < len; i++) { if (valueIndexMap.containsKey(arr[i])) { List<Integer> list = valueIndexMap.get(arr[i]); list.add(i); } else { List<Integer> list = new ArrayList<>(); list.add(i); valueIndexMap.put(arr[i], list); } } int pathLen = 0; boolean[] visited = new boolean[len]; // initialized with false Queue<Integer> queue = new LinkedList<>(); // stored indexes queue.add(0); while (!queue.isEmpty()) { int size = queue.size(); // traverse in layer order for (int i = 0; i < size; i++) { int currIndex = queue.poll(); if (visited[currIndex]) { continue; } if (currIndex == len - 1) { // the first return path is the shortest path return pathLen; } visited[currIndex] = true; if (currIndex - 1 >= 0) { queue.add(currIndex - 1); } if (currIndex + 1 < len) { queue.add(currIndex + 1); } for (int index : valueIndexMap.get(arr[currIndex])) { if (index != currIndex) { queue.add(index); } } // there is no meaning to teleport to the same value on current and further iterations // only produce paths with identical or larger length // so that to waive repeated inner loop valueIndexMap.get(arr[currIndex]).clear(); } pathLen++; } return -1;}","link":"/2022/01/25/LeetCode-1345-Jump-Game-IV/"},{"title":"LeetCode 15. 3Sum","text":"QuestionGiven an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0. Notice that the solution set must not contain duplicate triplets. Example 1: 12Input: nums = [-1,0,1,2,-1,-4]Output: [[-1,-1,2],[-1,0,1]] Example 2: 12Input: nums = []Output: [] Example 3: 12Input: nums = [0]Output: [] Constraints: 0 <= nums.length <= 3000 -105 <= nums[i] <= 105 Source: https://leetcode.com/problems/3sum/ SolutionNote that the final result should not include duplicate triplets (compared by value). The two-pointers solution for two-sum problem is a complete search for any approximations of target. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<>(); if (nums == null || nums.length < 3) { return result; } Arrays.sort(nums); for (int i = 0; i < nums.length - 2; i++) { // it is safe to skip duplicate first element // because for two identical first element, // the latter has a smaller selection range for its second and third elements if (i != 0 && nums[i] == nums[i - 1]) { continue; } // two sum int j = i + 1; int k = nums.length - 1; while (j < k) { int sum = nums[i] + nums[j] + nums[k]; if (sum == 0) { result.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[k]))); // skip duplicate two sum combinations while (j < k && nums[j] == nums[j + 1]) { j++; } while (j < k && nums[k] == nums[k - 1]) { k--; } j++; k--; } else if (sum < 0) { j++; } else { k--; } } } return result;}public List<List<Integer>> threeSum2(int[] nums) { List<List<Integer>> result = new ArrayList<>(); if (nums == null || nums.length < 3) { return result; } Arrays.sort(nums); for (int i = 0; i < nums.length - 2; i++) { // it is safe to skip duplicate first element // because for two identical first element, // the latter has a smaller selection range for its second and third elements if (i != 0 && nums[i] == nums[i - 1]) { continue; } // two sum int j = i + 1; int k = nums.length - 1; while (j < k) { // a better way to skip duplicates // can perform skipping even if sum != 0 if (j > i + 1 && nums[j] == nums[j - 1]) { j++; continue; } if (k < nums.length - 1 && nums[k] == nums[k + 1]) { k--; continue; } int sum = nums[i] + nums[j] + nums[k]; if (sum == 0) { result.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[k]))); j++; k--; } else if (sum < 0) { j++; } else { k--; } } } return result;}","link":"/2022/03/10/LeetCode-15-3Sum/"},{"title":"LeetCode 155. Min Stack","text":"QuestionDesign a stack that supports push, pop, top, and retrieving the minimum element in constant time. Implement the MinStack class: MinStack() initializes the stack object. void push(int val) pushes the element val onto the stack. void pop() removes the element on the top of the stack. int top() gets the top element of the stack. int getMin() retrieves the minimum element in the stack. Example 1: 12345678910111213141516Input["MinStack","push","push","push","getMin","pop","top","getMin"][[],[-2],[0],[-3],[],[],[],[]]Output[null,null,null,null,-3,null,0,-2]ExplanationMinStack minStack = new MinStack();minStack.push(-2);minStack.push(0);minStack.push(-3);minStack.getMin(); // return -3minStack.pop();minStack.top(); // return 0minStack.getMin(); // return -2 Constraints: -231 <= val <= 231 - 1 Methods pop, top and getMin operations will always be called on non-empty stacks. At most 3 * 104 calls will be made to push, pop, top, and getMin. Source: https://leetcode.com/problems/min-stack/ SolutionWe use a monotonic stack to maintain potential min elements in the future. Monotonic data structures are very effective in solving max/min problems. This solution can be further optimized. For example, we can replace mins with an object stack that contains <value, times>, so that we can save space when there are many mins with the same value. 1234567891011121314151617181920212223242526272829303132333435363738394041public class MinStack { private Deque<Integer> stack; // stack of candidate minimums // only keep candidates that can be the min after a series of operations private Deque<Integer> mins; public MinStack() { this.stack = new ArrayDeque<>(); this.mins = new ArrayDeque<>(); } public void push(int val) { stack.push(val); if (mins.isEmpty()) { mins.push(val); } else if (val <= mins.peek()) { mins.push(val); } } // @pre: stack is not empty public void pop() { if (stack.isEmpty()) { return; } int val = stack.pop(); if (val == mins.peek()) { mins.pop(); } } // @pre: stack is not empty public int top() { return stack.peek().intValue(); } // @pre: stack is not empty public int getMin() { return mins.peek(); }}","link":"/2022/01/09/LeetCode-155-Min-Stack/"},{"title":"LeetCode 16. 3Sum Closest","text":"QuestionGiven an integer array nums of length n and an integer target, find three integers in nums such that the sum is closest to target. Return the sum of the three integers. You may assume that each input would have exactly one solution. Example 1: 123Input: nums = [-1,2,1,-4], target = 1Output: 2Explanation: The sum that is closest to the target is 2. (-1 + 2 + 1 = 2). Example 2: 12Input: nums = [0,0,0], target = 1Output: 0 Constraints: 3 <= nums.length <= 1000 -1000 <= nums[i] <= 1000 -104 <= target <= 104 Source: https://leetcode.com/problems/3sum-closest/ Solution1234567891011121314151617181920212223public int threeSumClosest(int[] nums, int target) { Arrays.sort(nums); int diff = Integer.MAX_VALUE; int closest = 0; for (int i = 0; i < nums.length - 2; i++) { int j = i + 1; int k = nums.length - 1; // two sum is a complete search for any approximations of target while (j < k) { int sum = nums[i] + nums[j] + nums[k]; if (Math.abs(target - sum) < diff) { diff = Math.abs(target - sum); closest = sum; } if (sum < target) { j++; } else { k--; } } } return closest;}","link":"/2022/03/10/LeetCode-16-3Sum-Closest/"},{"title":"LeetCode 1644. Lowest Common Ancestor of a Binary Tree II","text":"QuestionGiven the root of a binary tree, return the lowest common ancestor (LCA) of two given nodes, p and q. If either node p or q does not exist in the tree, return null. All values of the nodes in the tree are unique. According to the definition of LCA on Wikipedia: “The lowest common ancestor of two nodes p and q in a binary tree T is the lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)”. A descendant of a node x is a node y that is on the path from node x to some leaf node. Example 1: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1Output: 3Explanation: The LCA of nodes 5 and 1 is 3. Example 2: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4Output: 5Explanation: The LCA of nodes 5 and 4 is 5. A node can be a descendant of itself according to the definition of LCA. Example 3: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 10Output: nullExplanation: Node 10 does not exist in the tree, so return null. Constraints: The number of nodes in the tree is in the range [1, 104]. -109 <= Node.val <= 109 All Node.val are unique. p != q Source: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-ii/ SolutionThe solution in LeetCode 236. Lowest Common Ancestor of a Binary Tree cannot solve this question because p or q may not exist in the tree. The following solution separates the information for existence and the information for result (lowest common ancestor reference), so that fix that flaw. 1234567891011121314151617181920212223242526272829303132333435363738static class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; }}private TreeNode lca = null;// return the number of target nodes in the tree of root// separate lca information and target occurrence informationprivate int dfs(TreeNode root, TreeNode p, TreeNode q) { if (root == null) { return 0; } int count = 0; if (root == p || root == q) { count++; } count += dfs(root.left, p, q); count += dfs(root.right, p, q); // only set lca once when encountering the lowest common ancestor if (count == 2 && lca == null) { lca = root; } return count;}public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { lca = null; dfs(root, p, q); return lca;}","link":"/2022/03/08/LeetCode-1644-Lowest-Common-Ancestor-of-a-Binary-Tree-II/"},{"title":"LeetCode 160. Intersection of Two Linked Lists","text":"QuestionGiven the heads of two singly linked-lists headA and headB, return the node at which the two lists intersect. If the two linked lists have no intersection at all, return null. The test cases are generated such that there are no cycles anywhere in the entire linked structure. Note that the linked lists must retain their original structure after the function returns. Custom Judge: The inputs to the judge are given as follows (your program is not given these inputs): intersectVal - The value of the node where the intersection occurs. This is 0 if there is no intersected node. listA - The first linked list. listB - The second linked list. skipA - The number of nodes to skip ahead in listA (starting from the head) to get to the intersected node. skipB - The number of nodes to skip ahead in listB (starting from the head) to get to the intersected node. The judge will then create the linked structure based on these inputs and pass the two heads, headA and headB to your program. If you correctly return the intersected node, then your solution will be accepted. Example 1: 1234Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3Output: Intersected at '8'Explanation: The intersected node's value is 8 (note that this must not be 0 if the two lists intersect).From the head of A, it reads as [4,1,8,4,5]. From the head of B, it reads as [5,6,1,8,4,5]. There are 2 nodes before the intersected node in A; There are 3 nodes before the intersected node in B. Example 2: 1234Input: intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2Output: No intersectionExplanation: From the head of A, it reads as [2,6,4]. From the head of B, it reads as [1,5]. Since the two lists do not intersect, intersectVal must be 0, while skipA and skipB can be arbitrary values.Explanation: The two lists do not intersect, so return null. Constraints: The number of nodes of listA is in the m. The number of nodes of listB is in the n. 1 <= m, n <= 3 * 104 1 <= Node.val <= 105 0 <= skipA < m 0 <= skipB < n intersectVal is 0 if listA and listB do not intersect. intersectVal == listA[skipA] == listB[skipB] if listA and listB intersect. Follow up: Could you write a solution that runs in O(m + n) time and use only O(1) memory? Source: https://leetcode.com/problems/intersection-of-two-linked-lists/ Solution12345678910111213141516171819202122public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode pa = headA; ListNode pb = headB; // -- aLen -- \\ // -- commonLen -- // -- bLen -- / // aLen + commonLen + bLen == bLen + commonLen + aLen while (pa != pb) { if (pa == null) { pa = headB; continue; } if (pb == null) { pb = headA; continue; } pa = pa.next; pb = pb.next; } return pa;}","link":"/2022/03/09/LeetCode-160-Intersection-of-Two-Linked-Lists/"},{"title":"LeetCode 1650. Lowest Common Ancestor of a Binary Tree III","text":"QuestionGiven two nodes of a binary tree p and q, return their lowest common ancestor (LCA). Each node will have a reference to its parent node. The definition for Node is below: 123456class Node { public int val; public Node left; public Node right; public Node parent;} According to the definition of LCA on Wikipedia: “The lowest common ancestor of two nodes p and q in a tree T is the lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself).” Example 1: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1Output: 3Explanation: The LCA of nodes 5 and 1 is 3. Example 2: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4Output: 5Explanation: The LCA of nodes 5 and 4 is 5 since a node can be a descendant of itself according to the LCA definition. Constraints: The number of nodes in the tree is in the range [2, 105]. -109 <= Node.val <= 109 All Node.val are unique. p != q p and q exist in the tree. Source: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii/ SolutionThis question is actually a disguised LeetCode 160. Intersection of Two Linked Lists. The lowest common ancestor is the head of intersection list. 1234567891011121314151617public Node lowestCommonAncestor(Node p, Node q) { Node first = p; Node second = q; while (first != second) { if (first == null) { first = q; continue; } if (second == null) { second = p; continue; } first = first.parent; second = second.parent; } return first;}","link":"/2022/03/09/LeetCode-1650-Lowest-Common-Ancestor-of-a-Binary-Tree-III/"},{"title":"LeetCode 1676. Lowest Common Ancestor of a Binary Tree IV","text":"QuestionGiven the root of a binary tree and an array of TreeNode objects nodes, return the lowest common ancestor (LCA) of all the nodes in nodes. All the nodes will exist in the tree, and all values of the tree’s nodes are unique. Extending the definition of LCA on Wikipedia: “The lowest common ancestor of n nodes p1, p2, …, pn in a binary tree T is the lowest node that has every pi as a descendant (where we allow a node to be a descendant of itself) for every valid i“. A descendant of a node x is a node y that is on the path from node x to some leaf node. Example 1: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], nodes = [4,7]Output: 2Explanation: The lowest common ancestor of nodes 4 and 7 is node 2. Example 2: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], nodes = [1]Output: 1Explanation: The lowest common ancestor of a single node is the node itself. Constraints: The number of nodes in the tree is in the range [1, 104]. -109 <= Node.val <= 109 All Node.val are unique. All nodes[i] will exist in the tree. All nodes[i] are distinct. Source: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iv/ Solution1234567891011121314151617181920212223242526272829private TreeNode lca = null;// return the number of target nodes in the tree of root// separate lca information and target occurrence informationprivate int dfs(TreeNode root, Set<TreeNode> set) { if (root == null) { return 0; } int count = 0; if (set.contains(root)) { count++; } count += dfs(root.left, set); count += dfs(root.right, set); // only set lca once when encountering the lowest common ancestor if (count == set.size() && lca == null) { lca = root; } return count;}public TreeNode lowestCommonAncestor(TreeNode root, TreeNode[] nodes) { lca = null; Set<TreeNode> nodeSet = new HashSet<>(Arrays.asList(nodes)); dfs(root, nodeSet); return lca;}","link":"/2022/03/09/LeetCode-1676-Lowest-Common-Ancestor-of-a-Binary-Tree-IV/"},{"title":"LeetCode 1679. Max Number of K-Sum Pairs","text":"QuestionYou are given an integer array nums and an integer k. In one operation, you can pick two numbers from the array whose sum equals k and remove them from the array. Return the maximum number of operations you can perform on the array. Example 1: 123456Input: nums = [1,2,3,4], k = 5Output: 2Explanation: Starting with nums = [1,2,3,4]:- Remove numbers 1 and 4, then nums = [2,3]- Remove numbers 2 and 3, then nums = []There are no more pairs that sum up to 5, hence a total of 2 operations. Example 2: 12345Input: nums = [3,1,3,4,3], k = 6Output: 1Explanation: Starting with nums = [3,1,3,4,3]:- Remove the first two 3's, then nums = [1,4,3]There are no more pairs that sum up to 6, hence a total of 1 operation. Constraints: 1 <= nums.length <= 105 1 <= nums[i] <= 109 1 <= k <= 109 Source: https://leetcode.com/problems/max-number-of-k-sum-pairs/ SolutionSimilar to the hashmap solution of LeetCode 1. Two Sum. 12345678910111213141516171819public int maxOperations(int[] nums, int k) { int count = 0; // num value -> num count Map<Integer, Integer> map = new HashMap<>(); // if qualified, i is always the bigger index in a pair for (int i = 0; i < nums.length; i++) { int n = nums[i]; if (map.containsKey(k - n)) { count++; map.put(k - n, map.get(k - n) - 1); if (map.get(k - n) == 0) { map.remove(k - n); } } else { map.put(n, map.getOrDefault(n, 0) + 1); } } return count;}","link":"/2022/03/10/LeetCode-1679-Max-Number-of-K-Sum-Pairs/"},{"title":"LeetCode 18. 4Sum","text":"QuestionGiven an array nums of n integers, return an array of all the unique quadruplets [nums[a], nums[b], nums[c], nums[d]] such that: 0 <= a, b, c, d < n a, b, c, and d are distinct. nums[a] + nums[b] + nums[c] + nums[d] == target You may return the answer in any order. Example 1: 12Input: nums = [1,0,-1,0,-2,2], target = 0Output: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]] Example 2: 12Input: nums = [2,2,2,2,2], target = 8Output: [[2,2,2,2]] Constraints: 1 <= nums.length <= 200 -109 <= nums[i] <= 109 -109 <= target <= 109 Source: https://leetcode.com/problems/4sum/ SolutionTime Complexity O(n^3). In fact, this is a DFS problem. Remember to draw the DFS tree. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768// two pointer search, take advantage of the orderliness of nums// @pre: nums is sortedprivate List<List<Integer>> twoSum(int[] nums, int target, int start) { List<List<Integer>> result = new ArrayList<>(); int left = start; int right = nums.length - 1; while (left < right) { // a better way to skip duplicates // can perform skipping even if sum != 0 if (left > start && nums[left] == nums[left - 1]) { left++; continue; } if (right < nums.length - 1 && nums[right] == nums[right + 1]) { right--; continue; } int sum = nums[left] + nums[right]; if (sum == target) { result.add(new ArrayList<>(Arrays.asList(nums[left], nums[right]))); left++; right--; } else if (sum > target) { right--; } else { left++; } } return result;}// dfs, depth is k// @pre: nums is sorted// return the k-sum combinations with given kprivate List<List<Integer>> kSum(int[] nums, int k, int target, int start) { List<List<Integer>> result = new ArrayList<>(); if (start >= nums.length) { return result; } // pruning double avg = ((double) target) / k; if (nums[start] > avg || nums[nums.length - 1] < avg) { return result; } if (k == 2) { return twoSum(nums, target, start); } for (int i = start; i < nums.length; i++) { // skip duplicates if (i != start && nums[i] == nums[i - 1]) { continue; } int n = nums[i]; for (List<Integer> subset : kSum(nums, k - 1, target - n, i + 1)) { List<Integer> currLayerComb = new ArrayList<>(); currLayerComb.add(n); currLayerComb.addAll(subset); result.add(currLayerComb); } } return result;}public List<List<Integer>> fourSum(int[] nums, int target) { Arrays.sort(nums); return kSum(nums, 4, target, 0);}","link":"/2022/03/10/LeetCode-18-4Sum/"},{"title":"LeetCode 188. Best Time to Buy and Sell Stock IV","text":"QuestionYou are given an integer array prices where prices[i] is the price of a given stock on the ith day, and an integer k. Find the maximum profit you can achieve. You may complete at most k transactions. Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again). Example 1: 123Input: k = 2, prices = [2,4,1]Output: 2Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2. Example 2: 123Input: k = 2, prices = [3,2,6,5,0,3]Output: 7Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4. Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3. Constraints: 0 <= k <= 100 0 <= prices.length <= 1000 0 <= prices[i] <= 1000 Source: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ SolutionTo find an efficient solution for this type of problem, we need to use DP. However, let us consider an extreme case fisrt. We are allow to make at most k transactions. If k is large enough to enable us to grab every profit, we do not have to do DP but just traverse the array once. The threshold of this k is n/2. Because a stock can rise at most n/2 times (two rises on two adjacent days can be merged into one). This is the idea of quickSolve(). For DP, the essence of DP is to solve small sub-problems first, and derive the results from these small sub-problem to larger problems. This max profit problem has two dimensions: 1. the number of days; 2. the max number of transactions allowed. So we creaste a 2D DP cache. And because deriving results from fewer days to more days is easier than changing max number of transactions, we make k+1 (more convenient than k) the first dimension and n the second dimension. Always use a variable that is easier to derive as the second dimension in 2D DP. dp[i][j] represents the max profit of prices[:j] (inclusive) with at most i transactions. Thus, there are only two options for dp[i][j]: sell the stock on jth day or not. If sell, we need to iterate all possible cases to find the max profit. If not, dp[i][j] should be equal to dp[i][j-1]. So, we can get the following formulas for these two situations: dp[i][j] = dp[i][j-1]. t is the date the last transaction was brought. dp[i][j] = for t: 0->j-1, max(dp[i-1][t-1]+prices[j]-prices[t]) . This solution still has a O((n^2)*k) time complexity. The bottleneck is the second formula. However, we can further optimize it. It is equivalent to for t: 0->j-1, max(dp[i-1][t-1]-prices[t])+prices[j]. Thus, we can find the max of the second formula while iterating 0 to j-1 once. Finally, the time complexity is optimized to O(nk). 1234567891011121314151617181920212223242526272829// return the max profit without limiting the number of transactionsprivate int quickSolve(int[] prices) { int maxProfit = 0; for (int i = 1; i < prices.length; i++) { // short-term trading maxProfit += Math.max(0, prices[i] - prices[i - 1]); } return maxProfit;}// Time Complexity O(nk)public int maxProfit(int k, int[] prices) { int n = prices.length; // if k allows us to get every profit if (k >= n / 2) { return quickSolve(prices); } // dp[i][j] is the max profit of prices[:j] (inclusive) with at most i transactions int[][] dp = new int[k + 1][n]; for (int i = 1; i <= k; i++) { // dp[i-1][t-1]-prices[t] int maxTemp = -prices[0]; for (int j = 1; j < n; j++) { dp[i][j] = Math.max(dp[i][j - 1], maxTemp + prices[j]); maxTemp = Math.max(maxTemp, dp[i - 1][j - 1] - prices[j]); } } return dp[k][n - 1];}","link":"/2022/01/24/LeetCode-188-Best-Time-to-Buy-and-Sell-Stock-IV/"},{"title":"LeetCode 235. Lowest Common Ancestor of a Binary Search Tree","text":"QuestionGiven a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST. According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).” Example 1: 123Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8Output: 6Explanation: The LCA of nodes 2 and 8 is 6. Example 2: 123Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4Output: 2Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition. Constraints: The number of nodes in the tree is in the range [2, 105]. -109 <= Node.val <= 109 All Node.val are unique. p != q p and q will exist in the BST. Source: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ Solution12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455// If BST is balanced, Time Complexity O(log(n))// Worst Time Complexity O(n)// return the lowest common ancestor// with an assumption that p and q must exist in treeprivate TreeNode dfs(TreeNode root, TreeNode p, TreeNode q) { if (p.val < root.val && q.val < root.val) { // both p, q in the left tree return dfs(root.left, p, q); } else if (p.val > root.val && q.val > root.val) { // both p, q in the right tree return dfs(root.right, p, q); } else { // p or q is current root, // or p and q are in two different subtrees respectively // in either case, current root is the lowest common ancestor return root; }}public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { return dfs(root, p, q);}private TreeNode lca = null;// an accelerated brutal force DFS, so has higher time complexity// but works for cases that p and q may not exist in the treeprivate int dfs2(TreeNode root, TreeNode p, TreeNode q) { if (root == null) { return 0; } int count = 0; if (root == p || root == q) { count++; } if (p.val < root.val && q.val < root.val) { count += dfs2(root.left, p, q); } else if (p.val > root.val && q.val > root.val) { count += dfs2(root.right, p, q); } else { count += dfs2(root.left, p, q); count += dfs2(root.right, p, q); } if (count == 2 && lca == null) { lca = root; } return count;}public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) { lca = null; dfs2(root, p, q); return lca;}","link":"/2022/03/09/LeetCode-235-Lowest-Common-Ancestor-of-a-Binary-Search-Tree/"},{"title":"LeetCode 236. Lowest Common Ancestor of a Binary Tree","text":"QuestionGiven a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).” Example 1: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1Output: 3Explanation: The LCA of nodes 5 and 1 is 3. Example 2: 123Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4Output: 5Explanation: The LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition. Example 3: 12Input: root = [1,2], p = 1, q = 2Output: 1 Constraints: The number of nodes in the tree is in the range [2, 105]. -109 <= Node.val <= 109 All Node.val are unique. p != q p and q will exist in the tree. Source: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/ SolutionDFS. Use the return value of recursive function to reflect the existence of p, q and their lowest common ancestor. The following implementation is feasible but no recommended, because the return value has multiple meanings. 1234567891011121314151617181920212223242526272829303132333435363738static class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; }}// one important prerequisite is that p and q must be in the whole tree// the return value has multiple meanings (NOT a good design):// if null, neither p nor q exists in the tree of root;// if not null, at least one of p and q exists in the tree of root,// if p and q exist in the left and right subtree of root respectively, return root itself.// because in this case, root is the lowest common ancestorprivate TreeNode dfs(TreeNode root, TreeNode p, TreeNode q) { if (root == null || root == p || root == q) { return root; } TreeNode leftResult = dfs(root.left, p, q); TreeNode rightResult = dfs(root.right, p, q); if (leftResult != null && rightResult != null) { // root is the lowest common ancestor return root; } else if (leftResult != null) { return leftResult; } else if (rightResult != null) { return rightResult; } else { return null; }}public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { return dfs(root, p, q);}","link":"/2022/03/08/LeetCode-236-Lowest-Common-Ancestor-of-a-Binary-Tree/"},{"title":"LeetCode 239. Sliding Window Maximum","text":"QuestionYou are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window. Example 1: 1234567891011Input: nums = [1,3,-1,-3,5,3,6,7], k = 3Output: [3,3,5,5,6,7]Explanation: Window position Max--------------- -----[1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7 Example 2: 12Input: nums = [1], k = 1Output: [1] Constraints: 1 <= nums.length <= 105 -104 <= nums[i] <= 104 1 <= k <= nums.length Source: https://leetcode.com/problems/sliding-window-maximum/ SolutionAs the window slides, we add a new element on right and discard an element on left. Because we discard elements from the window, we use a data structure to maintain all potential max values in the future (like LeetCode 155. Min Stack). Thus, we always push the new elment in to the queue when the window expands on right. If we find that the new element is greater than several previous elements, it means that the new element becomes the new “guardian”. The previous guardians that are smaller than this new guardian can be removed from the queue. Because they will be discarded eariler than the new guardian and will never become the max. In other words, elements with a larger index and a larger corresponding value have an overwhelming advantage in competing for max. The indexes in the monotonic queue are not necessarily consecutive. And this monotonic queue guarantees that these indexes are in ascending order and their corresponding elements in nums are in descending order. Because we need to repeatedly remove elements from the head and tail of the queue and append elements to the tail of the queue, we choose LinkedList as the instance class of our monotonic queue. 1234567891011121314151617181920212223242526272829303132// monotonic deque O(n)public int[] maxSlidingWindow2(int[] nums, int k) { int len = nums.length; int[] result = new int[len - k + 1]; // part indexes of elements in current window // i_{k}(max), i_{m}, i_{n}, ..., i_{right} // \\head i_{k} < i_{m} < i_{n} < ... < i_{right} \\tail // \\head nums[i_{k}] >= nums[i_{m}] >= nums[i_{n}] >= ... >= nums[i_{right}] \\tail Deque<Integer> monoDeque = new LinkedList<>(); // queue initialization with the first window for (int i = 0; i < k; i++) { while (!monoDeque.isEmpty() && nums[monoDeque.getLast()] < nums[i]) { monoDeque.removeLast(); } monoDeque.addLast(i); } result[0] = nums[monoDeque.getFirst()]; int left = 1, right = k; for (; right < len; left++, right++) { // max is to be removed if (!monoDeque.isEmpty() && monoDeque.getFirst() == left - 1) { monoDeque.removeFirst(); } while (!monoDeque.isEmpty() && nums[monoDeque.getLast()] < nums[right]) { monoDeque.removeLast(); } monoDeque.addLast(right); result[left] = nums[monoDeque.getFirst()]; } return result;}","link":"/2022/01/09/LeetCode-239-Sliding-Window-Maximum/"},{"title":"LeetCode 252. Meeting Rooms","text":"QuestionGiven an array of meeting time intervals where intervals[i] = [starti, endi], determine if a person could attend all meetings. Example 1: 12Input: intervals = [[0,30],[5,10],[15,20]]Output: false Example 2: 12Input: intervals = [[7,10],[2,4]]Output: true Constraints: 0 <= intervals.length <= 104 intervals[i].length == 2 0 <= starti < endi <= 106 Source: https://leetcode.com/problems/meeting-rooms/ SolutionSort intervals by start. 12345678910111213141516// sort, O(nlog(n))public boolean canAttendMeetings(int[][] intervals) { // sort intervals by the start in ascending order Arrays.sort(intervals, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { return o1[0] - o2[0]; } }); for (int i = 1; i < intervals.length; i++) { if (intervals[i - 1][1] > intervals[i][0]) { return false; } } return true;}","link":"/2022/01/24/LeetCode-252-Meeting-Rooms/"},{"title":"LeetCode 253. Meeting Rooms II","text":"QuestionGiven an array of meeting time intervals intervals where intervals[i] = [starti, endi], return the minimum number of conference rooms required. Example 1: 12Input: intervals = [[0,30],[5,10],[15,20]]Output: 2 Example 2: 12Input: intervals = [[7,10],[2,4]]Output: 1 Constraints: 1 <= intervals.length <= 104 0 <= starti < endi <= 106 Source: https://leetcode.com/problems/meeting-rooms-ii/ SolutionWe can simulate the schedule of meeting rooms by a min heap of ends of intervals. Because when a new meeting comes in, we only need to compare its start time with the end time of the earliest ending meeting, and decide whether we should add one more room. Note that in the following implementation, we poll at most one element from the heap per loop, rather than polling all terminated meetings, because we choose return the size of the heap as the result. Logically chronological ordering solution is further optimized, though its time complexity is the same heap-based solution, O(nlog(n)). The idea is break the relationship between start and end. We sort starts and ends separately, and use two pointers to traverse them. We can still get the correct result because we do not care about which meeting ends and makes a room spare, but the timepoint of the end. One evidence is that in the heap-based solution, we only stores ends of intervals in the heap. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061// heap, O(nlog(n))public int minMeetingRooms(int[][] intervals) { if (intervals.length == 0) { return 0; } // sort intervals by the start in ascending order Arrays.sort(intervals, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { return o1[0] - o2[0]; } }); // min heap of end of interval PriorityQueue<Integer> endMinHeap = new PriorityQueue<>(intervals.length, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } }); endMinHeap.add(intervals[0][1]); for (int i = 1; i < intervals.length; i++) { int[] curr = intervals[i]; // try to find a free room if (curr[0] >= endMinHeap.peek()) { endMinHeap.poll(); } endMinHeap.add(curr[1]); } return endMinHeap.size();}// Chronological Ordering, O(nlog(n))public int minMeetingRooms2(int[][] intervals) { int len = intervals.length; if (len == 0) { return 0; } int[] starts = new int[len]; int[] ends = new int[len]; for (int i = 0; i < len; i++) { starts[i] = intervals[i][0]; ends[i] = intervals[i][1]; } // sort respectively by ascending order // we want to know if there is an empty room at a certain point in time, // but don't care which meeting room it is Arrays.sort(starts); Arrays.sort(ends); int startIndex = 0, endIndex = 0; int numRoom = 0; while (startIndex < len && endIndex < len) { if (starts[startIndex] >= ends[endIndex]) { startIndex++; endIndex++; } else { startIndex++; numRoom++; } } return numRoom;}","link":"/2022/01/24/LeetCode-253-Meeting-Rooms-II/"},{"title":"LeetCode 259. 3Sum Smaller","text":"QuestionGiven an array of n integers nums and an integer target, find the number of index triplets i, j, k with 0 <= i < j < k < n that satisfy the condition nums[i] + nums[j] + nums[k] < target. Example 1: 12345Input: nums = [-2,0,1,3], target = 2Output: 2Explanation: Because there are two triplets which sums are less than 2:[-2,0,1][-2,0,3] Example 2: 12Input: nums = [], target = 0Output: 0 Example 3: 12Input: nums = [0], target = 0Output: 0 Constraints: n == nums.length 0 <= n <= 3500 -100 <= nums[i] <= 100 -100 <= target <= 100 Source: https://leetcode.com/problems/3sum-smaller/ Solution1234567891011121314151617181920212223public int threeSumSmaller(int[] nums, int target) { Arrays.sort(nums); int count = 0; for (int i = 0; i < nums.length - 2; i++) { // a variant of two-sum int j = i + 1; int k = nums.length - 1; while (j < k) { int sum = nums[i] + nums[j] + nums[k]; if (sum < target) { // for current j and k, // all k' between j and k satisfy the requirement count += k - j; j++; } else { // if sum >= target, // move right, try to reduce the value of sum k--; } } } return count;}","link":"/2022/03/10/LeetCode-259-3Sum-Smaller/"},{"title":"LeetCode 266. Palindrome Permutation","text":"QuestionGiven a string s, return true if a permutation of the string could form a palindrome. Example 1: 12Input: s = "code"Output: false Example 2: 12Input: s = "aab"Output: true Example 3: 12Input: s = "carerac"Output: true Constraints: 1 <= s.length <= 5000 s consists of only lowercase English letters. Source: https://leetcode.com/problems/palindrome-permutation/ Solution12345678910111213141516171819202122232425262728public boolean canPermutePalindrome(String s) { Map<Character, Integer> charCount = new HashMap<>(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); charCount.put(c, charCount.getOrDefault(c, 0) + 1); } int oddCount = 0; for (int count : charCount.values()) { if (count % 2 == 1) { oddCount++; } } // at most one character can appear odd times return oddCount == 0 || oddCount == 1;}public boolean canPermutePalindrome2(String s) { Set<Character> charSet = new HashSet<>(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (charSet.contains(c)) { charSet.remove(c); } else { charSet.add(c); } } return charSet.size() == 0 || charSet.size() == 1;}","link":"/2022/03/06/LeetCode-266-Palindrome-Permutation/"},{"title":"LeetCode 271. Encode and Decode String","text":"QuestionDesign an algorithm to encode a list of strings to a string. The encoded string is then sent over the network and is decoded back to the original list of strings. Machine 1 (sender) has the function: 1234string encode(vector<string> strs) { // ... your code return encoded_string;} Machine 2 (receiver) has the function: 1234vector<string> decode(string s) { //... your code return strs;} So Machine 1 does: 1string encoded_string = encode(strs); and Machine 2 does: 1vector<string> strs2 = decode(encoded_string); strs2 in Machine 2 should be the same as strs in Machine 1. Implement the encode and decode methods. You are not allowed to solve the problem using any serialize methods (such as eval). Example 1: 1234567891011Input: dummy_input = ["Hello","World"]Output: ["Hello","World"]Explanation:Machine 1:Codec encoder = new Codec();String msg = encoder.encode(strs);Machine 1 ---msg---> Machine 2Machine 2:Codec decoder = new Codec();String[] strs = decoder.decode(msg); Example 2: 12Input: dummy_input = [""]Output: [""] Constraints: 1 <= strs.length <= 200 0 <= strs[i].length <= 200 strs[i] contains any possible characters out of 256 valid ASCII characters. Source: https://leetcode.com/problems/encode-and-decode-strings/ SolutionThe best solution is to add a header that represents the length of each string instead of using delimiter. Strings will be concatenated as header-payload-header-.... Note that we call toCharArray method rather than getBytes(). We do not care about the content of payload so that we do not need to encode/decode strings. Also, on some OA platforms, we may need to import desired character set manually. 123456789101112131415161718192021222324252627282930313233343536373839404142// encode a 32-bit integer to a char array, according to little endian// then convert the byte array into a stringprivate String int2string(int len) { char[] bytes = new char[4]; for (int i = 0; i < 4; i++) { bytes[i] = (char) ((len >> (i * 8)) & 0xff); } return new String(bytes);}// decode a 32-bit string to intprivate int string2int(String s) { char[] bytes = s.toCharArray(); int len = 0; for (int i = 0; i < 4; i++) { len += ((int) (bytes[i])) << (i * 8); } return len;}// Encodes a list of strings to a single string.public String encode(List<String> strs) { StringBuilder builder = new StringBuilder(); for (String str : strs) { builder.append(int2string(str.length())); builder.append(str); } return builder.toString();}// Decodes a single string to a list of strings.public List<String> decode(String s) { int i = 0; List<String> res = new ArrayList<>(); while (i < s.length()) { int strLen = string2int(s.substring(i, i + 4)); i += 4; res.add(s.substring(i, i + strLen)); i += strLen; } return res;}","link":"/2021/12/30/LeetCode-271-Encode-and-Decode-String/"},{"title":"LeetCode 3. Longest Substring Without Repeating Characters","text":"QuestionGiven a string s, find the length of the longest substring without repeating characters. Example 1: 123Input: s = "abcabcbb"Output: 3Explanation: The answer is "abc", with the length of 3. Example 2: 123Input: s = "bbbbb"Output: 1Explanation: The answer is "b", with the length of 1. Example 3: 1234Input: s = "pwwkew"Output: 3Explanation: The answer is "wke", with the length of 3.Notice that the answer must be a substring, "pwke" is a subsequence and not a substring. Constraints: 0 <= s.length <= 5 * 104 s consists of English letters, digits, symbols and spaces Source: https://leetcode.com/problems/longest-substring-without-repeating-characters/ SolutionSliding window is an optimization for brutal force search. When searching substrings that satisfy certain conditions, we do not have to examine every substring. In some cases, we can end up some search paths early and try another direction, which is similar to pruning in DFS. Take the following string as an example. It is meaningless to check substrings that start with ‘a’, ‘b’, ‘c’ and the first ‘5’ because they will definitely include repeating ‘5’ if they have a larger or the same length. 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263// brutal force, O(n^2)public int lengthOfLongestSubstring0(String s) { int len = s.length(); int maxSubLen = 0; Set<Character> set = new HashSet<>(); // i, j are the left and right end (inclusive) of a substring for (int i = 0; i < len; i++) { for (int j = i; j < len; j++) { char c = s.charAt(j); if (set.contains(c)) { break; } else { set.add(c); maxSubLen = Math.max(maxSubLen, j - i + 1); } } set.clear(); } return maxSubLen;}// sliding window, O(n)public int lengthOfLongestSubstring1(String s) { int len = s.length(); int maxSubLen = 0; int left = 0, right = 0; // character -> frequency in the current substring Map<Character, Integer> map = new HashMap<>(); while (left < len && right < len) { char cright = s.charAt(right); while (map.getOrDefault(cright, 0) > 0) { char cleft = s.charAt(left); map.put(cleft, map.get(cleft) - 1); left++; } map.put(cright, 1); maxSubLen = Math.max(maxSubLen, right - left + 1); right++; } return maxSubLen;}// optimized sliding window, O(n)public int lengthOfLongestSubstring2(String s) { int len = s.length(); int maxSubLen = 0; int left = 0, right = 0; // character -> last index of this character Map<Character, Integer> map = new HashMap<>(); while (left < len && right < len) { char cright = s.charAt(right); if (map.containsKey(cright)) { // when left end moving forward, we do not remove kv pairs from map // only if the last index of cright is within the range of current substring // we move left forward left = Math.max(left, map.get(cright) + 1); } map.put(cright, right); maxSubLen = Math.max(maxSubLen, right - left + 1); right++; } return maxSubLen;}","link":"/2022/01/09/LeetCode-3-Longest-Substring-Without-Repeating-Characters/"},{"title":"LeetCode 30. Substring with Concatenation of All Words","text":"QuestionYou are given a string s and an array of strings words of the same length. Return all starting indices of substring(s) in s that is a concatenation of each word in words exactly once, in any order, and without any intervening characters. You can return the answer in any order. Example 1: 1234Input: s = "barfoothefoobarman", words = ["foo","bar"]Output: [0,9]Explanation: Substrings starting at index 0 and 9 are "barfoo" and "foobar" respectively.The output order does not matter, returning [9,0] is fine too. Example 2: 12Input: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]Output: [] Example 3: 12Input: s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]Output: [6,9,12] Constraints: 1 <= s.length <= 104 s consists of lower-case English letters. 1 <= words.length <= 5000 1 <= words[i].length <= 30 words[i] consists of lower-case English letters. Source: https://leetcode.com/problems/substring-with-concatenation-of-all-words/ SolutionSliding window with offset and step. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556// search by the step of wlenprivate void slidingWindow(int initLeft, int wlen, int targetSize, String s, Map<String, Integer> dict, List<Integer> result) { // left and right are the start indexes of the first word and the last word // in the window respectively int left = initLeft, right = initLeft; // the number of rules need to meet int toMeet = dict.size(); int slen = s.length(); Map<String, Integer> wordCount = new HashMap<>(); while (right <= slen - wlen) { String curr = s.substring(right, right + wlen); if (dict.containsKey(curr)) { wordCount.put(curr, wordCount.getOrDefault(curr, 0) + 1); if (wordCount.get(curr).equals(dict.get(curr))) { toMeet--; } while (toMeet == 0) { // exactly satisfy or beyond the demand if (right - left + wlen == targetSize) { result.add(left); } String remove = s.substring(left, left + wlen); // wordCount must contain remove wordCount.put(remove, wordCount.getOrDefault(remove, 0) - 1); if (wordCount.get(remove) < dict.get(remove)) { // break one rule toMeet++; } left += wlen; } right += wlen; } else { // encounter an invalid word right += wlen; left = right; toMeet = dict.size(); wordCount.clear(); } }}public List<Integer> findSubstring(String s, String[] words) { int wlen = words[0].length(); int wnum = words.length; // word -> the number of times it should appear Map<String, Integer> dict = new HashMap<>(); for (String word : words) { dict.put(word, dict.getOrDefault(word, 0) + 1); } List<Integer> result = new ArrayList<>(); // for each initial offset for (int i = 0; i < wlen; i++) { slidingWindow(i, wlen, wnum * wlen, s, dict, result); } return result;}","link":"/2022/03/12/LeetCode-30-Substring-with-Concatenation-of-All-Words/"},{"title":"LeetCode 31. Next Permutation","text":"QuestionA permutation of an array of integers is an arrangement of its members into a sequence or linear order. For example, for arr = [1,2,3], the following are considered permutations of arr: [1,2,3], [1,3,2], [3,1,2], [2,3,1]. The next permutation of an array of integers is the next lexicographically greater permutation of its integer. More formally, if all the permutations of the array are sorted in one container according to their lexicographical order, then the next permutation of that array is the permutation that follows it in the sorted container. If such arrangement is not possible, the array must be rearranged as the lowest possible order (i.e., sorted in ascending order). For example, the next permutation of arr = [1,2,3] is [1,3,2]. Similarly, the next permutation of arr = [2,3,1] is [3,1,2]. While the next permutation of arr = [3,2,1] is [1,2,3] because [3,2,1] does not have a lexicographical larger rearrangement. Given an array of integers nums, find the next permutation of nums. The replacement must be in place and use only constant extra memory. Example 1: 12Input: nums = [1,2,3]Output: [1,3,2] Example 2: 12Input: nums = [3,2,1]Output: [1,2,3] Example 3: 12Input: nums = [1,1,5]Output: [1,5,1] Constraints: 1 <= nums.length <= 100 0 <= nums[i] <= 100 Source: https://leetcode.com/problems/next-permutation/ SolutionOur idea is similar to carry in addition. We need to find the longest non-increasing suffix and perform a carry operation on this suffix, so that we get the next permutation. Specifically, we swap the digit before this suffix (nums[pivot]) with an element just greater than the digit (from right to left in the suffix, find the first element that is larger than the pivot). Then we reverse the suffix to make it non-decreasing so that the new suffix is the “smallest” permutation of itself. Here are several points worth noting. First, an element greater than pivot must exist in the suffix. Because this suffix is the longest non-increasing suffix, pivot at least is smaller than the first element in the suffix. Second, after swap, the new suffix is still non-increasing because elements in the left of the swap position must be equal to or less than the pivot. This is guaranteed by our search strategy. 12345678910111213141516171819202122232425262728293031323334353637383940414243private void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;}// reverse elements in [i, j]private void reverse(int[] nums, int i, int j) { while (i < j) { swap(nums, i, j); i++; j--; }}// Time Complexity, O(n)public void nextPermutation(int[] nums) { if (nums == null || nums.length <= 1) { return; } // find the longest non-increasing suffix int pivot = nums.length - 2; // find the pivot before the longest non-increasing suffix while (pivot >= 0 && nums[pivot] >= nums[pivot + 1]) { pivot--; } if (pivot >= 0) { // from right to left in the suffix, find the first element that is larger than the pivot // "first" is important for keeping suffix non-increasing int j = nums.length - 1; for (; j >= pivot + 1; j--) { if (nums[j] > nums[pivot]) { break; } } // swap and make the suffix non-decreasing by reversing it swap(nums, pivot, j); reverse(nums, pivot + 1, nums.length - 1); } else { // nums is the last permutation // turn it to the first permutation reverse(nums, pivot + 1, nums.length - 1); }} Example","link":"/2022/03/06/LeetCode-31-Next-Permutation/"},{"title":"LeetCode 33. Search in Rotated Sorted Array","text":"QuestionThere is an integer array nums sorted in ascending order (with distinct values). Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums. You must write an algorithm with O(log n) runtime complexity. Example 1: 12Input: nums = [4,5,6,7,0,1,2], target = 0Output: 4 Example 2: 12Input: nums = [4,5,6,7,0,1,2], target = 3Output: -1 Example 3: 12Input: nums = [1], target = 0Output: -1 Constraints: 1 <= nums.length <= 5000 -104 <= nums[i] <= 104 All values of nums are unique. nums is an ascending array that is possibly rotated. -104 <= target <= 104 Source: https://leetcode.com/problems/search-in-rotated-sorted-array/ SolutionThe main idea is first using binary search to find the pivot and then performing another binary search on a part of the array. For the binary search in findPivot, left must be adjacent to right (not overlapped). Because we use a conservative strategy to move pointers (left = mid, right = mid). 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556// find the pivot of rotated sorted array// that divide nums into two interval: [:pivot), [pivot:]// @pre: nums only contains distinct numbersprivate int findPivot(int[] nums) { int len = nums.length; int left = 0, right = len - 1; if (nums[left] < nums[right]) { return 0; } while (left < right - 1) { int mid = left + (right - left) / 2; if (nums[mid] > nums[left]) { left = mid; } else { right = mid; } } return right;}// normal binary search, assume nums is ascending in [left, right]private int binarySearch(int[] nums, int left, int right, int target) { while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] == target) { return mid; } else if (nums[mid] < target) { left = mid + 1; } else { right = mid; } } return nums[left] == target ? left : -1;}public int search(int[] nums, int target) { // cope with cases of length <= 1 if (nums == null || nums.length == 0) { return -1; } if (nums.length == 1) { return target == nums[0] ? 0 : -1; } int len = nums.length; int pivot = findPivot(nums); // handle special case that there is only one interval if (pivot == 0) { return binarySearch(nums, 0, len - 1, target); } if (target >= nums[0]) { return binarySearch(nums, 0, pivot - 1, target); } else { return binarySearch(nums, pivot, len - 1, target); }}","link":"/2022/03/09/LeetCode-33-Search-in-Rotated-Sorted-Array/"},{"title":"LeetCode 35. Search Insert Position","text":"QuestionGiven a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You must write an algorithm with O(log n) runtime complexity. Example 1: 12Input: nums = [1,3,5,6], target = 5Output: 2 Example 2: 12Input: nums = [1,3,5,6], target = 2Output: 1 Example 3: 12Input: nums = [1,3,5,6], target = 7Output: 4 Constraints: 1 <= nums.length <= 104 -104 <= nums[i] <= 104 nums contains distinct values sorted in ascending order. -104 <= target <= 104 Source: https://leetcode.com/problems/search-insert-position/ Solution12345678910111213141516171819202122232425public int searchInsert(int[] nums, int target) { int len = nums.length; int left = 0, right = len - 1; // mid is rounded to the floor, so left must make progress while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] == target) { return mid; } else if (nums[mid] > target) { right = mid; } else { left = mid + 1; } } // out of the loop, left == right if (nums[left] == target) { return left; } else if (nums[left] < target) { return right + 1; } else { return left; }}","link":"/2022/03/09/LeetCode-35-Search-Insert-Position/"},{"title":"LeetCode 370. Range Addition","text":"QuestionYou are given an integer length and an array updates where updates[i] = [startIdxi, endIdxi, inci]. You have an array arr of length length with all zeros, and you have some operation to apply on arr. In the ith operation, you should increment all the elements arr[startIdxi], arr[startIdxi + 1], ..., arr[endIdxi] by inci. Return arr after applying all the updates. Example 1: 12Input: length = 5, updates = [[1,3,2],[2,4,3],[0,2,-2]]Output: [-2,0,3,5,3] Example 2: 12Input: length = 10, updates = [[2,4,6],[5,6,8],[1,9,-4]]Output: [0,-4,2,2,2,4,4,-4,-4,-4] Constraints: 1 <= length <= 105 0 <= updates.length <= 104 0 <= startIdxi <= endIdxi < length -1000 <= inci <= 1000 Source: https://leetcode.com/problems/range-addition/ SolutionThe idea is like log. We do not directly compute the value of each element, but record the difference of each element’s value relative to its left neighbor. The time complexity is O(n). 12345678910111213141516171819202122public int[] getModifiedArray(int length, int[][] updates) { // deltas[i] represents the delta from result[i-1] to result[i] // for delta[0], we set result[-1] = 0 int[] deltas = new int[length]; for (int[] update: updates) { int start = update[0]; int end = update[1]; int value = update[2]; deltas[start] += value; if (end + 1 < length) { deltas[end + 1] -= value; } } int accumulate = 0; int[] result = new int[length]; for (int i = 0; i < length; i++) { accumulate += deltas[i]; result[i] = accumulate; } return result;}","link":"/2022/03/09/LeetCode-370-Range-Addition/"},{"title":"LeetCode 373. Find K Pairs with Smallest Sums","text":"QuestionYou are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define a pair (u, v) which consists of one element from the first array and one element from the second array. Return the k pairs (u1, v1), (u2, v2), ..., (uk, vk) with the smallest sums. Example 1: 123Input: nums1 = [1,7,11], nums2 = [2,4,6], k = 3Output: [[1,2],[1,4],[1,6]]Explanation: The first 3 pairs are returned from the sequence: [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6] Example 2: 123Input: nums1 = [1,1,2], nums2 = [1,2,3], k = 2Output: [[1,1],[1,1]]Explanation: The first 2 pairs are returned from the sequence: [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3] Example 3: 123Input: nums1 = [1,2], nums2 = [3], k = 3Output: [[1,3],[2,3]]Explanation: All possible pairs are returned from the sequence: [1,3],[2,3] Constraints: 1 <= nums1.length, nums2.length <= 105 -109 <= nums1[i], nums2[i] <= 109 nums1 and nums2 both are sorted in ascending order. 1 <= k <= 104 Source: https://leetcode.com/problems/find-k-pairs-with-smallest-sums/ SolutionThis problem is a disguised LeetCode 378. Kth Smallest Element in a Sorted Matrix. We can treat combinations of nums1 and nums2 as a (len1,len2) matrix. Each element in this matrix is the sum of a pair. Also, this matrix is sorted, which means that every row and column is sorted in ascending order. 123456789101112131415161718192021222324252627282930313233343536373839404142static class element { // nums1[x] + nums2[y] int val; int x; int y; public element(int _val, int _x, int _y) { this.val = _val; this.x = _x; this.y = _y; }}// min heap, Time Complexity O(k*log(k))public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) { int len1 = nums1.length; int len2 = nums2.length; if (len1 == 0 || len2 == 0 || k == 0) { return new ArrayList<>(); } PriorityQueue<element> minHeap = new PriorityQueue<>(Math.min(k, len1), new Comparator<element>() { @Override public int compare(element o1, element o2) { return o1.val - o2.val; } }); for (int i = 0; i < Math.min(k, len1); i++) { minHeap.add(new element(nums1[i] + nums2[0], i, 0)); } List<List<Integer>> result = new ArrayList<>(); // treat combinations of nums1 and nums2 as a (len1,len2) matrix for (int count = 0; count < k && !minHeap.isEmpty(); count++) { element e = minHeap.poll(); result.add(new ArrayList<Integer>(Arrays.asList(nums1[e.x], nums2[e.y]))); if (e.y < len2 - 1) { minHeap.add(new element(nums1[e.x] + nums2[e.y + 1], e.x, e.y + 1)); } } return result;}","link":"/2022/03/10/LeetCode-373-Find-K-Pairs-with-Smallest-Sums/"},{"title":"LeetCode 378. Kth Smallest Element in a Sorted Matrix","text":"QuestionGiven an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest element in the matrix. Note that it is the kth smallest element in the sorted order, not the kth distinct element. You must find a solution with a memory complexity better than O(n^2). Example 1: 123Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8Output: 13Explanation: The elements in the matrix are [1,5,9,10,11,12,13,13,15], and the 8th smallest number is 13 Example 2: 12Input: matrix = [[-5]], k = 1Output: -5 Constraints: n == matrix.length == matrix[i].length 1 <= n <= 300 -109 <= matrix[i][j] <= 109 All the rows and columns of matrix are guaranteed to be sorted in non-decreasing order. 1 <= k <= n2 Source: https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/ SolutionGenerally, there are three ways to find the kth smallest/largest element: 1. Max Heap 2. Min Heap 3. Quick Sort. We will skip quicksort for now in this blog post. If using max heap, we should instantiate a max heap with size of k, and fill the heap with the first k elements of the matrix (it is the initilization process). Then we traverse the rest of elements in the matrix. If an element e is greater than the heap top, drop it; otherwise, pop the heap top and push element e. After this process, elements in the max heap are k-smallest elements. And the heap top is the result. However, because we need to traverse the whole matrix (can be optimized but still on the order of O(n^2)) and do not utilize the order feature of given matrix, the time complexity will be O(n^2*log(k)). In the other solution, we treat a sorted matrix as n sorted list. So it can be regarded as an extension of finding kth smallest element in two sorted list. The difference is that we do not use n variables to store indexes on list and compare them, but use a min heap to do this job. The main difference between max heap solution and min heap solution: We use the max heap to find k-smallest elements by traverse the matrix and comparing elements with the heap top. For min heap, we get k-smallest elements by other means and use min heap to find largest one of them (the kth smallest element). 1234567891011121314151617181920212223242526272829303132333435363738class element { // matrix[x][y] int val; int x; int y; public element(int _val, int _x, int _y) { this.val = _val; this.x = _x; this.y = _y; }}// min heap, Time Complexity O(k*log(k))public int kthSmallest2(int[][] matrix, int k) { int n = matrix.length; int numSortedRow = Math.min(n, k); PriorityQueue<element> minHeap = new PriorityQueue<>(numSortedRow, new Comparator<element>() { @Override public int compare(element o1, element o2) { return o1.val - o2.val; } }); for (int i = 0; i < numSortedRow; i++) { minHeap.add(new element(matrix[i][0], i, 0)); } int result = 0; // treat a sorted matrix as n sorted lists for (int count = 0; count < k; count++) { element e = minHeap.poll(); result = e.val; if (e.y < n - 1) { minHeap.add(new element(matrix[e.x][e.y + 1], e.x, e.y + 1)); } } return result;}","link":"/2022/01/18/LeetCode-378-Kth-Smallest-Element-in-a-Sorted-Matrix/"},{"title":"LeetCode 39. Combination Sum","text":"QuestionGiven an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order. The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different. It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input. Example 1: 123456Input: candidates = [2,3,6,7], target = 7Output: [[2,2,3],[7]]Explanation:2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.7 is a candidate, and 7 = 7.These are the only two combinations. Example 2: 12Input: candidates = [2,3,5], target = 8Output: [[2,2,2,2],[2,3,3],[3,5]] Example 3: 12Input: candidates = [2], target = 1Output: [] Constraints: 1 <= candidates.length <= 30 1 <= candidates[i] <= 200 All elements of candidates are distinct. 1 <= target <= 500 Source: https://leetcode.com/problems/combination-sum/ SolutionWe are given a distinct candidates array, so we do not need to use a set to avoid duplicates like in LeetCode 47. Permutations II. Just by making elements of every combination in a non-decreasing order, we can get unique combinations. Pre-sorting helps us find valid candidates faster. When the candidates array is sorted, we can pass start position rather than start value to the deeper layer of DFS tree. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859private void dfs(int[] candidates, int start, int target, LinkedList<Integer> current, List<List<Integer>> result) { if (target < 0) { return; } if (target == 0) { List<Integer> copy = new ArrayList<>(current); result.add(copy); return; } for (int candi : candidates) { // combinations in non-decreasing order if (candi >= start) { // backtrace current.addLast(candi); dfs(candidates, candi, target - candi, current, result); current.removeLast(); } }}public List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> comb = new LinkedList<>(); dfs(candidates, 0, target, comb, result); return result;}// improve performance under the same time complexity// through pre-sorting candidates so that we can find valid candidates easilyprivate void dfs2(int[] sortedCandidates, int startPos, int target, LinkedList<Integer> current, List<List<Integer>> result) { if (target < 0) { return; } if (target == 0) { List<Integer> copy = new ArrayList<>(current); result.add(copy); return; } // combinations in non-decreasing order for (int i = startPos; i < sortedCandidates.length; i++) { int candi = sortedCandidates[i]; // backtrace current.addLast(candi); dfs2(sortedCandidates, i, target - candi, current, result); current.removeLast(); }}public List<List<Integer>> combinationSum2(int[] candidates, int target) { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> comb = new LinkedList<>(); Arrays.sort(candidates); dfs2(candidates, 0, target, comb, result); return result;}","link":"/2022/03/06/LeetCode-39-Combination-Sum/"},{"title":"LeetCode 4. Median of Two Sorted Arrays","text":"QuestionGiven two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). Example 1: 123Input: nums1 = [1,3], nums2 = [2]Output: 2.00000Explanation: merged array = [1,2,3] and median is 2. Example 2: 123Input: nums1 = [1,2], nums2 = [3,4]Output: 2.50000Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5. Constraints: nums1.length == m nums2.length == n 0 <= m <= 1000 0 <= n <= 1000 1 <= m + n <= 2000 -106 <= nums1[i], nums2[i] <= 106 Source: https://leetcode.com/problems/median-of-two-sorted-arrays/ SolutionA simple solution is to maintain two pointers for two sorted arrays, and use linear search to find the median. Obviously, it is simple but slow. Because both arrays are sorted, we can use binary search to improve performance. Remember that the essence of binary search is to exclude a certain percentage of candiates from the search range on each round. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253// Time complexity: O(log(m+n)), because k = (m+n)/2public double findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; int lmedian = (m + n + 1) / 2; int rmedian = (m + n + 2) / 2; if (lmedian == rmedian) { return getKth(nums1, nums2, 0, 0, lmedian); } else { return (getKth(nums1, nums2, 0, 0, lmedian) + getKth(nums1, nums2, 0, 0, rmedian)) / 2.0; }}// get the k-th smallest (starts from 1) element in two sorted arrays// start is the start index of search window (inclusive)// @pre: total number of elements in these two arrays is no less than k// Time complexity: O(log(k))private static int getKth(int[] nums1, int[] nums2, int start1, int start2, int k) { int m = nums1.length; int n = nums2.length; if (start1 > m - 1) { // used up nums1 return nums2[start2 + k - 1]; } if (start2 > n - 1) { // used up nums2 return nums1[start1 + k - 1]; } // to make sure k/2 > 0 if (k == 1) { return Math.min(nums1[start1], nums2[start2]); } // the k/2 th element from start // Finding the kth element is a recursive process // So using rounded down k/2 is safe int kmid1 = Integer.MAX_VALUE; int kmid2 = Integer.MAX_VALUE; if (start1 + k / 2 - 1 < m) { // nums1 has k/2 th element from start kmid1 = nums1[start1 + k / 2 - 1]; } if (start2 + k / 2 - 1 < n) { // nums2 has k/2 th element from start kmid2 = nums2[start2 + k / 2 - 1]; } // k/2 elements on the smaller side must be included in the smallest k elements // remove them from further searches if (kmid1 < kmid2) { return getKth(nums1, nums2, start1 + k / 2, start2, k - k / 2); } else { // if two mids are equal, we can remove either k/2 elements return getKth(nums1, nums2, start1, start2 + k / 2, k - k / 2); }}","link":"/2022/03/11/LeetCode-4-Median-of-Two-Sorted-Arrays/"},{"title":"LeetCode 40. Combination Sum II","text":"QuestionGiven a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target. Each number in candidates may only be used once in the combination. Note: The solution set must not contain duplicate combinations. Example 1: 12345678Input: candidates = [10,1,2,7,6,1,5], target = 8Output: [[1,1,6],[1,2,5],[1,7],[2,6]] Example 2: 123456Input: candidates = [2,5,2,1,2], target = 5Output: [[1,2,2],[5]] Constraints: 1 <= candidates.length <= 100 1 <= candidates[i] <= 50 1 <= target <= 30 Source: https://leetcode.com/problems/combination-sum-ii/ SolutionThis time we do not have distinct and reuse these two conveninent features. So we use pre-sorting and set to avoid duplicates. dfs2 provides another way to skip duplicate elements on the same layer. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465// depth of a dfs path is the length of the combination (variable)// pre-sorting makes candidates with the same value be grouped together// we can find valid candidates easilyprivate void dfs(int[] sortedCandidates, int startPos, int target, LinkedList<Integer> current, List<List<Integer>> result) { if (target < 0) { return; } if (target == 0) { List<Integer> copy = new ArrayList<>(current); result.add(copy); return; } Set<Integer> used = new HashSet<>(); // combinations in non-decreasing order for (int i = startPos; i < sortedCandidates.length; i++) { int candi = sortedCandidates[i]; // avoid adding duplicates in the same layer if (used.contains(candi)) { continue; } else { used.add(candi); } // backtrace current.addLast(candi); dfs(sortedCandidates, i + 1, target - candi, current, result); current.removeLast(); }}// another way to avoid duplicatesprivate void dfs2(int[] sortedCandidates, int startPos, int target, LinkedList<Integer> current, List<List<Integer>> result) { if (target < 0) { return; } if (target == 0) { List<Integer> copy = new ArrayList<>(current); result.add(copy); return; } // combinations in non-decreasing order for (int i = startPos; i < sortedCandidates.length; i++) { int candi = sortedCandidates[i]; // avoid adding duplicates in the same layer if (i != startPos && candi == sortedCandidates[i - 1]) { continue; } // backtrace current.addLast(candi); dfs2(sortedCandidates, i + 1, target - candi, current, result); current.removeLast(); }}public List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> comb = new LinkedList<>(); Arrays.sort(candidates); dfs(candidates, 0, target, comb, result); // dfs2(candidates, 0, target, comb, result); return result;}","link":"/2022/03/06/LeetCode-40-Combination-Sum-II/"},{"title":"LeetCode 402. Remove K Digits","text":"QuestionGiven string num representing a non-negative integer num, and an integer k, return the smallest possible integer after removing k digits from num. Example 1: 123Input: num = "1432219", k = 3Output: "1219"Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest. Example 2: 123Input: num = "10200", k = 1Output: "200"Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes. Example 3: 123Input: num = "10", k = 2Output: "0"Explanation: Remove all the digits from the number and it is left with nothing which is 0. Constraints: 1 <= k <= num.length <= 105 num consists of only digits. num does not have any leading zeros except for the zero itself. Source: https://leetcode.com/problems/remove-k-digits/ SolutionWe use the greedy thought. Assume you are given a set of numbers, how to order these numbers to make the value as small as possible? Obviously, we should place small numbers on high digits. In stack, we try to make elements ascending. Before running out of remove operations stack is monotonically ascending, because we keep kicking out large numbers and let small numbers be put on high digits. Note that for Deque, pop() = pollFirst() = removeFirst(), push() = offerFirst() = addFirst(). Because for a linked-list based deque, its head is the top of stack. 12345678910111213141516171819202122232425262728293031323334public String removeKdigits(String num, int k) { if (num == null || num.length() <= k) { return "0"; } StringBuilder builder = new StringBuilder(); Deque<Character> stack = new LinkedList<>(); // from left to right, try our best to make elements ascending for (int i = 0; i < num.length(); i++) { char digit = num.charAt(i); while (k > 0 && !stack.isEmpty() && stack.peek() > digit) { stack.pop(); k--; } stack.push(digit); } // apply unused remove operations // just remove the tail because the stack is ascending while (k > 0) { stack.pop(); k--; } // remove leading zeros while (!stack.isEmpty() && stack.getLast().equals('0')) { stack.pollLast(); } while (!stack.isEmpty()) { builder.append(stack.pop()); } if (builder.length() == 0) { return "0"; } else { return builder.reverse().toString(); }}","link":"/2022/03/12/LeetCode-402-Remove-K-Digits/"},{"title":"LeetCode 41. First Missing Positive","text":"QuestionGiven an unsorted integer array nums, return the smallest missing positive integer. You must implement an algorithm that runs in O(n) time and uses constant extra space. Example 1: 12Input: nums = [1,2,0]Output: 3 Example 2: 12Input: nums = [3,4,-1,1]Output: 2 Example 3: 12Input: nums = [7,8,9,11,12]Output: 1 Constraints: 1 <= nums.length <= 5 * 105 -231 <= nums[i] <= 231 - 1 Source: https://leetcode.com/problems/first-missing-positive/ SolutionWe try to put every positive number n on the position n-1. Then we traverse the array to find the first number that num[i] != i + 1, so that we get the first missing positive number. Note that there may be duplicate values that fit in the same position. We should skip the rest of them to avoid endless loop. 12345678910111213141516171819202122232425262728293031private void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;}public int firstMissingPositive(int[] nums) { int i = 0; // in-place // num should on the num - 1 position // put elements to the correct position if possible while (i < nums.length) { int num = nums[i]; if (num <= 0 || num == i + 1 || num - 1 >= nums.length) { // skip i++; } else if (num == nums[num - 1]) { // multiple elements fit the same position // prevent endless loop i++; } else { // do not update i here swap(nums, i, num - 1); } } i = 0; for (; i < nums.length; i++) { if (nums[i] != i + 1) { break; } } return i + 1;}","link":"/2022/03/09/LeetCode-41-First-Missing-Positive/"},{"title":"LeetCode 435. Non-overlapping Intervals","text":"QuestionGiven an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping. Example 1: 123Input: intervals = [[1,2],[2,3],[3,4],[1,3]]Output: 1Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping. Example 2: 123Input: intervals = [[1,2],[1,2],[1,2]]Output: 2Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping. Example 3: 123Input: intervals = [[1,2],[2,3]]Output: 0Explanation: You don't need to remove any of the intervals since they're already non-overlapping. Constraints: 1 <= intervals.length <= 105 intervals[i].length == 2 -5 * 104 <= starti < endi <= 5 * 104 Source: https://leetcode.com/problems/non-overlapping-intervals/ Solution1234567891011121314151617181920212223242526272829// sort by start, greedy, Time Complexity O(nlog(n))public int eraseOverlapIntervals(int[][] intervals) { if (intervals == null || intervals.length == 0) { return 0; } Arrays.sort(intervals, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { return o1[0] - o2[0]; } }); int numRemove = 0; int prevEnd = intervals[0][1]; for (int i = 1; i < intervals.length; i++) { int[] curr = intervals[i]; if (curr[0] >= prevEnd) { // no overlap prevEnd = curr[1]; } else if (prevEnd < curr[1]) { // intersection overlap // remove current interval // do not change prevEnd numRemove++; } else { // included overlap // remove previous interval numRemove++; prevEnd = curr[1]; } } return numRemove;}","link":"/2022/01/25/LeetCode-435-Non-overlapping-Intervals/"},{"title":"LeetCode 45. Jump Game II","text":"QuestionGiven an array of non-negative integers nums, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Your goal is to reach the last index in the minimum number of jumps. You can assume that you can always reach the last index. Example 1: 123Input: nums = [2,3,1,1,4]Output: 2Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index. Example 2: 12Input: nums = [2,3,0,1,4]Output: 2 Constraints: 1 <= nums.length <= 104 0 <= nums[i] <= 1000 Source: https://leetcode.com/problems/jump-game-ii/ Solution1D DP. dp[i] is the min number of jumps from i to the end. 123456789101112131415161718192021222324252627282930// DP, Time Complexity O(n^2)public int jump(int[] nums) { if (nums == null || nums.length == 0) { return -1; } int len = nums.length; // dp[i] is the min number of jumps from i to the end // if i cannot reach the end, dp[i] should be -1 int[] dp = new int[len]; for (int i = 0; i < len; i++) { dp[i] = -1; } dp[len - 1] = 0; for (int i = len - 2; i >= 0; i--) { int maxStep = nums[i]; int minJump = Integer.MAX_VALUE; boolean isGood = false; for (int k = 0; k <= maxStep && i + k < len; k++) { // if i+k can reach the end if (dp[i + k] != -1) { isGood = true; minJump = Math.min(minJump, dp[i + k] + 1); } } if (isGood) { dp[i] = minJump; } } return dp[0];}","link":"/2022/01/25/LeetCode-45-Jump-Game-II/"},{"title":"LeetCode 454. 4Sum II","text":"QuestionGiven four integer arrays nums1, nums2, nums3, and nums4 all of length n, return the number of tuples (i, j, k, l) such that: 0 <= i, j, k, l < n nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0 Example 1: 123456Input: nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]Output: 2Explanation:The two tuples are:1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 02. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0 Example 2: 12Input: nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]Output: 1 Constraints: n == nums1.length n == nums2.length n == nums3.length n == nums4.length 1 <= n <= 200 -228 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 228 Source: https://leetcode.com/problems/4sum-ii/ SolutionThe two differences of this problem from LeetCode 18. 4Sum are: 4 elements in the tuple are from 4 arrays respectively we do not care about the content of each qualified tuple, but the number of qualified tuples. Thus, we use a double loop to get all pairs between array1 and array2, and all pairs between array3 and array4. Then we traverse the map to calculate the number of qualified combinations. 12345678910111213141516171819202122// the key difference from 4Sum is that indexes in 4 arrays are independentpublic int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { int len = nums1.length; int result = 0; // pair sum -> pair count Map<Integer, Integer> pair12 = new HashMap<>(); Map<Integer, Integer> pair34 = new HashMap<>(); for (int i = 0; i < len; i++) { for (int j = 0; j < len; j++) { int sum12 = nums1[i] + nums2[j]; int sum34 = nums3[i] + nums4[j]; pair12.put(sum12, pair12.getOrDefault(sum12, 0) + 1); pair34.put(sum34, pair34.getOrDefault(sum34, 0) + 1); } } for (int sum12 : pair12.keySet()) { int count12 = pair12.get(sum12); int count34 = pair34.getOrDefault(-sum12, 0); result += count12 * count34; } return result;}","link":"/2022/03/10/LeetCode-454-4Sum-II/"},{"title":"LeetCode 452. Minimum Number of Arrows to Burst Balloons","text":"QuestionThere are some spherical balloons taped onto a flat wall that represents the XY-plane. The balloons are represented as a 2D integer array points where points[i] = [xstart, xend] denotes a balloon whose horizontal diameter stretches between xstart and xend. You do not know the exact y-coordinates of the balloons. Arrows can be shot up directly vertically (in the positive y-direction) from different points along the x-axis. A balloon with xstart and xend is burst by an arrow shot at x if xstart <= x <= xend. There is no limit to the number of arrows that can be shot. A shot arrow keeps traveling up infinitely, bursting any balloons in its path. Given the array points, return the minimum number of arrows that must be shot to burst all balloons. Example 1: 12345Input: points = [[10,16],[2,8],[1,6],[7,12]]Output: 2Explanation: The balloons can be burst by 2 arrows:- Shoot an arrow at x = 6, bursting the balloons [2,8] and [1,6].- Shoot an arrow at x = 11, bursting the balloons [10,16] and [7,12]. Example 2: 123Input: points = [[1,2],[3,4],[5,6],[7,8]]Output: 4Explanation: One arrow needs to be shot for each balloon for a total of 4 arrows. Example 3: 12345Input: points = [[1,2],[2,3],[3,4],[4,5]]Output: 2Explanation: The balloons can be burst by 2 arrows:- Shoot an arrow at x = 2, bursting the balloons [1,2] and [2,3].- Shoot an arrow at x = 4, bursting the balloons [3,4] and [4,5]. Constraints: 1 <= points.length <= 105 points[i].length == 2 -231 <= xstart < xend <= 231 - 1 Source: https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/ Solution12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061// sort by end, greedy, Time Complexity O(nlog(n))public int findMinArrowShots(int[][] points) { if (points == null || points.length == 0) { return 0; } // sort by end in ascending order Arrays.sort(points, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { // in case of overflow if (o1[1] == o2[1]) { return 0; } else if (o1[1] > o2[1]) { return 1; } else { return -1; } } }); int prevEnd = points[0][1]; int numArrow = 1; for (int[] point : points) { if (point[0] > prevEnd) { // no overlap numArrow++; prevEnd = point[1]; } } return numArrow;}// sort by start, greedy, Time Complexity O(nlog(n))public int findMinArrowShots2(int[][] points) { if (points == null || points.length == 0) { return 0; } // sort by start in ascending order Arrays.sort(points, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { // in case of overflow if (o1[0] == o2[0]) { return 0; } else if (o1[0] > o2[0]) { return 1; } else { return -1; } } }); int minEnd = points[0][1]; int numArrow = 1; for (int[] point : points) { if (point[0] > minEnd) { // no overlap numArrow++; minEnd = point[1]; } else { minEnd = Math.min(minEnd, point[1]); } } return numArrow;}","link":"/2022/01/25/LeetCode-452-Minimum-Number-of-Arrows-to-Burst-Balloons/"},{"title":"LeetCode 46. Permutations","text":"QuestionGiven an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. Example 1: 12Input: nums = [1,2,3]Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] Example 2: 12Input: nums = [0,1]Output: [[0,1],[1,0]] Example 3: 12Input: nums = [1]Output: [[1]] Constraints: 1 <= nums.length <= 6 -10 <= nums[i] <= 10 All the integers of nums are unique. Source: https://leetcode.com/problems/permutations/ SolutionA conveninent condition provided by this question is that all elements are distinct. Thus, we do not need to consider duplicates of result. The following solution is based on swap() so that it does not require additional space to store intermediate result of permutations. The idea is similar to selection sort. We treat elements before position as a determined permutation prefix, treat element after position as available elements in future searches. Another point is backtracking. Because all operations are on the same array nums, we need to recover changes of subtrees before diving into another path. A common trick for DFS problems is to draw a DFS tree. 12345678910111213141516171819202122232425262728293031private void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;}// the depth of dfs equals to the length of the array// elements before position are regarded as fixedprivate void dfs(int[] nums, int position, List<List<Integer>> result) { if (position == nums.length - 1) { List<Integer> permutation = new ArrayList<>(); for (int n : nums) { permutation.add(n); } result.add(permutation); } // CANNOT guarantee that result will be de-deduplicated for (int i = position; i < nums.length; i++) { // backtrace swap(nums, position, i); dfs(nums, position + 1, result); swap(nums, position, i); }}public List<List<Integer>> permute(int[] nums) { List<List<Integer>> permutations = new ArrayList<>(); dfs(nums, 0, permutations); return permutations;}","link":"/2022/03/06/LeetCode-46-Permutations/"},{"title":"LeetCode 47. Permutations II","text":"QuestionGiven a collection of numbers, nums, that might contain duplicates, return all possible unique permutations in any order. Example 1: 12345Input: nums = [1,1,2]Output:[[1,1,2], [1,2,1], [2,1,1]] Example 2: 12Input: nums = [1,2,3]Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] Constraints: 1 <= nums.length <= 8 -10 <= nums[i] <= 10 Source: https://leetcode.com/problems/permutations-ii/ SolutionThe following code shows two solutions for this problem. One constructs permutations by swap, the other by append. The idea of swap solution is explained in detail in post LeetCode 46. Permutations. The key point of de-duplication is to avoid traversing duplicate elements on the same DFS layer. By doing so, we can avoid duplicate permutation prefixes. Because constructing permutations is a recursive process, we guarantee that there is no duplicates in the final result. The swap solution achieves this by maintaining a set for each layer. For the append solution, we choose to use a character-count map. And on each layer, we traverse its key set rather than entry set so that we avoid duplicate elements. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273private void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;}// the depth of dfs equals to the length of the array// elements before position are regarded as fixedprivate void dfs(int[] nums, int position, List<List<Integer>> result) { if (position == nums.length - 1) { List<Integer> permutation = new ArrayList<>(); for (int n : nums) { permutation.add(n); } result.add(permutation); } // to store used elements in this layer // if two prefixes are identical, their dfs results must contain duplicates Set<Integer> used = new HashSet<>(); for (int i = position; i < nums.length; i++) { if (!used.contains(nums[i])) { used.add(nums[i]); // backtrace swap(nums, position, i); dfs(nums, position + 1, result); swap(nums, position, i); } }}public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> unique = new ArrayList<>(); dfs(nums, 0, unique); return unique;}// the depth of dfs equals to the length of the array// elements before position are regarded as fixedprivate void dfs2(int length, LinkedList<Integer> current, Map<Integer, Integer> dict, List<List<Integer>> result) { if (current.size() == length) { List<Integer> copy = new ArrayList<>(current); result.add(copy); } // traverse its key set instead of entry set to avoid duplicates for (Integer e : dict.keySet()) { int count = dict.get(e); if (count == 0) { continue; } // backtrace // do not remove key even if its value is 0, so as not to interfere map traversal current.addLast(e); dict.put(e, count - 1); dfs2(length, current, dict, result); current.removeLast(); dict.put(e, count); }}public List<List<Integer>> permuteUnique2(int[] nums) { List<List<Integer>> unique = new ArrayList<>(); Map<Integer, Integer> map = new HashMap<>(); LinkedList<Integer> permute = new LinkedList<>(); for (int n : nums) { map.put(n, map.getOrDefault(n, 0) + 1); } dfs2(nums.length, permute, map, unique); return unique;}","link":"/2022/03/06/LeetCode-47-Permutations-II/"},{"title":"LeetCode 528. Random Pick with Weight","text":"QuestionYou are given a 0-indexed array of positive integers w where w[i] describes the weight of the ith index. You need to implement the function pickIndex(), which randomly picks an index in the range [0, w.length - 1] (inclusive) and returns it. The probability of picking an index i is w[i] / sum(w). For example, if w = [1, 3], the probability of picking index 0 is 1 / (1 + 3) = 0.25 (i.e., 25%), and the probability of picking index 1 is 3 / (1 + 3) = 0.75 (i.e., 75%). Constraints: 1 <= w.length <= 104 1 <= w[i] <= 105 pickIndex will be called at most 104 times. Source: https://leetcode.com/problems/random-pick-with-weight/ SolutionIn theory, we create several intervals from the weight array. Then generate a random value. In which interval the random value falls, the corresponding index is returned. The key point is how to determine which interval the random value falls. We use binary search so that the time complexity is O(log(n)). In Java, TreeMap is implemented based on Red-Black tree, a kind of self-balancing binary search tree. Thus, we can also use TreeMap to get the same time complexity. class RandomPickWithWeight Use TreeMap to store lower bounds. 123456789101112131415161718192021// lower bound -> indexprivate TreeMap<Integer, Integer> treeMap;private Random rand;private int scope;public RandomPickWithWeight(int[] w) { treeMap = new TreeMap<>(); rand = new Random(System.currentTimeMillis()); int lowerBound = 0; for (int i = 0; i < w.length; i++) { treeMap.put(lowerBound, i); lowerBound += w[i]; } scope = lowerBound;}public int pickIndex() { // avoid negative random number int r = Math.abs(rand.nextInt() % scope); return treeMap.floorEntry(r).getValue();} Use prefix sum and binary search. Prefix sum array represents upper bounds. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465private Random rand;private int scope;// upper bounds, (,]private int[] prefixSums;public RandomPickWithWeightAlt1(int[] w) { rand = new Random(System.currentTimeMillis()); prefixSums = new int[w.length]; prefixSums[0] = w[0]; for (int i = 1; i < w.length; i++) { prefixSums[i] = prefixSums[i - 1] + w[i]; } scope = prefixSums[w.length - 1];}public int pickIndex() { // nextInt(n) returns a random number in [0,n) int r = rand.nextInt(scope) + 1; int len = prefixSums.length; // both ends of the search window are inclusive int left = 0, right = len - 1; // mid is rounded to the floor, so left must make progress while (left < right) { int mid = left + (right - left) / 2; // prevent overflow if (r == prefixSums[mid]) { return mid; } else if (r < prefixSums[mid]) { right = mid; } else { left = mid + 1; } } // left and right should be the same here // but using right is easier to understand return right;}public int pickIndex2() { // nextInt(n) returns a random number in [0,n) int r = rand.nextInt(scope) + 1; int len = prefixSums.length; // both ends of the search window are inclusive int left = 0, right = len - 1; // another implementation of binary search // avoid the case that left == mid while (left < right - 1) { int mid = left + (right - left) / 2; // prevent overflow if (r == prefixSums[mid]) { return mid; } else if (r < prefixSums[mid]) { right = mid; } else { left = mid + 1; } } // out of the loop, left == right or left are adjacent with right if (r <= prefixSums[left] && r <= prefixSums[right]) { return left; } else { return right; }}","link":"/2022/03/09/LeetCode-528-Random-Pick-with-Weight/"},{"title":"LeetCode 53. Maximum Subarray","text":"QuestionGiven an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum. A subarray is a contiguous part of an array. Example 1: 123Input: nums = [-2,1,-3,4,-1,2,1,-5,4]Output: 6Explanation: [4,-1,2,1] has the largest sum = 6. Example 2: 12Input: nums = [1]Output: 1 Example 3: 12Input: nums = [5,4,-1,7,8]Output: 23 Constraints: 1 <= nums.length <= 105 -104 <= nums[i] <= 104 Source: https://leetcode.com/problems/maximum-subarray/ SolutionFor subarray sum problems, prefix sum is a powerful tool. The sum of any subarray can be calculated by two prefix sums. And for a subarray ending with index i, its sum must be the difference between prefixSums[i] and the min prefix sum before i. The idea is similar to LeetCode 121. Best Time to Buy and Sell Stock. 1234567891011121314151617public int maxSubArray(int[] nums) { int len = nums.length; // prefixSums[i] represents the sum of nums[0, i) int[] prefixSums = new int[len + 1]; prefixSums[0] = 0; for (int i = 0; i < len; i++) { prefixSums[i + 1] = prefixSums[i] + nums[i]; } int minPrefix = 0; int maxSubarray = Integer.MIN_VALUE; for (int i = 1; i <= len; i++) { maxSubarray = Math.max(maxSubarray, prefixSums[i] - minPrefix); minPrefix = Math.min(minPrefix, prefixSums[i]); } return maxSubarray;}","link":"/2022/03/11/LeetCode-53-Maximum-Subarray/"},{"title":"LeetCode 56. Merge Intervals","text":"QuestionGiven an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. Example 1: 123Input: intervals = [[1,3],[2,6],[8,10],[15,18]]Output: [[1,6],[8,10],[15,18]]Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6]. Example 2: 123Input: intervals = [[1,4],[4,5]]Output: [[1,5]]Explanation: Intervals [1,4] and [4,5] are considered overlapping. Constraints: 1 <= intervals.length <= 104 intervals[i].length == 2 0 <= starti <= endi <= 104 Source: https://leetcode.com/problems/merge-intervals/ SolutionFor interval problems, sorting the interval array by start is usually a cracking direction. For sorted interval array, if prev.end < curr.start, there is no overlap between these two intervals Otherwise, they are overlapped. 12345678910111213141516171819202122public int[][] merge(int[][] intervals) { // sort by the start in ascending order Arrays.sort(intervals, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { return o1[0] - o2[0]; } }); Deque<int[]> merged = new LinkedList<>(); merged.add(intervals[0]); for (int[] interval : intervals) { int[] last = merged.getLast(); if (last[1] < interval[0]) { merged.addLast(interval); } else { // merge last[1] = Math.max(last[1], interval[1]); } } int[][] result = new int[merged.size()][]; merged.toArray(result); return result;}","link":"/2022/01/12/LeetCode-56-Merge-Intervals/"},{"title":"LeetCode 57. Insert Interval","text":"QuestionYou are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval. Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if necessary). Return intervals after the insertion. Example 1: 12Input: intervals = [[1,3],[6,9]], newInterval = [2,5]Output: [[1,5],[6,9]] Example 2: 123Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]Output: [[1,2],[3,10],[12,16]]Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10]. Constraints: 0 <= intervals.length <= 104 intervals[i].length == 2 0 <= starti <= endi <= 105 intervals is sorted by starti in ascending order. newInterval.length == 2 0 <= start <= end <= 105 Source: https://leetcode.com/problems/insert-interval/ Solution1234567891011121314151617181920212223242526272829303132333435public int[][] insert(int[][] intervals, int[] newInterval) { int start = newInterval[0]; int end = newInterval[1]; int index = 0; int len = intervals.length; Deque<int[]> merged = new LinkedList<>(); // no overlap while (index < len) { if (intervals[index][1] < start) { merged.addLast(intervals[index]); index++; } else { break; } } // merge while (index < len) { if (intervals[index][0] <= end) { start = Math.min(start, intervals[index][0]); end = Math.max(end, intervals[index][1]); index++; } else { break; } } merged.addLast(new int[]{start, end}); // no overlap while (index < len) { merged.addLast(intervals[index]); index++; } int[][] result = new int[merged.size()][]; merged.toArray(result); return result;}","link":"/2022/01/25/LeetCode-57-Insert-Interval/"},{"title":"LeetCode 55. Jump Game","text":"QuestionYou are given an integer array nums. You are initially positioned at the array’s first index, and each element in the array represents your maximum jump length at that position. Return true if you can reach the last index, or false otherwise. Example 1: 123Input: nums = [2,3,1,1,4]Output: trueExplanation: Jump 1 step from index 0 to 1, then 3 steps to the last index. Example 2: 123Input: nums = [3,2,1,0,4]Output: falseExplanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index. Constraints: 1 <= nums.length <= 104 0 <= nums[i] <= 105 Source: https://leetcode.com/problems/jump-game/ SolutionThe optimization idea is quite similar to finding Fibonacci number. For the current point, we only care about whether we can get to a point that can reach the end. Thus, we only need to maintain the index of left most point that can reach the end. 12345678910111213141516171819202122232425262728293031323334353637383940// DP, Time Complexity O(n^2)public boolean canJump(int[] nums) { if (nums == null || nums.length == 0) { return false; } int len = nums.length; // dp[i] represents the quality of index i. // 1, -1, 0 means can reach the end, cannot reach the end and unknown respectively // in fact, a visited index with value of 0 can also be regarded as -1 int[] dp = new int[len]; dp[len - 1] = 1; for (int i = len - 2; i >= 0; i--) { int maxStep = nums[i]; for (int k = 0; k <= maxStep && i + k < len; k++) { if (dp[i + k] == 1) { dp[i] = 1; break; } } } return dp[0] == 1;}// optimized DP, Time Complexity O(n)public boolean canJump2(int[] nums) { if (nums == null || nums.length == 0) { return false; } int len = nums.length; // in the DP solution, we only use the left most index that can reach the end int leftMostGood = len - 1; for (int i = len - 2; i >= 0; i--) { if (i + nums[i] >= leftMostGood) { // i is a good index leftMostGood = i; } } // whether index 0 is a good index return leftMostGood == 0;}","link":"/2022/01/25/LeetCode-55-Jump-Game/"},{"title":"LeetCode 60. Permutation Sequence","text":"QuestionThe set [1, 2, 3, ..., n] contains a total of n! unique permutations. By listing and labeling all of the permutations in order, we get the following sequence for n = 3: "123" "132" "213" "231" "312" "321" Given n and k, return the kth permutation sequence. Example 1: 12Input: n = 3, k = 3Output: "213" Example 2: 12Input: n = 4, k = 9Output: "2314" Example 3: 12Input: n = 3, k = 1Output: "123" Constraints: 1 <= n <= 9 1 <= k <= n! Source: https://leetcode.com/problems/permutation-sequence/ SolutionWe find the result digit by digit. For each digit, we can determine its value by dividing k by the number of permutations of its suffix. By subtracting the number of skipped permutations from k, we keep narrowing k till we find a certain permutation. Note that in each loop we need to removed current digit because we cannot reuse numbers. Also, permutations are in a lexicographic order, so we should keep nums sorted. Remove operations do not break the order of nums. 12345678910111213141516171819202122232425262728// Time Complexity, O(n)public String getPermutation(int n, int k) { StringBuilder result = new StringBuilder(); List<Integer> nums = new ArrayList<>(); for (int i = 1; i <= n; i++) { nums.add(i); } // [0!, 1!, ..., (n-1)!] int[] factorials = new int[n]; factorials[0] = 1; for (int i = 1; i < n; i++) { factorials[i] = i * factorials[i - 1]; } // make k start from 0, // so that the result of k / factorials[n - pos - 1] has the same meaning as an index of nums k -= 1; // [1 (2 3 4 5)] (n-1)!=4! // [3 5 4 (1 2)] (n-3)!=2! // [3 5 4 2 1()] (n-5)!=0! for (int pos = 0; pos < n; pos++) { int index = k / factorials[n - pos - 1]; // skip suffix permutations as much as possible result.append(nums.get(index)); nums.remove(index); // removing elements does not break the order of the list k -= index * factorials[n - pos - 1]; } return result.toString();}","link":"/2022/03/06/LeetCode-60-Permutation-Sequence/"},{"title":"LeetCode 62. Unique Paths","text":"QuestionThere is a robot on an m x n grid. The robot is initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time. Given the two integers m and n, return the number of possible unique paths that the robot can take to reach the bottom-right corner. The test cases are generated so that the answer will be less than or equal to 2 * 109. Example 1: 12Input: m = 3, n = 7Output: 28 Example 2: 123456Input: m = 3, n = 2Output: 3Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:1. Right -> Down -> Down2. Down -> Down -> Right3. Down -> Right -> Down Constraints: 1 <= m, n <= 100 Source: https://leetcode.com/problems/unique-paths/ Solution12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849// Time Complexity O(mn), Space Complexity O(mn)public int uniquePaths(int m, int n) { if (m == 1 || n == 1) { return 1; } // dp[i][j] represents the number of unique paths from (0,0) to (i,j) int[][] dp = new int[m][n]; for (int i = 0; i < m; i++) { dp[i][0] = 1; } for (int j = 0; j < n; j++) { dp[0][j] = 1; } for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { // one grid can only be reached from top or left dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; } } return dp[m - 1][n - 1];}// Time Complexity: O(mn), Space Complexity: O(n)// space complexity can be further optimized to O(min(m, n)) at the expense of readabilitypublic int uniquePaths2(int m, int n) { if (m == 1 || n == 1) { return 1; } if (n > m) { return uniquePaths2(n, m); } // dp[j] represents the number of unique paths from (0,0) to (i,j) // i is the round number when dp[j] is written // the optimization direction is similar to fibonacci // we only use the last row of 2D dp array, so we can replace it with a 1D dp array int[] dp = new int[n]; for (int j = 0; j < n; j++) { dp[j] = 1; } for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { dp[j] = dp[j - 1] + dp[j]; } } return dp[n - 1];}","link":"/2022/03/11/LeetCode-62-Unique-Paths/"},{"title":"LeetCode 616. Add Bold Tag in String","text":"QuestionYou are given a string s and an array of strings words. You should add a closed pair of bold tag <b> and </b> to wrap the substrings in s that exist in words. If two such substrings overlap, you should wrap them together with only one pair of closed bold-tag. If two substrings wrapped by bold tags are consecutive, you should combine them. Return s after adding the bold tags. Example 1: 12Input: s = "abcxyz123", words = ["abc","123"]Output: "<b>abc</b>xyz<b>123</b>" Example 2: 12Input: s = "aaabbcc", words = ["aaa","aab","bc"]Output: "<b>aaabbc</b>c" Constraints: 1 <= s.length <= 1000 0 <= words.length <= 100 1 <= words[i].length <= 1000 s and words[i] consist of English letters and digits. All the values of words are unique. Source: https://leetcode.com/problems/add-bold-tag-in-string/ SolutionConvert this problem to a merge interval problem. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859class Interval { public int start; // inclusive public int end; // exclusive public Interval(int s, int e) { this.start = s; this.end = e; }}private List<Interval> mergeIntervals(List<Interval> intervals) { if (intervals == null || intervals.size() == 0) { return new LinkedList<Interval>(); } // sort intervals by start in ascending order Collections.sort(intervals, new Comparator<Interval>() { @Override public int compare(Interval o1, Interval o2) { return o1.start - o2.start; } }); LinkedList<Interval> merged = new LinkedList<>(); int size = intervals.size(); merged.add(intervals.get(0)); for (int i = 1; i < size; i++) { Interval curr = intervals.get(i); Interval last = merged.getLast(); if (curr.start > last.end) { // no overlap merged.add(curr); } else { last.end = Math.max(last.end, curr.end); } } return merged;}public String addBoldTag(String s, String[] words) { List<Interval> intervals = new ArrayList<>(); for (String pattern : words) { int plen = pattern.length(); int index = s.indexOf(pattern, -1); while (index != -1) { intervals.add(new Interval(index, index + plen)); index = s.indexOf(pattern, index + 1); } } List<Interval> merged = mergeIntervals(intervals); StringBuilder builder = new StringBuilder(); int prevEnd = 0; for (Interval interval : merged) { builder.append(s.substring(prevEnd, interval.start)); builder.append("<b>"); builder.append(s.substring(interval.start, interval.end)); builder.append("</b>"); prevEnd = interval.end; } builder.append(s.substring(prevEnd, s.length())); return builder.toString();}","link":"/2022/01/25/LeetCode-616-Add-Bold-Tag-in-String/"},{"title":"LeetCode 63. Unique Paths II","text":"QuestionA robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below). The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below). Now consider if some obstacles are added to the grids. How many unique paths would there be? An obstacle and space is marked as 1 and 0 respectively in the grid. Example 1: 123456Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]Output: 2Explanation: There is one obstacle in the middle of the 3x3 grid above.There are two ways to reach the bottom-right corner:1. Right -> Right -> Down -> Down2. Down -> Down -> Right -> Right Example 2: 12Input: obstacleGrid = [[0,1],[0,0]]Output: 1 Constraints: m == obstacleGrid.length n == obstacleGrid[i].length 1 <= m, n <= 100 obstacleGrid[i][j] is 0 or 1. Source: https://leetcode.com/problems/unique-paths-ii/ SolutionFor grids occupied by obstacles, we set their number of unique paths to zero. Note that when initializing the first row and column of the DP array, an obstacle will make itself and all following grids unreachable. 12345678910111213141516171819202122232425262728293031323334353637383940// Time Complexity O(mn), Space Complexity O(mn)public int uniquePathsWithObstacles(int[][] obstacleGrid) { int m = obstacleGrid.length; int n = obstacleGrid[0].length; // dp[i][j] represents the number of unique paths from (0,0) to (i,j) int[][] dp = new int[m][n]; boolean blocked = false; for (int i = 0; i < m; i++) { if (obstacleGrid[i][0] == 1) { blocked = true; } if (blocked) { dp[i][0] = 0; } else { dp[i][0] = 1; } } blocked = false; for (int j = 0; j < n; j++) { if (obstacleGrid[0][j] == 1) { blocked = true; } if (blocked) { dp[0][j] = 0; } else { dp[0][j] = 1; } } for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { if (obstacleGrid[i][j] == 1) { dp[i][j] = 0; } else { dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; } } } return dp[m - 1][n - 1];}","link":"/2022/03/11/LeetCode-63-Unique-Paths-II/"},{"title":"LeetCode 696. Count Binary Substrings","text":"QuestionGive a binary string s, return the number of non-empty substrings that have the same number of 0‘s and 1‘s, and all the 0‘s and all the 1‘s in these substrings are grouped consecutively. Substrings that occur multiple times are counted the number of times they occur. Example 1: 12345Input: s = "00110011"Output: 6Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01".Notice that some of these substrings repeat and are counted the number of times they occur.Also, "00110011" is not a valid substring because all the 0's (and 1's) are not grouped together. Example 2: 123Input: s = "10101"Output: 4Explanation: There are 4 substrings: "10", "01", "10", "01" that have equal number of consecutive 1's and 0's. Constraints: 1 <= s.length <= 105 s[i] is either '0' or '1'. Source: https://leetcode.com/problems/count-binary-substrings/ SolutionRepresent the string as groups of continuous 1s and 0s. The question requires “substrings”, which greatly simplifies the solution since characters in a substring must be continuous in the original string. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152// group// Time Complexity: O(N), Space Complexity: O(N)public int countBinarySubstrings(String s) { // parameter validation if (s == null || s.length() < 1) { return 0; } int len = s.length(); int[] group = new int[len]; int lastGroupIndex = 0; group[0] = 1; // get 0,1 distribution // [3, 2, 4, 1] for "1110011110" for (int i = 1; i < len; i++) { if (s.charAt(i - 1) != s.charAt(i)) { lastGroupIndex++; group[lastGroupIndex] = 1; } else { group[lastGroupIndex]++; } } int result = 0; for (int i = 1; i <= lastGroupIndex; i++) { result += Math.min(group[i - 1], group[i]); } return result;}// space optimized// like Fibonacci sequence// Time Complexity: O(N), Space Complexity: O(1)public int countBinarySubstrings2(String s) { // parameter validation if (s == null || s.length() < 1) { return 0; } int len = s.length(); int result = 0, lastGroupNum = 0, currGroupNum = 1; for (int i = 1; i < len; i++) { if (s.charAt(i - 1) != s.charAt(i)) { // update result at the start of a new group result += Math.min(lastGroupNum, currGroupNum); lastGroupNum = currGroupNum; currGroupNum = 1; } else { currGroupNum++; } } // handle corner case result += Math.min(lastGroupNum, currGroupNum); return result;}","link":"/2021/12/29/LeetCode-696-Count-Binary-Substrings/"},{"title":"LeetCode 718. Maximum Length of Repeated Subarray","text":"QuestionGiven two integer arrays nums1 and nums2, return the maximum length of a subarray that appears in both arrays. Example 1: 123Input: nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]Output: 3Explanation: The repeated subarray with maximum length is [3,2,1]. Example 2: 12Input: nums1 = [0,0,0,0,0], nums2 = [0,0,0,0,0]Output: 5 Constraints: 1 <= nums1.length, nums2.length <= 1000 0 <= nums1[i], nums2[i] <= 100 Source: https://leetcode.com/problems/maximum-length-of-repeated-subarray/ SolutionThe key idea of the transition equation is that a long common string (array) must contain short common strings (arrays). Thus, a long common string can be grown from a short common string. We simplify the searching process by searching common prefix num[i:], so that we only need to move one end of the string (array). Also, common suffix has the same effect. 1234567891011121314151617181920// DP, prefixpublic int findLength(int[] nums1, int[] nums2) { int len1 = nums1.length; int len2 = nums2.length; int maxCommonLen = 0; // dp[i][j] is the max length of common prefix of nums1[i:] and nums2[j:] // elements on the redundant row and column are initialized to zero, simplify the implementation int[][] dp = new int[len1 + 1][len2 + 1]; for (int i = len1 - 1; i >= 0; i--) { for (int j = len2 - 1; j >= 0; j--) { if (nums1[i] == nums2[j]) { dp[i][j] = dp[i + 1][j + 1] + 1; } else { dp[i][j] = 0; } maxCommonLen = Math.max(maxCommonLen, dp[i][j]); } } return maxCommonLen;}","link":"/2022/01/08/LeetCode-718-Maximum-Length-of-Repeated-Subarray/"},{"title":"LeetCode 727. Minimum Window Subsequence","text":"QuestionGiven strings s1 and s2, return the minimum contiguous substring part of s1, so that s2 is a subsequence of the part. If there is no such window in s1 that covers all characters in s2, return the empty string "". If there are multiple such minimum-length windows, return the one with the left-most starting index. Example 1: 12345Input: s1 = "abcdebdde", s2 = "bde"Output: "bcde"Explanation: "bcde" is the answer because it occurs before "bdde" which has the same length."deb" is not a smaller window because the elements of s2 in the window must occur in order. Example 2: 12Input: s1 = "jmeqksfrsdcmsiwvaovztaqenprpvnbstl", s2 = "u"Output: "" Constraints: 1 <= s1.length <= 2 * 104 1 <= s2.length <= 100 s1 and s2 consist of lowercase English letters. Source: https://leetcode.com/problems/minimum-window-subsequence/ SolutionA classic sliding window problem. The main idea is to maintain two pointers for two ends of current window. We first move right forward (expand the window) to find a qualified window, but note that this window may not be the leftmost min window. Then, we move left forward (shrink the window) to minimize current window. In this problem, when searching a subsequence, we need to care about both character occurrences and character order. A traditional thought is to maintain thest states so that we are able to know whether the window is still qualified after each shrinking step. However, though occurrences can be recorded easily in a map, it is hard to describe the order of characters. This makes it complex to maintain content states of the window. Thus, we do not maintain the content states of subsequences. Instead, when shrinking the window, we do not scan the window from left to right, but right to left. Actually, it would be more appropriate to say that we find a minimized window in a qualified window from left to right. Note that the next search after a shrink, should start from one step forward to the old left. Because only by doing so, we ensure the completeness of the search. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849// s1 original string, s2 patternpublic String minWindow(String s1, String s2) { int slen1 = s1.length(); int slen2 = s2.length(); // two ends of the window, both inclusive int left = 0, right = 0; // pattern index to be matched, can be reset multiple times during the search int pIndex = 0; int minWindowLen = slen1 + 1; // start index of left-most min window int minWindowStart = 0; while (left < slen1 && right < slen1) { if (s1.charAt(right) == s2.charAt(pIndex)) { if (pIndex == slen2 - 1) { // has included the whole pattern, // try to shrink the window int leftShrink = right; int pIndexShrink = slen2 - 1; // scan current window from right to left while (pIndexShrink >= 0) { if (s1.charAt(leftShrink) == s2.charAt(pIndexShrink)) { pIndexShrink--; } leftShrink--; } int currentSize = right - leftShrink; // only update min window states when current size is smaller // in order to get the leftmost min window if (currentSize < minWindowLen) { minWindowLen = currentSize; minWindowStart = leftShrink + 1; } // reset pattern index and start next window search pIndex = 0; // ensure the completeness of the search. left = leftShrink + 2; right = left; // TODO: blog } else { // move forward pattern index pIndex++; right++; } } else { // expand the window right++; } } return minWindowLen == slen1 + 1 ? "" : s1.substring(minWindowStart, minWindowStart + minWindowLen);}","link":"/2022/03/12/LeetCode-727-Minimum-Window-Subsequence/"},{"title":"LeetCode 759. Employee Free Time","text":"QuestionWe are given a list schedule of employees, which represents the working time for each employee. Each employee has a list of non-overlapping Intervals, and these intervals are in sorted order. Return the list of finite intervals representing common, positive-length free time for all employees, also in sorted order. (Even though we are representing Intervals in the form [x, y], the objects inside are Intervals, not lists or arrays. For example, schedule[0][0].start = 1, schedule[0][0].end = 2, and schedule[0][0][0] is not defined). Also, we wouldn’t include intervals like [5, 5] in our answer, as they have zero length. Example 1: 12345Input: schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]]Output: [[3,4]]Explanation: There are a total of three employees, and all commonfree time intervals would be [-inf, 1], [3, 4], [10, inf].We discard any intervals that contain inf as they aren't finite. Example 2: 12Input: schedule = [[[1,3],[6,7]],[[2,4]],[[2,5],[9,12]]]Output: [[5,6],[7,9]] Constraints: 1 <= schedule.length , schedule[i].length <= 50 0 <= schedule[i].start < schedule[i].end <= 10^8 Source: https://leetcode.com/problems/employee-free-time/ SolutionWhat we need to return is the common free times for all employees. Thus, the identity of employees does not matter. We can flatten schedule into a 1D list and sort it by start. Then we can solve this problem with the same idea as LeetCode 56. Merge Intervals. 1234567891011121314151617181920212223242526272829303132333435363738394041424344class Interval { public int start; public int end; public Interval() { } public Interval(int _start, int _end) { start = _start; end = _end; }}public List<Interval> employeeFreeTime(List<List<Interval>> schedule) { // contains all intervals List<Interval> timeline = new ArrayList<>(); for (List<Interval> list : schedule) { timeline.addAll(list); } // sort by start in ascending order Collections.sort(timeline, new Comparator<Interval>() { @Override public int compare(Interval o1, Interval o2) { return o1.start - o2.start; } }); int len = timeline.size(); List<Interval> result = new ArrayList<>(); if (len == 0) { return result; } int prevEnd = timeline.get(0).end; // each common interval is defined by the largest previous end and the smallest next start for (int i = 1; i < len; i++) { Interval curr = timeline.get(i); if (curr.start > prevEnd) { // no overlap result.add(new Interval(prevEnd, curr.start)); prevEnd = curr.end; } else { // overlap prevEnd = Math.max(prevEnd, curr.end); } } return result;}","link":"/2022/01/25/LeetCode-759-Employee-Free-Time/"},{"title":"LeetCode 76. Minimum Window Substring","text":"QuestionGiven two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return the empty string "". The testcases will be generated such that the answer is unique. A substring is a contiguous sequence of characters within the string. Example 1: 123Input: s = "ADOBECODEBANC", t = "ABC"Output: "BANC"Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t. Example 2: 123Input: s = "a", t = "a"Output: "a"Explanation: The entire string s is the minimum window. Example 3: 1234Input: s = "a", t = "aa"Output: ""Explanation: Both 'a's from t must be included in the window.Since the largest window of s only has one 'a', return empty string. Constraints: m == s.length n == t.length 1 <= m, n <= 105 s and t consist of uppercase and lowercase English letters. Source: https://leetcode.com/problems/minimum-window-substring/ SolutionWe first expand the right end of the window to find a valid window. Then we shrink the left end to find a candidate min window. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849// sliding window, Time complexity O(s.length() + t.length())public String minWindow(String s, String t) { int tLen = t.length(); int sLen = s.length(); int minWindowLen = sLen + 1; // two ends of the window with min length, both inclusive int minWindowLeft = 0, minWindowRight = 0; int left = 0, right = 0; // the dictionary that describes characters to be included in desired window Map<Character, Integer> dict = new HashMap<>(); Map<Character, Integer> wordCount = new HashMap<>(); for (int i = 0; i < tLen; i++) { char c = t.charAt(i); dict.put(c, dict.getOrDefault(c, 0) + 1); } // number of requirements to meet int toMeet = dict.size(); while (left < sLen && right < sLen) { char c = s.charAt(right); if (dict.containsKey(c)) { wordCount.put(c, wordCount.getOrDefault(c, 0) + 1); if (wordCount.get(c).intValue() == dict.get(c).intValue()) { toMeet--; } // valid window while (toMeet == 0) { // check and record current min window if (right - left + 1 < minWindowLen) { minWindowLen = right - left + 1; minWindowLeft = left; minWindowRight = right; } // shrink window char cRemove = s.charAt(left); if (dict.containsKey(cRemove)) { wordCount.put(cRemove, wordCount.get(cRemove) - 1); if (wordCount.get(cRemove) < dict.get(cRemove)) { toMeet++; } } left++; } } // invalid window, expand right end right++; } return minWindowLen == sLen + 1 ? "" : s.substring(minWindowLeft, minWindowRight + 1);}","link":"/2022/01/09/LeetCode-76-Minimum-Window-Substring/"},{"title":"LeetCode 77. Combinations","text":"QuestionGiven two integers n and k, return all possible combinations of k numbers out of the range [1, n]. You may return the answer in any order. Example 1: 12345678910Input: n = 4, k = 2Output:[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4],] Example 2: 12Input: n = 1, k = 1Output: [[1]] Constraints: 1 <= n <= 20 1 <= k <= n Source: https://leetcode.com/problems/combinations/ SolutionThe following code is based on the append solution of LeetCode 47. Permutations II. Why not swap-based solution? Because available elements in this question are not a fixed set but can be different subsets of [1, n]. Because combinations do not care about order, we can get combinations by restricting permutations to an ascending order. It is ok if the value on current position is too large to leave enough space for deeper layers. DFS paths just safely return to last layer before reach kth layer (leave layer). 12345678910111213141516171819202122232425// start is the smallest valid value for current positionprivate void dfs(int n, int k, int start, LinkedList<Integer> current, List<List<Integer>> result) { if (current.size() == k) { List<Integer> copy = new ArrayList<>(current); result.add(copy); return; } // combinations do not care about order // we can get combinations by restricting permutations to an ascending order // it is ok if the value on current position is too large to leave enough space for deeper layers // dfs paths just safely return to last layer before reach kth layer (leave layer) for (int i = start; i <= n; i++) { current.addLast(i); dfs(n, k, i + 1, current, result); current.removeLast(); }}public List<List<Integer>> combine(int n, int k) { LinkedList<Integer> comb = new LinkedList<>(); List<List<Integer>> combines = new ArrayList<>(); dfs(n, k, 1, comb, combines); return combines;}","link":"/2022/03/06/LeetCode-77-Combinations/"},{"title":"LeetCode 785. Is Graph Bipartite?","text":"QuestionThere is an undirected graph with n nodes, where each node is numbered between 0 and n - 1. You are given a 2D array graph, where graph[u] is an array of nodes that node u is adjacent to. More formally, for each v in graph[u], there is an undirected edge between node u and node v. The graph has the following properties: There are no self-edges (graph[u] does not contain u). There are no parallel edges (graph[u] does not contain duplicate values). If v is in graph[u], then u is in graph[v] (the graph is undirected). The graph may not be connected, meaning there may be two nodes u and v such that there is no path between them. A graph is bipartite if the nodes can be partitioned into two independent sets A and B such that every edge in the graph connects a node in set A and a node in set B. Return true if and only if it is bipartite. Example 1: 123Input: graph = [[1,2,3],[0,2],[0,1,3],[0,2]]Output: falseExplanation: There is no way to partition the nodes into two independent sets such that every edge connects a node in one and a node in the other. Example 2: 123Input: graph = [[1,3],[0,2],[1,3],[0,2]]Output: trueExplanation: We can partition the nodes into two sets: {0, 2} and {1, 3}. Constraints: graph.length == n 1 <= n <= 100 0 <= graph[u].length < n 0 <= graph[u][i] <= n - 1 graph[u] does not contain u. All the values of graph[u] are unique. If graph[u] contains v, then graph[v] contains u. Source: https://leetcode.com/problems/is-graph-bipartite/ SolutionA graph is bipartite means that for each node in that graph, its neighbors and itself should be in two different groups. 123456789101112131415161718192021222324252627282930313233// BFS + coloring, Time Complexity O(|V|+|E|)public boolean isBipartite(int[][] graph) { // number of nodes int N = graph.length; // node ID -> color, 1 red, -1 blue Map<Integer, Integer> colorMap = new HashMap<>(); // queue for bfs Queue<Integer> queue = new LinkedList<>(); for (int id = 0; id < N; id++) { // to handle forest if (!colorMap.containsKey(id)) { colorMap.put(id, 1); // group to red by default // BFS queue.add(id); while (!queue.isEmpty()) { int tid = queue.poll(); int tcolor = colorMap.get(tid); for (int nid : graph[tid]) { // visit neighbors if (colorMap.containsKey(nid)) { int ncolor = colorMap.get(nid); if (ncolor == tcolor) { return false; } } else { // color the neighbor with the opposite color colorMap.put(nid, -tcolor); queue.add(nid); } } } } } return true;}","link":"/2022/01/25/LeetCode-785-Is-Graph-Bipartite/"},{"title":"LeetCode 90. Subsets II","text":"QuestionGiven an integer array nums that may contain duplicates, return all possible subsets (the power set). The solution set must not contain duplicate subsets. Return the solution in any order. Example 1: 12Input: nums = [1,2,2]Output: [[],[1],[1,2],[1,2,2],[2],[2,2]] Example 2: 12Input: nums = [0]Output: [[],[0]] Constraints: 1 <= nums.length <= 10 -10 <= nums[i] <= 10 Source: https://leetcode.com/problems/subsets-ii/ SolutionSet is a kind of combination without constraint of length. The given array may contain duplicates. So we pre-sort nums, which enables us to conveninently skip duplicate elements on DFS layers. 123456789101112131415161718192021222324252627282930private void dfs(int[] sortedNums, int startPos, LinkedList<Integer> current, List<List<Integer>> result) { if (startPos == sortedNums.length) { List<Integer> copy = new ArrayList<>(current); result.add(copy); return; } // do not add any more elements // regarded as a subset dfs(sortedNums, sortedNums.length, current, result); for (int i = startPos; i < sortedNums.length; i++) { int n = sortedNums[i]; // skip used elements in this layer if (i != startPos && sortedNums[i] == sortedNums[i - 1]) { continue; } // backtrace current.addLast(n); dfs(sortedNums, i + 1, current, result); current.removeLast(); }}public List<List<Integer>> subsetsWithDup(int[] nums) { List<List<Integer>> subsets = new ArrayList<>(); LinkedList<Integer> subset = new LinkedList<>(); Arrays.sort(nums); dfs(nums, 0, subset, subsets); return subsets;}","link":"/2022/03/06/LeetCode-90-Subsets-II/"},{"title":"OA Count Analogous Arrays","text":"QuestionAn array is said to be analogous to the secret array if all of the following conditions are true:• The length of the array is equal to the length of the secret array.• Each integer in the array lies in the interval [lowerBound, upperBound].• The difference between each pair of consecutive integers of the array must be equal to the difference between the respective pair of consecutive integers in the secret array. In other words, let the secret array be [s[0], s[1],…, s[n-1]] and let the analogous array be [a[0], a[1],…, a[n-1]], then (a[i-1] - a[i]) must be equal to (s[i-1] - s[i]) for each i from 1 to n -1. Given the value of integers lowerBound and upperBound, inclusive, and the array of differences between each pair of consecutive integers of the secret array, find the number of arrays that are analogous to the secret array. If there is no array analogous to the secret array, return 0. For example:consecutiveDifference = [-2, -1, -2, 5]lowerBound = 3upperBound = 10 Note that none of the values is out of the bound. All possible analogous arrays are:[3, 5, 6, 8, 3][4, 6, 7, 9, 4][5, 7, 8, 10, 5] Tha answer is 3. Source: https://leetcode.com/discuss/interview-question/1332322/amazon-online-assessment-july-2021-secret-array Solution123456789101112131415161718public static int countAnalogousArrays(int[] consecutiveDifference, int lowerBound, int upperBound) { // parameter validation if (consecutiveDifference == null || consecutiveDifference.length < 1 || lowerBound > upperBound) { return 0; } int delta = 0, maxDelta = 0, minDelta = 0; for (int i = 0; i < consecutiveDifference.length; i++) { delta += consecutiveDifference[i]; maxDelta = Math.max(maxDelta, delta); minDelta = Math.min(minDelta, delta); } int maxDiff = maxDelta - minDelta, boundGap = upperBound - lowerBound; // max difference exceeds bound gap if (maxDiff > boundGap) { return 0; } return boundGap - maxDiff;}","link":"/2021/12/29/OA-Count-Analogous-Arrays/"},{"title":"LeetCode 862. Shortest Subarray with Sum at Least K","text":"QuestionGiven an integer array nums and an integer k, return the length of the shortest non-empty subarray of nums with a sum of at least k. If there is no such subarray, return -1. A subarray is a contiguous part of an array. Example 1: 12Input: nums = [1], k = 1Output: 1 Example 2: 12Input: nums = [1,2], k = 4Output: -1 Example 3: 12Input: nums = [2,-1,2], k = 3Output: 3 Constraints: 1 <= nums.length <= 105 -105 <= nums[i] <= 105 1 <= k <= 109 Source: https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/ SolutionWe use prefix sum to simplify the sum of subarray. The sum of any subarray can be calculated from prefix sum. This idea is similar to the usage of common prefix/suffix in LeetCode 718. Maximum Length of Repeated Subarray. We use a for loop to traverse ends of prefix sums prefixSums[y], a monotonic queue to maintain starts of prefix sums prefixSums[x]. For valid combinations of prefix sums (i.e. prefixSums[y]-prefixSums[x]>=k), smaller y, larger x and larger prefixSums[x] have advantage. 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051// 2D DP, O(n^2), Memory Limit Exceeded// still has redundant computationpublic int shortestSubarray(int[] nums, int k) { int len = nums.length; int minLen = len + 1; // dp[i][j] is the sum of subarray nums[i:j] (both ends are inclusive) int[][] dp = new int[len][len]; for (int m = 0; m < len; m++) { dp[m][m] = nums[m]; if (dp[m][m] >= k) { // can also return early minLen = Math.min(minLen, 1); } } for (int i = 0; i < len; i++) { for (int j = i + 1; j < len; j++) { dp[i][j] = dp[i][j - 1] + nums[j]; // repeat same computation if (dp[i][j] >= k) { minLen = Math.min(minLen, j - i + 1); } } } return minLen == len + 1 ? -1 : minLen;}// monotonic queue, O(n)public int shortestSubarray2(int[] nums, int k) { int len = nums.length; int minLen = len + 1; // prefixSums[i] is the sum of nums[:i] (exclusive) long[] prefixSums = new long[len + 1]; // calculate prefix sum for (int i = 1; i <= len; i++) { prefixSums[i] = prefixSums[i - 1] + nums[i - 1]; } // mono-dequeue, each element is the index of prefixSums, // prefixSums[i] ascending Deque<Integer> monoDeque = new LinkedList<>(); // sum of subarray nums[x:y] (x inclusive, y exclusive) = prefixSums[y] - prefixSums[x] for (int y = 0; y <= len; y++) { while (!monoDeque.isEmpty() && prefixSums[y] - prefixSums[monoDeque.getFirst()] >= k) { int x = monoDeque.removeFirst(); minLen = Math.min(minLen, y - x); } while (!monoDeque.isEmpty() && prefixSums[y] <= prefixSums[monoDeque.getLast()]) { monoDeque.removeLast(); } monoDeque.addLast(y); } return minLen == len + 1 ? -1 : minLen;}","link":"/2022/01/09/LeetCode-862-Shortest-Subarray-with-Sum-at-Least-K/"},{"title":"The C Programming Language Chapter-1","text":"Chapter-1 A Tutorial Introduction Notes of The C programming Language Integer Division of CIn C, integer division will truncate the result to zero, regardless of whether the result is positive or negative. printf Conversion Specification %fFor example, %6.4f means printf should print the value as floating point, at least 6 characters wide (it means the total width) and 4 after the decimal point. 123456789#include <stdio.h>int main() { float printFloatWidth1 = 1111.222; float printFloatWidth2 = 0.2; printf("printFloatWidth1: %6.4f\\n", printFloatWidth1); printf("printFloatWidth2: %6.2f\\n", printFloatWidth2); return 0;} 12printFloatWidth1: 1111.2220printFloatWidth2: 0.20 If the valid digits of the value cannot fill the least width, printf will fill it with spaces, as illustrated in printFloatWidth2. Symbolic Constants1#define PI 3.1415 Input and OutputC provides getchar() and putchar() as the user input/output interface. Comparing with InputStream and OutputStream in Java, input and output methods in C do not specify the source, destination and whether use buffer explicitly. getchar() and putchar() implicitly set console as input source and output destination, and enable buffer. It’s worth noting that \\n is not EOF. When we enter \\n in the terminal, the program will push the buffer (including \\n itself) to the while loop including getchar(). Then getchar() will read every character till the buffer is empty, and getchar() will block on it. Although the behaviors of this ‘echo’ program like it’s processing strings, it can only recognize characters one by one. A simple example 123456void copyInput() { int c; while ((c=getchar())!=EOF) { putchar(c); }} 1234567811145151451562365346236534 151 151 Declare functions in a compatible formatWe all know if you leave empty in the definition of C function argument list, it means this function need no argument. But for the compatibility with older C programs, it would be better to use void for an explicitly empty argument list. 12345678#include <stdio.h>int getLine(void);int copy(void);int main() { return 0;}","link":"/2021/12/10/The-C-Programming-Language-Chapter-1/"},{"title":"LeetCode 42. Trapping Rain Water","text":"QuestionGiven n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining. Example 1: 123Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]Output: 6Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Example 2: 12Input: height = [4,2,0,3,2,5]Output: 9 Constraints: n == height.length 1 <= n <= 2 * 104 0 <= height[i] <= 105 Source: https://leetcode.com/problems/trapping-rain-water/ SolutionThe main idea is to calculate and sum up the volume of water trapped on each bar. For bar i, the volume of water on it is determined by the max heights on its left and right sides. $v = min(leftMax, rightMax) - height[i]$. Thus, the most naive solution is to cache the leftMax and rightMax for each bar. Then we traverse height again and calculate the volume of trapped water. The thought of caching is similar to DP but this solution does not have a classic transition phase. A more space-efficient solution is two-pointer. The key point is that we do not have to get both accurate left max and right max, but get the min of them. We maintain two variables leftMax, rightMax, which store the max values on the left of left pointer and the right of right pointer respectively. For left, leftMax is trusted but rightMax may be less than the real right max value for left. For right, the situation is similar to left. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859// Cache left max and right max// Time Complexity O(n)// Space Complexity O(n)public int trap(int[] height) { if (height == null || height.length == 0) { return 0; } int len = height.length; int[] leftMax = new int[len]; // leftMax[i] is the max value in height[:, i] int[] rightMax = new int[len]; // rightMax[i] is the max value in height[i, :] // init int max = 0; for (int i = 0; i < len; i++) { max = Math.max(max, height[i]); leftMax[i] = max; } max = 0; for (int i = len - 1; i >= 0; i--) { max = Math.max(max, height[i]); rightMax[i] = max; } int volume = 0; for (int i = 0; i < len; i++) { // the volume of trapped rain on index i depends on its left max height and right max height volume += Math.min(leftMax[i], rightMax[i]) - height[i]; } return volume;}// Two-pointer// Time Complexity O(n)// Space Complexity O(1)public int trap2(int[] height) { if (height == null || height.length == 0) { return 0; } int len = height.length; int left = 0, right = len - 1; int leftMax = 0; // the max value in height[:, left] int rightMax = 0; // the max value in height[right, :] int volume = 0; // no need to consider left == right, // because for left == right == i, leftMax == rightMax == height[i], // the volume of water trapped on this bar is zero while (left < right) { leftMax = Math.max(leftMax, height[left]); rightMax = Math.max(rightMax, height[right]); if (leftMax < rightMax) { // trusty case for height[left] volume += leftMax - height[left]; left++; } else { // trusty case for height[right] volume += rightMax - height[right]; right--; } } return volume;}","link":"/2022/05/18/LeetCode-42-Trapping-Rain-Water/"},{"title":"LeetCode 26. Remove Duplicates from Sorted Array","text":"QuestionGiven an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique element appears only once. The relative order of the elements should be kept the same. Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements. Return k after placing the final result in the first k slots of nums. Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory. Custom Judge: The judge will test your solution with the following code: 123456789int[] nums = [...]; // Input arrayint[] expectedNums = [...]; // The expected answer with correct lengthint k = removeDuplicates(nums); // Calls your implementationassert k == expectedNums.length;for (int i = 0; i < k; i++) { assert nums[i] == expectedNums[i];} If all assertions pass, then your solution will be accepted. Example 1: 1234Input: nums = [1,1,2]Output: 2, nums = [1,2,_]Explanation: Your function should return k = 2, with the first two elements of nums being 1 and 2 respectively.It does not matter what you leave beyond the returned k (hence they are underscores). Example 2: 1234Input: nums = [0,0,1,1,1,2,2,3,3,4]Output: 5, nums = [0,1,2,3,4,_,_,_,_,_]Explanation: Your function should return k = 5, with the first five elements of nums being 0, 1, 2, 3, and 4 respectively.It does not matter what you leave beyond the returned k (hence they are underscores). Constraints: 1 <= nums.length <= 3 * 104 -100 <= nums[i] <= 100 nums is sorted in non-decreasing order. Source: https://leetcode.com/problems/remove-duplicates-from-sorted-array/ Solution12345678910111213141516171819public int removeDuplicates(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int count = 1; int len = nums.length; int nextIndex = 1; // where the next unique element will be placed for (int i = 1; i < len; i++) { if (nums[i] != nums[i - 1]) { // unique element // DO NOT use swap, because swap can affect finding subsequent duplicates nums[nextIndex] = nums[i]; nextIndex++; count++; } // for duplicates, just skip them } return count;}","link":"/2022/05/18/LeetCode-26-Remove-Duplicates-from-Sorted-Array/"},{"title":"LeetCode 81. Search in Rotated Sorted Array II","text":"QuestionThere is an integer array nums sorted in non-decreasing order (not necessarily with distinct values). Before being passed to your function, nums is rotated at an unknown pivot index k (0 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,4,4,5,6,6,7] might be rotated at pivot index 5 and become [4,5,6,6,7,0,1,2,4,4]. Given the array nums after the rotation and an integer target, return true if target is in nums, or false if it is not in nums. You must decrease the overall operation steps as much as possible. Example 1: 12Input: nums = [2,5,6,0,0,1,2], target = 0Output: true Example 2: 12Input: nums = [2,5,6,0,0,1,2], target = 3Output: false Constraints: 1 <= nums.length <= 5000 -104 <= nums[i] <= 104 nums is guaranteed to be rotated at some pivot. -104 <= target <= 104 Source: https://leetcode.com/problems/search-in-rotated-sorted-array-ii/ SolutionThe differences between this problem and LeetCode 33. Search in Rotated Sorted Array are: 1. in this problem, nums may contain duplicate elements; 2. the solution only needs to return whether nums contains target, but not the index of target if it exists. 12345678910111213141516171819202122232425262728293031323334353637// Time Complexity: average O(log(n)); worst O(n)public boolean search(int[] nums, int target) { if (nums == null || nums.length == 0) { return false; } int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] == target) { return true; } if (nums[mid] > nums[left]) { // [left, mid] is sorted if (nums[mid] > target && nums[left] <= target) { // target may be in the sorted interval right = mid - 1; } else { left = mid + 1; } } else if (nums[mid] < nums[left]) { // [mid, right] is sorted if (nums[mid] < target && nums[right] >= target) { // target may be in the sorted interval left = mid + 1; } else { right = mid - 1; } } else { // nums[mid] == nums[left] // exclude left to make progress. // it is safe because nums[left] != target left++; } } return false;}","link":"/2022/05/19/LeetCode-81-Search-in-Rotated-Sorted-Array-II/"},{"title":"LeetCode 67. Add Binary","text":"QuestionGiven two binary strings a and b, return their sum as a binary string. Example 1: 12Input: a = "11", b = "1"Output: "100" Example 2: 12Input: a = "1010", b = "1011"Output: "10101" Constraints: 1 <= a.length, b.length <= 104 a and b consist only of '0' or '1' characters. Each string does not contain leading zeros except for the zero itself. Source: https://leetcode.com/problems/add-binary/ Solution123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354public String addBinary(String a, String b) { if (a == null && b == null) { return ""; } else if (a == null) { return b; } else if (b == null) { return a; } int lenA = a.length(); int lenB = b.length(); if (lenA == 0 && lenB == 0) { return ""; } else if (lenA == 0) { return b; } else if (lenB == 0) { return a; } StringBuilder sb = new StringBuilder(); int ia = lenA - 1, ib = lenB - 1; int carry = 0; while (ia >= 0 && ib >= 0) { int bitA = a.charAt(ia) - '0'; int bitB = b.charAt(ib) - '0'; int bitC = (bitA + bitB + carry) % 2; carry = (bitA + bitB + carry) / 2; sb.append(bitC); ia--; ib--; } // these two loops handle remaining bits // only one loop will be entered while (ia >= 0) { int bitA = a.charAt(ia) - '0'; int bitC = (bitA + carry) % 2; carry = (bitA + carry) / 2; sb.append(bitC); ia--; } while (ib >= 0) { int bitB = b.charAt(ib) - '0'; int bitC = (bitB + carry) % 2; carry = (bitB + carry) / 2; sb.append(bitC); ib--; } // if carry is the most significant bit if (carry == 1) { sb.append(carry); } sb.reverse(); return sb.toString();}","link":"/2022/05/20/LeetCode-67-Add-Binary/"},{"title":"LeetCode 72. Edit Distance","text":"QuestionGiven two strings word1 and word2, return the minimum number of operations required to convert word1 to word2. You have the following three operations permitted on a word: Insert a character Delete a character Replace a character Example 1: 123456Input: word1 = "horse", word2 = "ros"Output: 3Explanation: horse -> rorse (replace 'h' with 'r')rorse -> rose (remove 'r')rose -> ros (remove 'e') Example 2: 12345678Input: word1 = "intention", word2 = "execution"Output: 5Explanation: intention -> inention (remove 't')inention -> enention (replace 'i' with 'e')enention -> exention (replace 'n' with 'x')exention -> exection (replace 'n' with 'c')exection -> execution (insert 'u') Constraints: 0 <= word1.length, word2.length <= 500 word1 and word2 consist of lowercase English letters. Source: https://leetcode.com/problems/edit-distance/ SolutionTo make word1[:i] equal to word2[:j], there are 4 possible actions on their last characters: no operation (only when their last characters are identical), insert, delete, and replace. In each step, we only consider the last characters in the two strings. Note that i,j in the following illustration and those in the code have different meanings. Dependencies in the transition equation determine the order of the loops. 1234567891011121314151617181920212223242526272829303132333435363738394041// Time Complexity O(mn)// Space Complexity O(mn)public int minDistance(String word1, String word2) { if (word1 == null || word2 == null) { return -1; } int len1 = word1.length(); int len2 = word2.length(); if (len1 == 0) { // word1 is "" return len2; } if (len2 == 0) { // word2 is "" return len1; } // dp[i][j] is the min number of operations to make word1[0, i) equals word2[0, j) int[][] dp = new int[len1 + 1][len2 + 1]; // init for (int j = 0; j <= len2; j++) { dp[0][j] = j; // always insert } for (int i = 0; i <= len1; i++) { dp[i][0] = i; // always delete } // transition for (int i = 1; i <= len1; i++) { for (int j = 1; j <= len2; j++) { int noOp = dp[i - 1][j - 1]; // can only be used if word1[i - 1] == word2[j - 1] int replaceOp = 1 + dp[i - 1][j - 1]; int deleteOp = 1 + dp[i - 1][j]; int insertOp = 1 + dp[i][j - 1]; // consider all possible choices if (word1.charAt(i - 1) == word2.charAt(j - 1)) { // no need replace dp[i][j] = Math.min(noOp, Math.min(deleteOp, insertOp)); } else { dp[i][j] = Math.min(replaceOp, Math.min(deleteOp, insertOp)); } } } return dp[len1][len2];}","link":"/2022/05/22/LeetCode-72-Edit-Distance/"}],"tags":[{"name":"DP","slug":"DP","link":"/tags/DP/"},{"name":"Java","slug":"Java","link":"/tags/Java/"},{"name":"Go","slug":"Go","link":"/tags/Go/"},{"name":"K Sum","slug":"K-Sum","link":"/tags/K-Sum/"},{"name":"Tree","slug":"Tree","link":"/tags/Tree/"},{"name":"BFS","slug":"BFS","link":"/tags/BFS/"},{"name":"Rolling Hash","slug":"Rolling-Hash","link":"/tags/Rolling-Hash/"},{"name":"Binary Search","slug":"Binary-Search","link":"/tags/Binary-Search/"},{"name":"Common Substring","slug":"Common-Substring","link":"/tags/Common-Substring/"},{"name":"String","slug":"String","link":"/tags/String/"},{"name":"Substring","slug":"Substring","link":"/tags/Substring/"},{"name":"Array","slug":"Array","link":"/tags/Array/"},{"name":"Monotonic","slug":"Monotonic","link":"/tags/Monotonic/"},{"name":"DFS","slug":"DFS","link":"/tags/DFS/"},{"name":"Delta","slug":"Delta","link":"/tags/Delta/"},{"name":"Two Pointers","slug":"Two-Pointers","link":"/tags/Two-Pointers/"},{"name":"Stack","slug":"Stack","link":"/tags/Stack/"},{"name":"List","slug":"List","link":"/tags/List/"},{"name":"Queue","slug":"Queue","link":"/tags/Queue/"},{"name":"Sliding Window","slug":"Sliding-Window","link":"/tags/Sliding-Window/"},{"name":"Interval","slug":"Interval","link":"/tags/Interval/"},{"name":"Heap","slug":"Heap","link":"/tags/Heap/"},{"name":"Permutation","slug":"Permutation","link":"/tags/Permutation/"},{"name":"Greedy","slug":"Greedy","link":"/tags/Greedy/"},{"name":"Backtrace","slug":"Backtrace","link":"/tags/Backtrace/"},{"name":"Combination","slug":"Combination","link":"/tags/Combination/"},{"name":"In-place","slug":"In-place","link":"/tags/In-place/"},{"name":"Prefix Sum","slug":"Prefix-Sum","link":"/tags/Prefix-Sum/"},{"name":"Graph","slug":"Graph","link":"/tags/Graph/"},{"name":"Coloring","slug":"Coloring","link":"/tags/Coloring/"},{"name":"Subset","slug":"Subset","link":"/tags/Subset/"},{"name":"C","slug":"C","link":"/tags/C/"},{"name":"Simulation","slug":"Simulation","link":"/tags/Simulation/"}],"categories":[{"name":"LeetCode","slug":"LeetCode","link":"/categories/LeetCode/"},{"name":"Math","slug":"Math","link":"/categories/Math/"},{"name":"Algorithm Template","slug":"Algorithm-Template","link":"/categories/Algorithm-Template/"},{"name":"Programming Tips","slug":"Programming-Tips","link":"/categories/Programming-Tips/"},{"name":"C - K&R","slug":"C-K-R","link":"/categories/C-K-R/"}]}