├── .gitignore ├── .idea ├── .gitignore ├── kotlinc.xml ├── leetcode │ └── editor.xml ├── libraries │ └── KotlinJavaRuntime.xml ├── misc.xml └── vcs.xml ├── LeetCodeKotlin.iml ├── README.md └── src └── com └── boycoder ├── Main.kt ├── basis ├── Basis.kt ├── algorithm │ ├── BasicAlgorithm.kt │ ├── BinarySearch.kt │ └── sort │ │ ├── BubbleSort.kt │ │ ├── CountSort.kt │ │ ├── HeapSort.kt │ │ ├── MergeSort.kt │ │ └── SelectionSort.kt └── datastructure │ ├── BinaryHeap.kt │ ├── ListNode.kt │ ├── TreeNode.kt │ ├── UnionFindQuickFind.kt │ └── UnionFindQuickUnion.kt ├── problems ├── LeetCode.kt ├── array │ ├── Easy_27_RemoveElement.kt │ ├── Med_1248_CountNiceSubArray.kt │ ├── Med_209_MinSizeSubArray.kt │ ├── Med_325_MaxSizeSubArray.kt │ ├── Med_523_ContinuousSubArraySumK.kt │ ├── Med_560_SubArrayEqualK.kt │ ├── Med_713_SubArrayLessK.kt │ └── Med_974_SubArraySumDivisiableK.kt ├── backtrack │ ├── Hard_51_NQueens.kt │ ├── Med_077_Combinations.kt │ ├── Med_131_PalindromePartition.kt │ ├── Med_17_LetterCombinationPhoneNum.kt │ ├── Med_216_CombinationSumIII.kt │ ├── Med_332_ReconstructItinerary.kt │ ├── Med_39_CombinationSum.kt │ ├── Med_40_CombinationSumII.kt │ ├── Med_46_Permutations.kt │ ├── Med_47_PermutationsII.kt │ ├── Med_491_IncreasingSubSequences.kt │ ├── Med_78_SubSet.kt │ ├── Med_90_SubSetII.kt │ └── Med_93_RestoreIPAddress.kt ├── bst │ ├── Easy_108_ArrayToBST.kt │ ├── Easy_235_LowestCommonAncestorBST.kt │ ├── Easy_501_ModeInBST.kt │ ├── Easy_530_AbsDifferenceInBST.kt │ ├── Easy_700_SearchInBST.kt │ ├── Med_450_DeleteInBST.kt │ ├── Med_538_BSTToGreaterTree.kt │ ├── Med_669_TrimBST.kt │ ├── Med_701_InsertIntoBST.kt │ └── Med_98_ValidateBST.kt ├── dp │ ├── Easy_062_UniquePath.kt │ ├── Easy_070_ClimbStairs.kt │ ├── Easy_509_Fibonacci.kt │ ├── Easy_746_ClimbStairsCost.kt │ ├── Med_063_UniquePathII.kt │ ├── Med_096_UniqueBST.kt │ ├── Med_343_IntegerBreak.kt │ ├── editdistance │ │ ├── Easy_392_IsSubSequence.kt │ │ ├── Hard_115_DistinctSubSequence.kt │ │ ├── Hard_72_EditDistance.kt │ │ ├── Med_583_DeleteForTwoString.kt │ │ └── Med_647_PalindromicSubString.kt │ ├── knapsack │ │ ├── Med_1049_LastStoneWeight.kt │ │ ├── Med_139_WordBreak.kt │ │ ├── Med_279_PerfectSquares.kt │ │ ├── Med_322_CoinChange.kt │ │ ├── Med_377_CombinationSumIV.kt │ │ ├── Med_416_SubSetSum.kt │ │ ├── Med_474_ZeroAndOne.kt │ │ ├── Med_494_TargetSum.kt │ │ └── Med_518_CoinChangeII.kt │ ├── robber │ │ ├── Med_198_HouseRobber.kt │ │ ├── Med_213_HouseRobberII.kt │ │ └── Med_337_HouseRobberIII.kt │ ├── stock │ │ ├── Med_121_SellStock.kt │ │ ├── Med_122_SellStockII.kt │ │ ├── Med_123_SellStockIII.kt │ │ ├── Med_188_SellStockIV.kt │ │ ├── Med_309_SellStockCoolDown.kt │ │ └── Med_714_SellStockFee.kt │ └── subsequence │ │ ├── Easy_53_MaxSubArray.kt │ │ ├── Med_1035_UncrossedLines.kt │ │ ├── Med_1143_LongestCommonSubsequence.kt │ │ ├── Med_300_LongestIncreasingSubsequence.kt │ │ ├── Med_674_LongestIncreasingSubsequenceII.kt │ │ └── Med_718_LongestSubArray.kt ├── greedy │ └── Easy_445_AssignCookie.kt ├── hash │ ├── Easy_001_TwoSum.kt │ ├── Easy_202_HappyNumber.kt │ ├── Easy_242_Anagram.kt │ ├── Easy_349_IntersectionTwoArray.kt │ ├── Med_15_3Sum.kt │ ├── Med_18_4Sum.kt │ ├── Med_383_RansomNote.kt │ └── Med_454_FourSum.kt ├── linkedList │ ├── Easy_203_RemoveLinkedListElements.kt │ ├── Easy_206_ReverseLinkedList.kt │ ├── Med_19_RemoveNthFromEnd.kt │ └── Med_24_SwapNodeInPair.kt ├── monotone │ ├── Easy_155_MinStack.kt │ ├── Hard_42_TrapRainWater.kt │ ├── Med_1438_LongestSubArrayLimitedAbs.kt │ ├── Med_739_DailyTemperatures.kt │ └── Med_MaxQueue.kt ├── pointers │ └── Easy_35_SearchInsertPosition.kt ├── stack │ ├── Easy_1047_RemoveDuplicate.kt │ ├── Easy_20_ValidParentheses.kt │ ├── Easy_225_ImpStackUseQueue.kt │ ├── Easy_232_ImpQueueUseStack.kt │ ├── Hard_239_SlidingWindowMax.kt │ ├── Med_150_ReversePolishNotation.kt │ └── Med_347_TopKFrequentElements.kt ├── string │ ├── Easy_28_ImplementStr.kt │ ├── Easy_344_ReverseString.kt │ └── Med_151_ReverseWordInString.kt └── tree │ ├── Easy_101_SymmetricTree.kt │ ├── Easy_104_MaxDepthBinaryTree.kt │ ├── Easy_110_BalancedBinaryTree.kt │ ├── Easy_111_MinDepthBinaryTree.kt │ ├── Easy_112_PathSum.kt │ ├── Easy_144_BinaryTreePreorder.kt │ ├── Easy_145_BinaryTreePostorderTraversal.kt │ ├── Easy_226_InvertBinaryTree.kt │ ├── Easy_257_BinaryTreePaths.kt │ ├── Easy_404_SumOfLeftLeaves.kt │ ├── Easy_559_MaxDepthNAryTree.kt │ ├── Easy_617_MergeTwoBinaryTree.kt │ ├── Easy_94_BinaryTreeInorderTraversal.kt │ ├── Med_102_BinaryTreeLevelOrderTraversal.kt │ ├── Med_106_ConstructBinaryTree.kt │ ├── Med_222_CompleteTreeNodeCount.kt │ ├── Med_236_LowestCommonAncestor.kt │ ├── Med_513_BottomLeftTreeValue.kt │ └── Med_654_MaxBinaryTree.kt └── utils ├── Utils.kt └── WrongResultException.kt /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # Java class files 26 | *.class 27 | 28 | # Generated files 29 | bin/ 30 | gen/ 31 | out/ 32 | # Uncomment the following line in case you need and you don't have the release build type files in your app 33 | # release/ 34 | 35 | # Gradle files 36 | .gradle/ 37 | build/ 38 | 39 | # Log Files 40 | *.log 41 | 42 | # Android Studio 3 in .gitignore file. 43 | .idea/caches 44 | .idea/modules.xml 45 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 46 | .idea/navEditor.xml -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/leetcode/editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/libraries/KotlinJavaRuntime.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LeetCodeKotlin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/com/boycoder/Main.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder 2 | 3 | -------------------------------------------------------------------------------- /src/com/boycoder/basis/Basis.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis 2 | 3 | -------------------------------------------------------------------------------- /src/com/boycoder/basis/algorithm/BasicAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.algorithm 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/4 6 | * @desc: 7 | */ -------------------------------------------------------------------------------- /src/com/boycoder/basis/algorithm/sort/BubbleSort.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.algorithm.sort 2 | 3 | import com.boycoder.utils.asserts 4 | import com.boycoder.utils.isSorted 5 | import kotlin.random.Random 6 | 7 | /** 8 | * @Author: zhutao 9 | * @datetime: 2021/6/4 10 | * 11 | * @desc: The basic ideal of bubble sort is, compare every pair of element, 12 | * make sure the biggest element go to the end. 13 | * eg: 6,4,5,1,5 14 | * | | 15 | * swap 16 | * 4,6,5,1,5 17 | * | | 18 | * 4,5,6,1,5 19 | * | | 20 | * 4,5,1,6,5 21 | * | | 22 | * 4,5,1,5,6 23 | * | | 24 | * 4,5,1,5 25 | * | | 26 | * Time: O(N^2) 27 | * Space: O(1) 28 | * In-Place: Yes 29 | * Stable: Yes 30 | */ 31 | object BubbleSort { 32 | fun sort(array: IntArray): IntArray { 33 | return sort3(array) 34 | } 35 | 36 | /** 37 | * Bubble sort + optimize 38 | * For the case that the tail elements already sorted, 39 | * we can just skip. 40 | */ 41 | private fun sort2(array: IntArray): IntArray { 42 | var end = array.size - 1 43 | 44 | while (end >= 1) { 45 | var endIndex = 1 46 | for (begin in 1..end) { 47 | if (array[begin - 1] > array[begin]) { 48 | val temp = array[begin - 1] 49 | array[begin - 1] = array[begin] 50 | array[begin] = temp 51 | 52 | endIndex = begin 53 | } 54 | } 55 | end = endIndex 56 | end-- 57 | } 58 | 59 | return array 60 | } 61 | 62 | // Basic bubble sort with while 63 | private fun sort3(array: IntArray): IntArray { 64 | var end = array.size - 1 65 | 66 | while (end >= 1) { 67 | var begin = 1 68 | while (begin <= end) { 69 | if (array[begin - 1] > array[begin]) { 70 | val temp = array[begin - 1] 71 | array[begin - 1] = array[begin] 72 | array[begin] = temp 73 | } 74 | begin++ 75 | } 76 | end-- 77 | } 78 | 79 | return array 80 | } 81 | 82 | // Basic bubble sort 83 | private fun sort1(array: IntArray): IntArray { 84 | for (end in (array.size - 1) downTo 1) { 85 | for (begin in 1..end) { 86 | if (array[begin - 1] > array[begin]) { 87 | val temp = array[begin - 1] 88 | array[begin - 1] = array[begin] 89 | array[begin] = temp 90 | } 91 | } 92 | } 93 | 94 | return array 95 | } 96 | } 97 | 98 | fun main() { 99 | val array = (1..100).shuffled().toIntArray() 100 | val res = isSorted(BubbleSort.sort(array)) 101 | asserts(res, true) 102 | } -------------------------------------------------------------------------------- /src/com/boycoder/basis/algorithm/sort/CountSort.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.algorithm.sort 2 | 3 | import com.boycoder.utils.asserts 4 | import com.boycoder.utils.isSorted 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/4 9 | * @desc: The ideal of count sort is a trade-off between time and space 10 | */ 11 | object CountSort { 12 | fun sort(array: IntArray, max: Int): IntArray { 13 | val count = IntArray(max + 1) 14 | for (i in array) { 15 | count[i]++ 16 | } 17 | val list = mutableListOf() 18 | for (i in 0 until count.size) { 19 | for (j in 1..count[i]) { 20 | list.add(i) 21 | } 22 | } 23 | return list.toIntArray() 24 | } 25 | } 26 | 27 | fun main() { 28 | val array = (1..100).shuffled().toIntArray() 29 | val res = isSorted(CountSort.sort(array, 100)) 30 | asserts(res, true) 31 | } -------------------------------------------------------------------------------- /src/com/boycoder/basis/algorithm/sort/HeapSort.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.algorithm.sort 2 | 3 | import com.boycoder.basis.datastructure.BinaryHeap 4 | import com.boycoder.utils.asserts 5 | import com.boycoder.utils.isSorted 6 | 7 | /** 8 | * @Author: zhutao 9 | * @datetime: 2021/6/4 10 | * @desc: 11 | */ 12 | object HeapSort { 13 | fun sort(array: IntArray): IntArray { 14 | 15 | // min heap 16 | val heap = BinaryHeap{o1, o2 -> o2 -o1} 17 | 18 | for (i in array) { 19 | heap.add(i) 20 | } 21 | 22 | val list = mutableListOf() 23 | repeat(heap.size()) { 24 | list.add(heap.remove()) 25 | } 26 | 27 | return list.toIntArray() 28 | } 29 | } 30 | 31 | fun main() { 32 | val array = (1..100).shuffled().toIntArray() 33 | val res = isSorted(HeapSort.sort(array)) 34 | asserts(res, true) 35 | } -------------------------------------------------------------------------------- /src/com/boycoder/basis/algorithm/sort/MergeSort.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.algorithm.sort 2 | 3 | import com.boycoder.utils.asserts 4 | import com.boycoder.utils.isSorted 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/4 9 | * @desc: The ideal of merge sort is, Divide Conquer 10 | * We divide the array into many parts, and merge them to sort 11 | * 12 | * Time: O(NLogN) 13 | * Space: O(N) 14 | * In-Place: No 15 | * Stable: Yes 16 | */ 17 | object MergeSort { 18 | fun sort(array: IntArray): IntArray { 19 | mergeSort(array, 0, array.size - 1) 20 | return array 21 | } 22 | 23 | private fun mergeSort(array: IntArray, begin: Int, end: Int) { 24 | if (end - begin < 1) { 25 | // there is only one element 26 | return 27 | } 28 | 29 | val mid = begin + (end - begin) / 2 30 | 31 | // divide 32 | mergeSort(array, begin, mid) 33 | mergeSort(array, mid + 1, end) 34 | 35 | // conquer 36 | merge(array, begin, mid, end) 37 | } 38 | 39 | /** 40 | * [begin, mid] 41 | * [mid + 1, end] 42 | */ 43 | private fun merge(array: IntArray, begin: Int, mid: Int, end: Int) { 44 | val backup = array.slice(begin..mid) 45 | var cur = begin 46 | var leftCur = 0 47 | var rightCur = mid + 1 48 | 49 | while (cur <= end) { 50 | // 1. pick left 51 | // 2. right side out of index 52 | if (rightCur > end || (leftCur < backup.size && backup[leftCur] < array[rightCur])) { 53 | array[cur] = backup[leftCur] 54 | leftCur++ 55 | } else { 56 | array[cur] = array[rightCur] 57 | rightCur++ 58 | } 59 | cur++ 60 | } 61 | } 62 | } 63 | 64 | fun main() { 65 | val array = (1..100).shuffled().toIntArray() 66 | val res = isSorted(MergeSort.sort(array)) 67 | asserts(res, true) 68 | } -------------------------------------------------------------------------------- /src/com/boycoder/basis/algorithm/sort/SelectionSort.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.algorithm.sort 2 | 3 | import com.boycoder.utils.asserts 4 | import com.boycoder.utils.isSorted 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/4 9 | * @desc: Selection sort is like Bubble sort, but it dont swap every time. 10 | * Instead of swapping, we mark the max index, and swap it with the end. 11 | * Time: O(N^2) 12 | * Space: O(1) 13 | * In-Place: Yes 14 | * Stable: No 15 | */ 16 | object SelectionSort { 17 | fun sort(array: IntArray): IntArray { 18 | for (end in (array.size - 1) downTo 1) { 19 | 20 | var maxIndex = 0 21 | for (begin in 1..end) { 22 | if (array[begin] > array[maxIndex]) { 23 | // mark max index 24 | maxIndex = begin 25 | } 26 | } 27 | 28 | // swap 29 | val temp = array[maxIndex] 30 | array[maxIndex] = array[end] 31 | array[end] = temp 32 | } 33 | 34 | return array 35 | } 36 | } 37 | 38 | fun main() { 39 | val array = (1..100).shuffled().toIntArray() 40 | val res = isSorted(SelectionSort.sort(array)) 41 | asserts(res, true) 42 | } -------------------------------------------------------------------------------- /src/com/boycoder/basis/datastructure/ListNode.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.datastructure 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/4 6 | * @desc: Node for LinkedList 7 | */ 8 | data class ListNode(var value: Int, var next: ListNode?) -------------------------------------------------------------------------------- /src/com/boycoder/basis/datastructure/TreeNode.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.datastructure 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/4 6 | * @desc: 7 | */ 8 | data class TreeNode(var value: Int, var left: TreeNode? = null, var right: TreeNode? = null) -------------------------------------------------------------------------------- /src/com/boycoder/basis/datastructure/UnionFindQuickFind.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.datastructure 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: Quick Find 7 | * 8 | * 1. find O(1) 9 | * 2. union O(n) 10 | */ 11 | class UnionFindQuickFind(capacity: Int): IUnionFind { 12 | 13 | private var parents: IntArray 14 | 15 | init { 16 | if (capacity <= 0) { 17 | throw IllegalArgumentException("Capacity must > 0") 18 | } 19 | parents = IntArray(capacity) 20 | 21 | // init, let every element point to itself 22 | for (i in 0 until capacity) { 23 | parents[i] = i 24 | } 25 | } 26 | 27 | override fun find(element: Int): Int { 28 | rangeCheck(element) 29 | 30 | return parents[element] 31 | } 32 | 33 | /** 34 | * Quick find. 35 | * 36 | * 2 4 37 | * 0 1 3 38 | * | 39 | * | 40 | * 4 41 | * 0 1 2 3 42 | * 43 | * Merge element1 and it's children into element2 parents 44 | * This way, find will be really quick, but union is slow. 45 | */ 46 | override fun union(element1: Int, element2: Int) { 47 | val p1 = find(element1) 48 | val p2 = find(element2) 49 | 50 | // already union 51 | if (p1 == p2) return 52 | 53 | for (i in 0 until parents.size) { 54 | if (parents[i] == p1) { 55 | parents[i] = p2 56 | } 57 | } 58 | } 59 | 60 | override fun isSame(element1: Int, element2: Int): Boolean { 61 | return find(element1) == find(element2) 62 | } 63 | 64 | private fun rangeCheck(element: Int) { 65 | if (element < 0 || element >= parents.size) { 66 | throw IllegalArgumentException("Out of range") 67 | } 68 | } 69 | } 70 | 71 | interface IUnionFind { 72 | fun find(element: Int): Int 73 | fun union(element1: Int, element2: Int) 74 | fun isSame(element1: Int, element2: Int): Boolean 75 | } -------------------------------------------------------------------------------- /src/com/boycoder/basis/datastructure/UnionFindQuickUnion.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.basis.datastructure 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: Quick Union 7 | * 8 | * 1. find O(logn) 9 | * 2. union O(logn) 10 | */ 11 | class UnionFindQuickUnion(capacity: Int): IUnionFind { 12 | 13 | private var parents: IntArray 14 | 15 | init { 16 | if (capacity <= 0) { 17 | throw IllegalArgumentException("Capacity must > 0") 18 | } 19 | parents = IntArray(capacity) 20 | 21 | // init, let every element point to itself 22 | for (i in 0 until capacity) { 23 | parents[i] = i 24 | } 25 | } 26 | 27 | /** 28 | * For Quick Union 29 | * Find is slower. 30 | */ 31 | override fun find(element: Int): Int { 32 | rangeCheck(element) 33 | 34 | var temp = element 35 | while (temp != parents[temp]) { 36 | temp = parents[temp] 37 | } 38 | 39 | return temp 40 | } 41 | 42 | /** 43 | * Quick Union 44 | * 45 | * This case, we just let element1's parent point to element2's parent 46 | */ 47 | override fun union(element1: Int, element2: Int) { 48 | val p1 = find(element1) 49 | val p2 = find(element2) 50 | 51 | // already union 52 | if (p1 == p2) return 53 | parents[p1] = p2 54 | } 55 | 56 | override fun isSame(element1: Int, element2: Int): Boolean { 57 | return find(element1) == find(element2) 58 | } 59 | 60 | private fun rangeCheck(element: Int) { 61 | if (element < 0 || element >= parents.size) { 62 | throw IllegalArgumentException("Out of range") 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/LeetCode.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems 2 | 3 | -------------------------------------------------------------------------------- /src/com/boycoder/problems/array/Easy_27_RemoveElement.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.array 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/4 6 | * @desc: 7 | */ 8 | object Easy_27_RemoveElement { 9 | 10 | /** 11 | * Two pointer start from two side of array 12 | */ 13 | fun remove(array: IntArray, target: Int): Int { 14 | var left = 0 15 | var right = array.size - 1 16 | 17 | while (left <= right) { 18 | if (array[right] == target) { 19 | right-- 20 | continue 21 | } 22 | if (array[left] == target) { 23 | array[left] = array[right] 24 | right-- 25 | left++ 26 | } else { 27 | left++ 28 | } 29 | } 30 | 31 | return right + 1 32 | } 33 | 34 | /** 35 | * Two point: slow and fast, start from beginning 36 | */ 37 | fun remove1(array: IntArray, target: Int): Int { 38 | var slow: Int = 0 39 | var fast: Int = 0 40 | 41 | val size = array.size 42 | 43 | while (fast < size && slow <= fast) { 44 | if (array[fast] == target) { 45 | fast++ 46 | } else { 47 | if (slow != fast) { 48 | array[slow] = array[fast] 49 | } 50 | 51 | fast++ 52 | slow++ 53 | } 54 | } 55 | 56 | return slow 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/array/Med_1248_CountNiceSubArray.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.array 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/21 8 | * @desc: https://leetcode-cn.com/problems/count-number-of-nice-subarrays/ 9 | * Brute force 10 | * slide window 11 | */ 12 | object Med_1248_CountNiceSubArray { 13 | fun numberOfSubarrays(nums: IntArray, k: Int): Int { 14 | return count(nums, k) 15 | } 16 | 17 | private fun count(nums: IntArray, k: Int): Int { 18 | var count = 0 19 | var presum = 0 20 | // 21 | var map = hashMapOf() 22 | map[0] = 1 23 | 24 | for (right in 0 until nums.size) { 25 | presum = presum + (nums[right] and 1) 26 | 27 | if (map.containsKey(presum - k)) { 28 | count = count + map[presum - k]!! 29 | } 30 | 31 | map.put(presum, map.getOrDefault(presum, 0) + 1) 32 | } 33 | 34 | return count 35 | } 36 | } 37 | 38 | fun main() { 39 | val res = Med_1248_CountNiceSubArray.numberOfSubarrays(intArrayOf(1,1,2,1,1), 3) 40 | asserts(res, 2) 41 | val res1 = Med_1248_CountNiceSubArray.numberOfSubarrays(intArrayOf(2,4,6), 1) 42 | asserts(res1, 0) 43 | val res2 = Med_1248_CountNiceSubArray.numberOfSubarrays(intArrayOf(2,2,2,1,2,2,1,2,2,2), 2) 44 | asserts(res2, 16) 45 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/array/Med_209_MinSizeSubArray.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.array 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/21 8 | * @desc: https://leetcode-cn.com/problems/minimum-size-subarray-sum/ 9 | */ 10 | object Med_209_MinSizeSubArray { 11 | fun minSubArrayLen(target: Int, nums: IntArray): Int { 12 | return min2(target, nums) 13 | } 14 | 15 | /** 16 | * slide window 17 | */ 18 | private fun min2(target: Int, nums: IntArray): Int { 19 | var left = 0 20 | var sum = 0 21 | var min = Int.MAX_VALUE 22 | 23 | for (right in 0 until nums.size) { 24 | sum = sum + nums[right] 25 | 26 | while (sum >= target) { 27 | min = Math.min(min, right - left + 1) 28 | sum = sum - nums[left] 29 | left++ 30 | } 31 | } 32 | 33 | return min 34 | } 35 | 36 | /** 37 | * Brute Force 38 | */ 39 | private fun min(target: Int, nums: IntArray): Int { 40 | var min = Int.MAX_VALUE 41 | for(i in 0 until nums.size) { 42 | for (j in i until nums.size) { 43 | // sum 44 | var sum = 0 45 | for (k in i..j) { 46 | sum = sum + nums[k] 47 | } 48 | if (sum >= target) { 49 | min = Math.min(min, j - i + 1) 50 | } 51 | } 52 | } 53 | 54 | return if (min == Int.MAX_VALUE) 0 else min 55 | } 56 | 57 | /** 58 | * Two pointer 59 | */ 60 | private fun min1(target: Int, nums: IntArray): Int { 61 | if (nums.size == 0) return 0 62 | 63 | var left = 0 64 | var right = 0 65 | var sum = nums[0] 66 | var min = Int.MAX_VALUE 67 | 68 | while (right < nums.size && left < nums.size) { 69 | if (sum >= target) { 70 | min = Math.min(min, right - left + 1) 71 | sum = sum - nums[left] 72 | left++ 73 | } else { 74 | right++ 75 | if (right == nums.size) break 76 | sum = sum + nums[right] 77 | } 78 | } 79 | 80 | return if (min == Int.MAX_VALUE) 0 else min 81 | } 82 | } 83 | 84 | fun main() { 85 | val res = Med_209_MinSizeSubArray.minSubArrayLen(7, intArrayOf(2,3,1,2,4,3)) 86 | asserts(res, 2) 87 | val res1 = Med_209_MinSizeSubArray.minSubArrayLen(4, intArrayOf(1,4,4)) 88 | asserts(res1, 1) 89 | val res2 = Med_209_MinSizeSubArray.minSubArrayLen(11, intArrayOf(1,1,1,1,1,1,1,1)) 90 | asserts(res2, 0) 91 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/array/Med_325_MaxSizeSubArray.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.array 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/21 8 | * @desc: https://leetcode-cn.com/problems/maximum-size-subarray-sum-equals-k/ 9 | * Brute force 10 | * Prefix sum 11 | */ 12 | object Med_325_MaxSizeSubArray { 13 | 14 | /** 15 | * ----------sum[i]--------------- 16 | * ------prefix[j]------- 17 | * ----k---- 18 | * length = i - j 19 | * 20 | * 21 | * [1, -1, 5, -2, 3] 22 | * sum [0,1, 0, 5, 3, 6] 23 | * map [0 to 0, 1 to 1, 5 to 3, 3 to 4, 6 to 5] 24 | * 25 | * j from end to 1 26 | * j = 5 sum[5] - k = 3, map[3] = 4, max = 5 - 4 27 | * j = 4 sum[4] - k = 0, map[0] = 0, max = 4 - 0 28 | * j = 3 sum[3] - k = 2, map[0] not found 29 | * j = 2 sum[2] - k = -3, map[-3] not found 30 | * j = 1 sum[1] - k = -2, map[-2] not found 31 | * j = 0 sum[0] - k = -3, map[-3] not found 32 | */ 33 | fun maxSubArrayLen(nums: IntArray, k: Int): Int { 34 | // 35 | val map = hashMapOf() 36 | val sum = IntArray(nums.size + 1) 37 | var max = 0 38 | map.put(0, 0) 39 | 40 | // Get the prefix sum, and put them into map 41 | for (i in 1..nums.size) { 42 | sum[i] = sum[i - 1] + nums[i - 1] 43 | 44 | // For the same prefix sum, we always save the min index 45 | if (!map.containsKey(sum[i])) { 46 | map.put(sum[i], i) 47 | } 48 | } 49 | 50 | // find the max length 51 | // so we need to from end to start 52 | // prefix + k = sum[i] 53 | for (j in nums.size downTo 1) { 54 | if (!map.containsKey(sum[j] - k)) continue 55 | 56 | val start = map.getOrDefault(sum[j] - k, 0) 57 | max = Math.max(max, j - start) 58 | } 59 | 60 | return max 61 | } 62 | } 63 | 64 | fun main() { 65 | val res = Med_325_MaxSizeSubArray.maxSubArrayLen(intArrayOf(1, -1, 5, -2, 3), 3) 66 | asserts(res, 4) 67 | val res1 = Med_325_MaxSizeSubArray.maxSubArrayLen(intArrayOf(-2, -1, 2, 1), 1) 68 | asserts(res1, 2) 69 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/array/Med_523_ContinuousSubArraySumK.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.array 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/21 8 | * @desc: https://leetcode-cn.com/problems/continuous-subarray-sum/ 9 | */ 10 | object Med_523_ContinuousSubArraySum { 11 | fun checkSubarraySum(nums: IntArray, k: Int): Boolean { 12 | return check1(nums, k) 13 | } 14 | 15 | /** 16 | * prefix sum 17 | * 18 | * similar to [Med_560_SubArrayEqualK.count3] 19 | * 20 | * Ideal from:https://leetcode-cn.com/problems/continuous-subarray-sum/solution/gong-shui-san-xie-tuo-zhan-wei-qiu-fang-1juse/ 21 | */ 22 | private fun check1(nums: IntArray, k: Int): Boolean { 23 | val sum = IntArray(nums.size + 1) 24 | // 25 | val set = hashSetOf() 26 | 27 | for (i in 1..nums.size) { 28 | sum[i] = sum[i - 1] + nums[i - 1] 29 | } 30 | 31 | for (j in 2..nums.size) { 32 | set.add(sum[j - 2] % k) 33 | if (set.contains(sum[j] % k)) return true 34 | } 35 | 36 | return false 37 | } 38 | 39 | /** 40 | * Brute force 41 | */ 42 | private fun check(nums: IntArray, k: Int): Boolean { 43 | 44 | for (start in 0 until nums.size) { 45 | var sum = nums[start] 46 | for (end in (start + 1) until nums.size) { 47 | sum = sum + nums[end] 48 | if (sum % k == 0) { 49 | return true 50 | } 51 | } 52 | } 53 | 54 | return false 55 | } 56 | } 57 | 58 | fun main() { 59 | val res = Med_523_ContinuousSubArraySum.checkSubarraySum(intArrayOf(23,2,4,6,7), 6) 60 | asserts(res, true) 61 | val res1 = Med_523_ContinuousSubArraySum.checkSubarraySum(intArrayOf(23,2,6,4,7), 6) 62 | asserts(res1, true) 63 | val res2 = Med_523_ContinuousSubArraySum.checkSubarraySum(intArrayOf(23,2,6,4,7), 13) 64 | asserts(res2, false) 65 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/array/Med_560_SubArrayEqualK.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.array 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/21 8 | * @desc: https://leetcode-cn.com/problems/subarray-sum-equals-k/ 9 | * Brute force 10 | * Brute force optimize 11 | * Prefix sum 12 | */ 13 | object Med_SubArrayEqualK { 14 | fun subarraySum(nums: IntArray, k: Int): Int { 15 | return count3(nums, k) 16 | } 17 | 18 | /** 19 | * ----------sum[i]--------------- 20 | * ------prefix[j]------- 21 | * ----k---- 22 | * length = i - j 23 | * 24 | * [1,1,1] k = 2 25 | * prefix [0, 1,2,3] 26 | * map [0 to 1, 1 to 1, 2 to 2, 3 to 3] 27 | * end = 3, 3 - k = 1, map[1] = 1, count++ 28 | * end = 2, 2 - k = 0, map[0] = 1, count++ 29 | * end = 1, 1 - k = -1, map[-1] not found 30 | * 31 | * [-1, -1, 1] k = 0 32 | * prefix [0,-1, -2, -1] 33 | * map [0 to 1, -1 to 2, -2 to 1] 34 | * end = 3, -1 - k = -1, count = 2 35 | * 36 | * 37 | * 38 | * This solution is similar to [Med_325_MaxSizeSubArray.maxSubArrayLen] 39 | */ 40 | private fun count3(nums: IntArray, k: Int): Int { 41 | var count = 0 42 | var prefix = 0 43 | val map = hashMapOf() 44 | map.put(0 , 1) 45 | 46 | for (num in nums) { 47 | prefix = prefix + num 48 | if (map.containsKey(prefix - k)) { 49 | count = count + map.getOrDefault(prefix - k, 0) 50 | } 51 | 52 | map.put(prefix, map.getOrDefault(prefix, 0) + 1) 53 | } 54 | 55 | return count 56 | } 57 | 58 | /** 59 | * ----------sum[i]--------------- 60 | * ------prefix[j]------- 61 | * ----k---- 62 | * loop start end end 63 | * if (prefix[end] - prefix[start]) count++ 64 | * 65 | * This solution is similar to [Med_325_MaxSizeSubArray.maxSubArrayLen] 66 | */ 67 | private fun count2(nums: IntArray, k: Int): Int { 68 | val prefix = IntArray(nums.size + 1) 69 | var count = 0 70 | 71 | for (i in 1..nums.size) { 72 | prefix[i] = prefix[i - 1] + nums[i - 1] 73 | } 74 | 75 | for (start in 0 until nums.size) { 76 | for (end in (start+1)..nums.size) { 77 | if (prefix[end] - prefix[start] == k) { 78 | count++ 79 | } 80 | } 81 | } 82 | 83 | return count 84 | } 85 | 86 | 87 | 88 | /** 89 | * Brute force + optimize 90 | */ 91 | private fun count1(nums: IntArray, k: Int): Int { 92 | var count = 0 93 | 94 | for (start in 0 until nums.size) { 95 | var sum = 0 96 | for (end in start until nums.size) { 97 | sum = sum + nums[end] 98 | 99 | if (sum == k) { 100 | count++ 101 | } 102 | } 103 | } 104 | 105 | return count 106 | } 107 | 108 | /** 109 | * Brute force 110 | */ 111 | private fun count(nums: IntArray, k: Int): Int { 112 | var count = 0 113 | 114 | for (start in 0 until nums.size) { 115 | 116 | for (end in start until nums.size) { 117 | var sum = 0 118 | for (k in start..end) { 119 | sum = sum + nums[k] 120 | } 121 | 122 | if (sum == k) { 123 | count++ 124 | } 125 | } 126 | } 127 | 128 | return count 129 | } 130 | } 131 | 132 | fun main() { 133 | val res = Med_SubArrayEqualK.subarraySum(intArrayOf(1, 1, 1), 2) 134 | asserts(res, 2) 135 | val res1 = Med_SubArrayEqualK.subarraySum(intArrayOf(-1, -1, 1), 1) 136 | asserts(res1, 1) 137 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/array/Med_713_SubArrayLessK.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.array 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/21 8 | * @desc: https://leetcode-cn.com/problems/subarray-product-less-than-k/ 9 | * Brutte force 10 | * Two pointer 11 | */ 12 | object Med_713_SubArrayLessK { 13 | fun numSubarrayProductLessThanK(nums: IntArray, k: Int): Int { 14 | return count(nums, k) 15 | } 16 | 17 | private fun count(nums: IntArray, k: Int): Int { 18 | if (nums.size == 0) return 0 19 | var left = 0 20 | var product = 1 21 | var count = 0 22 | 23 | for (right in 0 until nums.size) { 24 | product = product * nums[right] 25 | 26 | while (product >= k && left <= right) { 27 | product = product / nums[left] 28 | left++ 29 | } 30 | 31 | // find the min left index that [left, right] product less than k 32 | // and in this range, there is (right - left + 1) count 33 | count = count + right - left + 1 34 | } 35 | 36 | return count 37 | } 38 | } 39 | 40 | fun main() { 41 | val res = Med_713_SubArrayLessK.numSubarrayProductLessThanK(intArrayOf(10,5,2,6), 100) 42 | asserts(res, 8) 43 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/array/Med_974_SubArraySumDivisiableK.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.array 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/21 8 | * @desc: https://leetcode-cn.com/problems/subarray-sums-divisible-by-k/ 9 | */ 10 | object Med_974_SubArraySumDivisiableK { 11 | fun subarraysDivByK(nums: IntArray, k: Int): Int { 12 | return count1(nums, k) 13 | } 14 | 15 | /** 16 | * Pre sum 17 | * 18 | * ---------------------sum[i]%k=3--------------------------- 19 | * ----------sum[j]%k=3----------- 20 | * ---------sum[i,j]%k=0------ 21 | * 22 | * 23 | * similar to [Med_523_ContinuousSubArraySumK] 24 | */ 25 | private fun count1(nums: IntArray, k: Int): Int { 26 | var presum = 0 27 | var count = 0 28 | val map = hashMapOf() 29 | map[0] = 1 30 | 31 | for (i in nums) { 32 | presum = presum + i 33 | // presum maybe negative 34 | val key = (presum % k + k) % k 35 | if (map.containsKey(key)) { 36 | count = count + map.getOrDefault(key, 0) 37 | } 38 | map[key] = map.getOrDefault(key, 0) + 1 39 | } 40 | 41 | return count 42 | } 43 | 44 | /** 45 | * Brute force 46 | */ 47 | private fun count(nums: IntArray, k: Int): Int { 48 | var count = 0 49 | 50 | for (start in 0 until nums.size) { 51 | var sum = 0 52 | for (end in start until nums.size) { 53 | sum = sum + nums[end] 54 | 55 | if (sum % k == 0) { 56 | count++ 57 | } 58 | } 59 | } 60 | 61 | return count 62 | } 63 | } 64 | 65 | fun main() { 66 | val res = Med_974_SubArraySumDivisiableK.subarraysDivByK(intArrayOf(4,5,0,-2,-3,1), 5) 67 | asserts(res, 7) 68 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Hard_51_NQueens.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | * 8 | * Ideal 9 | * For a n*n chessboard, we need to take n step. 10 | * And for every step, we can choose i = [0, n); j = [0, n) 11 | * 12 | * We use loop go through the col, and recursion go through the row, 13 | * and for every step, we check validation. 14 | */ 15 | object Hard_51_NQueens { 16 | private val list: MutableList> = mutableListOf() 17 | private val path: MutableList> = mutableListOf() 18 | private var n = 0 19 | 20 | fun solveNQueens(n: Int): List> { 21 | this.n = n 22 | backtrack(0) 23 | return list 24 | } 25 | 26 | private fun backtrack(row: Int) { 27 | if (path.size == n) { 28 | saveResult() 29 | return 30 | } 31 | 32 | for (col in 0 until n) { 33 | if (!isValid(row, col)) { 34 | continue 35 | } 36 | 37 | path.add(Pair(row, col)) 38 | backtrack(row + 1) 39 | path.removeAt(path.size - 1) 40 | } 41 | 42 | } 43 | 44 | private fun saveResult() { 45 | path.sortBy{ it.first } 46 | val tempList = mutableListOf() 47 | for (i in 0 until n) { 48 | val pair = path[i] 49 | var temp = StringBuilder() 50 | for (j in 0 until n) { 51 | if (j == pair.second) { 52 | temp.append("Q") 53 | } else { 54 | temp.append(".") 55 | } 56 | } 57 | tempList.add(temp.toString()) 58 | } 59 | 60 | list.add(tempList) 61 | } 62 | 63 | private fun isValid(row: Int, col: Int): Boolean { 64 | for (pair in path) { 65 | if (!isPairValid(pair, row, col)) { 66 | return false 67 | } 68 | } 69 | 70 | return true 71 | } 72 | 73 | private fun isPairValid(pair: Pair, row: Int, col: Int): Boolean { 74 | if (pair.first == row || pair.second == col) { 75 | return false 76 | } 77 | 78 | val x = Math.abs(pair.second - col) 79 | val y = Math.abs(pair.first - row) 80 | if (x == y) { 81 | return false 82 | } 83 | 84 | return true 85 | } 86 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_077_Combinations.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/4 6 | * @desc: 7 | * 8 | Ideal 9 | 10 | For 1...n K combination, we pick one for loop, one for recursion 11 | If we use n to loop, then we recur until we collet k elements 12 | 1..n is not very good for recursion 13 | 14 | eg: n = 3, k = 2 15 | 16 | [1,2,3] 17 | 1/ 2| \3 18 | [2,3] [3] [] 19 | 2/ \3 |3 20 | (1,2) (1,3) (2,3) 21 | 22 | 23 | */ 24 | object Med_077_Combinations { 25 | val list: MutableList> = mutableListOf() 26 | val path: MutableList = mutableListOf() 27 | var n = 0 28 | 29 | fun combine(n: Int, k: Int): List> { 30 | this.n = n 31 | dfs(path, 1, k) 32 | return list 33 | } 34 | 35 | // Another way for combination 36 | private fun dfs(path: MutableList, start: Int, k: Int) { 37 | if (path.size == k) { 38 | list.add(path) 39 | return 40 | } 41 | 42 | if (path.size > k) { 43 | return 44 | } 45 | 46 | if (start > n) { 47 | return 48 | } 49 | 50 | dfs(path.toMutableList().also{ it.add(start) }, start + 1, k) 51 | dfs(path.toMutableList(), start + 1, k) 52 | } 53 | 54 | // pruming got better performance 55 | private fun doCombinePruming(n: Int, start: Int, k: Int) { 56 | 57 | if (path.size == k) { 58 | // careful toMutableList() 59 | list.add(path.toMutableList()) 60 | return 61 | } 62 | 63 | // conner case: k = 2, start = 2, n = 3, path.size = 0 64 | if ((k - path.size - 1) > (n - start)) return 65 | 66 | // careful start..n 67 | // every time start, we can avoid duplication 68 | for (i in start..n) { 69 | // conner case: k = 2, i = 2, n = 3, path.size = 0 70 | if ((k - path.size - 1) > (n - i)) continue 71 | 72 | path.add(i) 73 | doCombinePruming(n, i + 1, k) 74 | path.remove(i) 75 | } 76 | } 77 | 78 | private fun doCombine(n: Int, start: Int, k: Int) { 79 | 80 | if (path.size == k) { 81 | // careful toMutableList() 82 | list.add(path.toMutableList()) 83 | return 84 | } 85 | 86 | // careful start..n 87 | // every time start, we can avoid duplication 88 | for (i in start..n) { 89 | path.add(i) 90 | doCombine(n, i + 1, k) 91 | path.remove(i) 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_131_PalindromePartition.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | * 8 | * Ideal 9 | * For the given string, 10 | * we check if it can be split into several substring, 11 | * that every substring is palindrome. 12 | * We need a loop to help us split at index. 13 | * For every index, we decide how many chars we split, 14 | * and then save the split result and pass the remain into the recursion. 15 | * 16 | * eg: aab down recursion 17 | * /a |aa \ | 18 | * ab b [] | 19 | * a/\ | when start index = size - 1 20 | * b [] [aa, b] 21 | * | 22 | * [a, a, b] 23 | * 24 | * -------------> 25 | * Loop to decide how many char to split 26 | * 27 | */ 28 | object Med_131_PalindromePartition { 29 | private val list: MutableList> = mutableListOf() 30 | private val path: MutableList = mutableListOf() 31 | 32 | fun partition(s: String): List> { 33 | part(s.toCharArray(), 0) 34 | return list 35 | } 36 | 37 | private fun part(array: CharArray, start: Int) { 38 | if (start == array.size) { 39 | list.add(path.toMutableList()) 40 | return 41 | } 42 | 43 | // pruming 44 | if (start > array.size) { 45 | return 46 | } 47 | 48 | for (i in start until array.size) { // [start, size - 1] 49 | if (!isPalindrome(array, start, i)) { 50 | continue 51 | } 52 | path.add(array.slice(start..i).joinToString("")) 53 | part(array, i + 1) 54 | path.removeAt(path.size - 1) 55 | } 56 | } 57 | 58 | private fun isPalindrome(array: CharArray, left: Int, right: Int): Boolean { 59 | var left = left 60 | var right = right 61 | 62 | while (left < right) { 63 | if (array[left] != array[right]) { 64 | return false 65 | } 66 | left++ 67 | right-- 68 | } 69 | 70 | return true 71 | } 72 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_17_LetterCombinationPhoneNum.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Med_17_LetterCombinationPhoneNum { 9 | private val list: MutableList = mutableListOf() 10 | private val path: MutableList = mutableListOf() 11 | 12 | fun letterCombinations(digits: String): List { 13 | if (digits.isBlank()) return list 14 | combine(digits.toCharArray(), 0) 15 | return list 16 | } 17 | 18 | private fun combine(digits: CharArray, index: Int) { 19 | if (path.size == digits.size) { 20 | list.add(path.joinToString("")) 21 | return 22 | } 23 | 24 | if (index >= digits.size) return 25 | 26 | val array = getChars(digits[index]) 27 | 28 | for (char in array) { 29 | path.add(char) 30 | combine(digits, index + 1) 31 | // careful 32 | // for test case: "999" 33 | // path.remove(char) wont pass 34 | // so for this case we need remove last 35 | path.removeAt(path.size - 1) 36 | } 37 | } 38 | 39 | private inline fun getChars(num: Char): CharArray { 40 | return when (num) { 41 | '2' -> charArrayOf('a', 'b', 'c') 42 | '3' -> charArrayOf('d', 'e', 'f') 43 | '4' -> charArrayOf('g', 'h', 'i') 44 | '5' -> charArrayOf('j', 'k', 'l') 45 | '6' -> charArrayOf('m', 'n', 'o') 46 | '7' -> charArrayOf('p', 'q', 'r', 's') 47 | '8' -> charArrayOf('t', 'u', 'v') 48 | '9' -> charArrayOf('w', 'x', 'y', 'z') 49 | else -> return charArrayOf() 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_216_CombinationSumIII.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Med_216_CombinationSumIII { 9 | private val list: MutableList> = mutableListOf() 10 | private val path: MutableList = mutableListOf() 11 | 12 | fun combinationSum3(k: Int, n: Int): List> { 13 | combine(k, n, n, 1) 14 | return list 15 | } 16 | 17 | // with pruming 18 | private fun combine(k: Int, n: Int, remain: Int, start: Int) { 19 | if (remain < 0) return 20 | 21 | if (path.size == k) { 22 | if (remain == 0) { 23 | list.add(path.toMutableList()) 24 | } 25 | 26 | return 27 | } 28 | 29 | // conner case: start > remain 30 | if (start > remain) return 31 | 32 | // conner case: k = 3, start = 8, path.size = 1 33 | if ((k - path.size - 1) > 9 - start) return 34 | 35 | for (i in start..9) { 36 | path.add(i) 37 | combine(k, n, remain - i, i + 1) 38 | path.removeAt(path.size - 1) 39 | } 40 | } 41 | 42 | private fun combineNoPruming(k: Int, n: Int, remain: Int, start: Int) { 43 | if (remain < 0) return 44 | 45 | if (path.size == k) { 46 | if (remain == 0) { 47 | list.add(path.toMutableList()) 48 | } 49 | 50 | return 51 | } 52 | 53 | for (i in start..9) { 54 | path.add(i) 55 | combine(k, n, remain - i, i + 1) 56 | path.removeAt(path.size - 1) 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_332_ReconstructItinerary.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | import java.util.* 4 | import kotlin.collections.HashMap 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | * 11 | * Ideal 12 | * We take airport as vertex and ticket as edge. 13 | * Map> 14 | * it means for this airport we can go to destination count times. 15 | * We start from JFK, 16 | * and find a way that all the list.size = tickets.size + 1, 17 | * it means we used all the tickets. 18 | * This is a normal dfs 19 | */ 20 | object Med_332_ReconstructItinerary { 21 | // sorted by key 22 | private val graph: HashMap> = hashMapOf() 23 | private val path: MutableList = mutableListOf() 24 | private var count = 0 25 | 26 | fun findItinerary(tickets: List>): List { 27 | // pre process 28 | count = tickets.size 29 | for (ticket in tickets) { 30 | val start = ticket[0] 31 | val destination = ticket[1] 32 | 33 | // careful: Kotlin sortedMapOf() means TreeMap 34 | val map = graph[start]?: sortedMapOf() 35 | map.put(destination, map.getOrDefault(destination, 0) + 1) 36 | 37 | graph[start] = map 38 | } 39 | 40 | // start from JFK 41 | path.add("JFK") 42 | backtrack("JFK") 43 | return path 44 | } 45 | 46 | private fun backtrack(start: String): Boolean { 47 | if (path.size == count + 1) { 48 | return true 49 | } 50 | 51 | // this is a tree map 52 | val destMap = graph[start]?:return false 53 | 54 | for (entry in destMap.entries) { 55 | if (entry.value <= 0) { 56 | continue 57 | } 58 | val value = entry.value 59 | val key = entry.key 60 | 61 | path.add(entry.key) 62 | destMap.put(key, value - 1) 63 | if (backtrack(entry.key)) { 64 | return true 65 | } 66 | path.removeAt(path.size - 1) 67 | destMap.put(key, value) 68 | } 69 | 70 | return false 71 | } 72 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_39_CombinationSum.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/combination-sum/ 7 | */ 8 | object Med_39_CombinationSum { 9 | private val list: MutableList> = mutableListOf() 10 | private val path: MutableList = mutableListOf() 11 | private var sum: Int = 0 12 | 13 | fun combinationSum(candidates: IntArray, target: Int): List> { 14 | combine(candidates, target, 0) 15 | return list 16 | } 17 | 18 | private fun combine(nums: IntArray, target: Int, start: Int) { 19 | if (sum > target) return 20 | 21 | if (sum == target) { 22 | // careful toMutableList 23 | list.add(path.toMutableList()) 24 | return 25 | } 26 | 27 | for (i in start until nums.size) { 28 | path.add(nums[i]) 29 | sum = sum + nums[i] 30 | // We can pick the same element many times, so dont use i+1 31 | // By using i, every recursion can pick the same value 32 | combine(nums, target, i) 33 | sum = sum - nums[i] 34 | path.removeAt(path.size - 1) 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_40_CombinationSumII.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/combination-sum-ii/ 7 | */ 8 | object Med_40_CombinationSumII { 9 | private val list: MutableList> = mutableListOf() 10 | private val path: MutableList = mutableListOf() 11 | private var sum = 0 12 | 13 | fun combinationSum2(candidates: IntArray, target: Int): List> { 14 | /** 15 | Though we can use a set in a level to avoid the same element been pick more than one time. 16 | But we still need to sort the array to avoid duplication 17 | EG: 1,2,7,1 18 | / \ 19 | 2,7,1 1 20 | \ (7, 1) 21 | (1, 7) 22 | 23 | EG: 1,1,2,7 24 | / \ 25 | 2,7,1 (NO) 26 | \ 27 | (1, 7) 28 | */ 29 | candidates.sort() 30 | combine(candidates, target, 0) 31 | return list 32 | } 33 | 34 | /** 35 | * So, to avoid duplication, 36 | * we can use sort and nums[i] == nums[i - 1] to check 37 | */ 38 | private fun combine(nums: IntArray, target: Int, start: Int) { 39 | if (sum > target) return 40 | 41 | if (sum == target) { 42 | list.add(path.toMutableList()) 43 | return 44 | } 45 | 46 | for (i in start until nums.size) { 47 | if (i > start && nums[i] == nums[i - 1]) continue 48 | 49 | path.add(nums[i]) 50 | sum = sum + nums[i] 51 | // We dont want to pick the same element more than once 52 | // So in the next level of recursion, the start index is i + 1 53 | // then it never repeat the same index for the single combination 54 | combine(nums, target, i + 1) 55 | sum = sum - nums[i] 56 | path.removeAt(path.size - 1) 57 | } 58 | } 59 | 60 | private fun combine1(nums: IntArray, target: Int, start: Int) { 61 | if (sum > target) return 62 | 63 | if (sum == target) { 64 | list.add(path.toMutableList()) 65 | return 66 | } 67 | 68 | val set: HashSet = hashSetOf() 69 | for (i in start until nums.size) { 70 | if (set.contains(nums[i])) continue 71 | set.add(nums[i]) 72 | 73 | path.add(nums[i]) 74 | sum = sum + nums[i] 75 | // We dont want to pick the same element more than once 76 | // So in the next level of recursion, the start index is i + 1 77 | // then it never repeat the same index for the single combination 78 | combine(nums, target, i + 1) 79 | sum = sum - nums[i] 80 | path.removeAt(path.size - 1) 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_46_Permutations.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | * 8 | * Ideal 9 | * Use recursion and backtrack to solve permutations problem 10 | * 11 | * Because we need permutations, so we can reuse the same array in every level. But the same element can not duplicate in the same path, so we need to check the element going to visit, if it in the path already. 12 | * 13 | * The difference between combination and permutation is : 14 | * 1. No start index passed 15 | * 2. Always loop from 0 16 | * 3. No duplicate for the path elements 17 | */ 18 | object Med_46_Permutations { 19 | private val list: MutableList> = mutableListOf() 20 | private val path: MutableList = mutableListOf() 21 | 22 | fun permute(nums: IntArray): List> { 23 | backtrack(nums) 24 | return list 25 | } 26 | 27 | private fun backtrack(array: IntArray) { 28 | if (path.size == array.size) { 29 | list.add(path.toList()) 30 | } 31 | 32 | for (i in 0 until array.size) { 33 | // we can do this only if the elements in array wont duplicate (are distinct) 34 | if (path.contains(array[i])) { 35 | continue 36 | } 37 | path.add(array[i]) 38 | backtrack(array) 39 | path.removeAt(path.size - 1) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_47_PermutationsII.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | * 8 | * Ideal 9 | * This is a little bit like https://leetcode-cn.com/problems/permutations/ 10 | * For this case, 11 | * we can sort the array and make sure in the same loop we dont visit the same element. 12 | * And because the elements in array can be duplicate, 13 | * so we can not use path.contains(value) to check, 14 | * we need to record the index, not the value, for the path. 15 | */ 16 | object Med_47_PermutationsII { 17 | private val list: MutableList> = mutableListOf() 18 | private val path: MutableList = mutableListOf() 19 | private val used = BooleanArray(21) 20 | 21 | fun permuteUnique(nums: IntArray): List> { 22 | nums.sort() 23 | backtrack(nums) 24 | return list 25 | } 26 | 27 | private fun backtrack(array: IntArray) { 28 | if (path.size == array.size) { 29 | list.add(path.toList()) 30 | return 31 | } 32 | 33 | val set = hashSetOf() 34 | 35 | for (i in 0 until array.size) { 36 | // avoid duplication for the same result 37 | if (used[i + 10]) { 38 | continue 39 | } 40 | 41 | // for the same level loop we avoid duplicate value 42 | if (set.contains(array[i])) { 43 | continue 44 | } 45 | set.add(array[i]) 46 | 47 | used[i + 10] = true 48 | path.add(array[i]) 49 | backtrack(array) 50 | used[i + 10] = false 51 | path.removeAt(path.size - 1) 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_491_IncreasingSubSequences.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Med_491_IncreasingSubSequences { 9 | private val list: MutableList> = mutableListOf() 10 | private val path: MutableList = mutableListOf() 11 | 12 | fun findSubsequences(nums: IntArray): List> { 13 | backtrack(nums, 0) 14 | return list 15 | } 16 | 17 | private fun backtrack(array: IntArray, start: Int) { 18 | if (path.size > 1) { 19 | list.add(path.toList()) 20 | } 21 | 22 | if (start >= array.size) { 23 | return 24 | } 25 | 26 | val used = BooleanArray(210) 27 | for (i in start until array.size) { 28 | if (path.size > 0 && array[i] < path[path.size - 1]) { 29 | continue 30 | } 31 | 32 | if (used[array[i] + 100]) { 33 | continue 34 | } 35 | 36 | used[array[i] + 100] = true 37 | 38 | path.add(array[i]) 39 | backtrack(array, i + 1) 40 | path.removeAt(path.size - 1) 41 | } 42 | } 43 | 44 | private fun find(array: IntArray, start: Int) { 45 | if (path.size > 1) { 46 | list.add(path.toList()) 47 | } 48 | 49 | if (start >= array.size) { 50 | return 51 | } 52 | 53 | val set = hashSetOf() 54 | for (i in start until array.size) { 55 | if (!isValid(set, array[i])) { 56 | continue 57 | } 58 | 59 | set.add(array[i]) 60 | path.add(array[i]) 61 | find(array, i + 1) 62 | path.removeAt(path.size - 1) 63 | } 64 | } 65 | 66 | // current elment is smaller than path end element 67 | // current element has been visited 68 | private fun isValid(set: HashSet, num: Int): Boolean { 69 | if (path.size > 0 && 70 | num < path[path.size - 1]) { 71 | // not increasing 72 | return false 73 | } 74 | 75 | if (set.contains(num)) { 76 | return false 77 | } 78 | 79 | return true 80 | } 81 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_78_SubSet.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | * 8 | * Ideal 9 | * For the sub set of a set. When we trying to find the combination, all the node of this tree(state of path), are the no-duplicate subset 10 | * 11 | * eg: n = 3 12 | * 13 | * [1,2,3] 14 | * 1/ 2| \3 15 | * [2,3] [3] [] 16 | * 2/ \3 |3 17 | * [3] [] [] 18 | * 3/ 19 | * [] 20 | * 21 | */ 22 | object Med_78_SubSet { 23 | private val list: MutableList> = mutableListOf() 24 | private val path: MutableList = mutableListOf() 25 | 26 | fun subsets(nums: IntArray): List> { 27 | sub(nums, 0) 28 | return list 29 | } 30 | 31 | private fun sub(nums: IntArray, start: Int) { 32 | 33 | list.add(path.toList()) 34 | 35 | if (start >= nums.size) { 36 | return 37 | } 38 | 39 | for (i in start until nums.size) { 40 | path.add(nums[i]) 41 | sub(nums, i + 1) 42 | path.removeAt(path.size - 1) 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_90_SubSetII.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Med_90_SubSetII { 9 | private val list: MutableList> = mutableListOf() 10 | private val path: MutableList = mutableListOf() 11 | 12 | fun subsetsWithDup(nums: IntArray): List> { 13 | nums.sort() 14 | sub(nums, 0) 15 | return list 16 | } 17 | 18 | // using sorted array and num[i] == nums[i - 1] to check 19 | private fun sub(nums: IntArray, start: Int) { 20 | list.add(path.toList()) 21 | 22 | if (path.size >= nums.size) { 23 | return 24 | } 25 | 26 | for (i in start until nums.size) { 27 | if (i > start && nums[i] == nums[i - 1]) { 28 | continue 29 | } 30 | 31 | path.add(nums[i]) 32 | sub(nums, i + 1) 33 | path.removeAt(path.size - 1) 34 | } 35 | } 36 | 37 | private fun sub1(nums: IntArray, start: Int) { 38 | list.add(path.toList()) 39 | 40 | if (path.size >= nums.size) { 41 | return 42 | } 43 | 44 | val set = hashSetOf() 45 | for (i in start until nums.size) { 46 | if (set.contains(nums[i])) { 47 | continue 48 | } 49 | set.add(nums[i]) 50 | 51 | path.add(nums[i]) 52 | sub(nums, i + 1) 53 | path.removeAt(path.size - 1) 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/backtrack/Med_93_RestoreIPAddress.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.backtrack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | * 8 | * Ideal 9 | * This problem is a little bit like palindrome-partitioning 10 | * Back tracking can be use in partitioning 11 | * 12 | * For the loop, we decide how many char to split, 13 | * from 1 to 3, check valide, then we save the path, 14 | * and pass the rest of string into the recursion i + 1. 15 | * 16 | * For the recursion, we check path.size == 4 && start == string.size 17 | * "25525511135" 18 | * 19 | */ 20 | object Med_93_RestoreIPAddress { 21 | private val list: MutableList = mutableListOf() 22 | private val path: MutableList = mutableListOf() 23 | 24 | fun restoreIpAddresses(s: String): List { 25 | split(s, 0) 26 | return list 27 | } 28 | 29 | private fun split(s: String, start: Int) { 30 | if (path.size == 4) { 31 | if (start == s.length) { 32 | // all string are used 33 | list.add(path.joinToString(".")) 34 | } 35 | return 36 | } 37 | 38 | // pruming 39 | // if there is too many num left 40 | // such as: 41 | // s.length == 13 42 | if ((4 - path.size) * 3 < (s.length - start)) { 43 | return 44 | } 45 | 46 | // s = "25525511135" s.length = 11 47 | // start = 9 48 | val end = Math.min(start + 3, s.length) 49 | for (i in start until end) { 50 | val temp = s.slice(start..i) 51 | if (!isValid(temp)) continue 52 | 53 | path.add(temp) 54 | split(s, i + 1) 55 | path.removeAt(path.size - 1) 56 | } 57 | } 58 | 59 | private fun isValid(s: String): Boolean { 60 | if (s.startsWith("0")) { 61 | // only zero can start with 0 62 | return s.length == 1 63 | } else { 64 | val num = s.toInt() 65 | return num in 0..255 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Easy_108_ArrayToBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Easy_108_ArrayToBST { 11 | fun sortedArrayToBST(nums: IntArray): TreeNode? { 12 | return build(nums, 0, nums.size - 1) 13 | } 14 | 15 | private fun build(nums: IntArray, left: Int, right: Int): TreeNode? { 16 | if (left > right) return null 17 | 18 | val mid = left + ((right - left) / 2) 19 | 20 | val root = TreeNode(nums[mid]) 21 | root.left = build(nums, left, mid - 1) 22 | root.right = build(nums, mid + 1, right) 23 | 24 | return root 25 | } 26 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Easy_235_LowestCommonAncestorBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Easy_235_LowestCommonAncestorBST { 11 | fun lowestCommonAncestor(root: TreeNode?, p: TreeNode?, q: TreeNode?): TreeNode? { 12 | if (root == null || p == null || q == null) return null 13 | return find(root, p, q) 14 | } 15 | 16 | private fun find(root: TreeNode?, p: TreeNode, q: TreeNode): TreeNode? { 17 | if (root == null) return null 18 | 19 | var node: TreeNode? = root 20 | while (node != null) { 21 | if (node.value > p.value && node.value > q.value) { 22 | node = node.left 23 | } else if (node.value < p.value && node.value < q.value) { 24 | node = node.right 25 | } else { 26 | return node 27 | } 28 | } 29 | 30 | return null 31 | } 32 | 33 | // recursion 34 | private fun find1(root: TreeNode?, p: TreeNode, q: TreeNode): TreeNode? { 35 | if (root == null) return null 36 | 37 | if (root.value > p.value && root.value > q.value) { 38 | return find(root.left, p, q) 39 | } else if (root.value < p.value && root.value < q.value) { 40 | return find(root.right, p, q) 41 | } else { 42 | return root 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Easy_501_ModeInBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | import kotlin.collections.ArrayList 6 | 7 | /** 8 | * @Author: zhutao 9 | * @datetime: 2021/6/22 10 | * @desc: 11 | */ 12 | object Easy_501_ModeInBST { 13 | var pre: TreeNode? = null 14 | var max: Int = 0 15 | var count: Int = 0 16 | val list = ArrayList() 17 | 18 | fun findMode(root: TreeNode?): IntArray { 19 | find1(root) 20 | return list.toIntArray() 21 | } 22 | 23 | /** 24 | * loop + stack 25 | * 26 | * in order traversal 27 | */ 28 | private fun find1(root: TreeNode?) { 29 | if (root == null) return 30 | 31 | val stk = LinkedList() 32 | stk.add(root) 33 | 34 | while (!stk.isEmpty()) { 35 | val node = stk.getLast() 36 | 37 | if (node != null) { 38 | stk.removeLast() 39 | 40 | // right 41 | if (node.right != null) stk.addLast(node.right) 42 | // root 43 | stk.addLast(node) 44 | stk.addLast(null) 45 | // left 46 | if (node.left != null) stk.addLast(node.left) 47 | } else { 48 | // remove null 49 | stk.removeLast() 50 | 51 | val temp = stk.removeLast()?:return 52 | if (pre == null) { 53 | count = 1 54 | } else if (pre?.value == temp.value){ 55 | count++ 56 | } else { 57 | count = 1 58 | } 59 | 60 | if (count == max) { 61 | list.add(temp.value) 62 | } 63 | 64 | if (count > max) { 65 | list.clear() 66 | list.add(temp.value) 67 | } 68 | 69 | max = Math.max(max, count) 70 | pre = temp 71 | } 72 | } 73 | } 74 | 75 | // recursion with pointer 76 | private fun find(root: TreeNode?) { 77 | if (root == null) return 78 | 79 | find(root.left) 80 | 81 | if (pre == null) { 82 | count = 1 83 | list.add(root.value) 84 | } else if (pre!!.value == root.value) { 85 | count++ 86 | } else { 87 | count = 1 88 | } 89 | 90 | if (count == max) { 91 | list.add(root.value) 92 | } 93 | 94 | if (count > max) { 95 | list.clear() 96 | 97 | list.add(root.value) 98 | } 99 | 100 | max = Math.max(count, max) 101 | pre = root 102 | 103 | find(root.right) 104 | } 105 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Easy_530_AbsDifferenceInBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Easy_530_AbsDifferenceInBST { 12 | 13 | // we can store the value only to get more performance 14 | private var pre:TreeNode? = null 15 | private var min = Int.MAX_VALUE 16 | 17 | fun getMinimumDifference(root: TreeNode?): Int { 18 | getMin(root) 19 | return min 20 | } 21 | 22 | private fun getMin(root: TreeNode?) { 23 | if (root == null) return 24 | 25 | val stk = LinkedList() 26 | stk.addLast(root) 27 | var node: TreeNode? = null 28 | 29 | while (!stk.isEmpty()) { 30 | node = stk.getLast() // peek 31 | 32 | node?.let { 33 | stk.removeLast() 34 | 35 | if (it.right != null) stk.addLast(it.right) 36 | 37 | stk.addLast(it) 38 | stk.addLast(null) 39 | 40 | if (it.left != null) stk.addLast(it.left) 41 | }?: run { 42 | stk.removeLast() // remove null 43 | val cur = stk.removeLast()?: return // element itself 44 | 45 | pre?.let { 46 | min = Math.min(min, cur.value - it.value) 47 | } 48 | pre = cur 49 | } 50 | } 51 | } 52 | 53 | private fun getMin1(root: TreeNode?) { 54 | if (root == null) return 55 | 56 | getMin(root.left) 57 | 58 | pre?.let { 59 | // cur is always larger than pre 60 | min = Math.min(min, root.value - it.value) 61 | } 62 | pre = root 63 | 64 | getMin(root.right) 65 | } 66 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Easy_700_SearchInBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Easy_700_SearchInBST { 11 | fun searchBST(root: TreeNode?, target: Int): TreeNode? { 12 | if (root == null) return null 13 | if (root.value == target) return root 14 | if (root.value > target) return searchBST(root.left, target) 15 | if (root.value < target) return searchBST(root.right, target) 16 | 17 | return null 18 | } 19 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Med_450_DeleteInBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_450_DeleteInBST { 11 | fun deleteNode(root: TreeNode?, key: Int): TreeNode? { 12 | return delete(root, key) 13 | } 14 | 15 | private fun delete(root: TreeNode?, key: Int): TreeNode? { 16 | if (root == null) return null 17 | 18 | if (root.value == key) { 19 | if (root.left == null && root.right == null) { 20 | // return null means delete 21 | return null 22 | } else if (root.left == null && root.right != null) { 23 | return root.right 24 | } else if (root.left != null && root.right == null) { 25 | return root.left 26 | } else { 27 | // left != null right != null 28 | var node = root.right!! 29 | // find successor 30 | while (node.left != null) { 31 | node = node.left!! 32 | } 33 | node.left = root.left 34 | root.left = null 35 | return root.right 36 | } 37 | } else if (root.value > key) { 38 | root.left = delete(root.left, key) 39 | } else if (root.value < key) { 40 | root.right = delete(root.right, key) 41 | } 42 | 43 | return root 44 | } 45 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Med_538_BSTToGreaterTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Med_538_BSTToGreaterTree { 12 | var pre: TreeNode? = null 13 | fun convertBST(root: TreeNode?): TreeNode? { 14 | return conver(root) 15 | } 16 | 17 | // loop, treat this tree as normal binary tree 18 | private fun conver(root: TreeNode?): TreeNode? { 19 | if (root == null) return null 20 | 21 | val stk = LinkedList() 22 | stk.addLast(root) 23 | 24 | while (!stk.isEmpty()) { 25 | val node = stk.getLast() 26 | 27 | if (node != null) { 28 | // reversed inorder traversal 29 | stk.removeLast() 30 | 31 | if (node.left != null) stk.addLast(node.left) 32 | 33 | stk.addLast(node) 34 | stk.addLast(null) 35 | 36 | if (node.right != null) stk.addLast(node.right) 37 | } else { 38 | stk.removeLast() // remove null 39 | 40 | // traversal element 41 | stk.removeLast()?.let { 42 | if (pre != null) { 43 | it.value = pre!!.value + it.value 44 | } 45 | pre = it 46 | } 47 | } 48 | } 49 | 50 | return root 51 | } 52 | 53 | 54 | // recursion 55 | private fun conver1(root: TreeNode?): TreeNode? { 56 | if (root == null) return root 57 | 58 | conver(root.right) 59 | if (pre != null) { 60 | root.value = pre!!.value + root.value 61 | } 62 | pre = root 63 | conver(root.left) 64 | 65 | return root 66 | } 67 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Med_669_TrimBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_669_TrimBST { 11 | fun trimBST(root: TreeNode?, low: Int, high: Int): TreeNode? { 12 | return trim(root, low, high) 13 | } 14 | 15 | private fun trim(root: TreeNode?, low: Int, high: Int): TreeNode? { 16 | var node = root 17 | if (node == null) return null 18 | 19 | if (node.value < low && node.value < high) { 20 | // drop the left side 21 | node = node.right 22 | node = trim(node, low, high) 23 | } else if (node.value > high && node.value > low) { 24 | // drop the right side 25 | node = node.left 26 | node = trim(node, low, high) 27 | } else { 28 | // node is fine 29 | // but we need to check node.left and node right 30 | node.left = trim(node.left, low, high) 31 | node.right = trim(node.right, low, high) 32 | } 33 | 34 | return node 35 | } 36 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Med_701_InsertIntoBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_701_InsertIntoBST { 11 | fun insertIntoBST(root: TreeNode?, target: Int): TreeNode? { 12 | return insert(root, target) 13 | } 14 | 15 | private fun insert(root: TreeNode?, target: Int): TreeNode? { 16 | if (root == null) return TreeNode(target) 17 | 18 | var node: TreeNode? = root 19 | var pre: TreeNode? = null 20 | 21 | while (node != null) { 22 | pre = node 23 | if (node.value > target) { 24 | node = node.left 25 | } else if (node.value < target) { 26 | node = node.right 27 | } else { 28 | } 29 | } 30 | 31 | if (node == null && pre != null) { 32 | node = TreeNode(target) 33 | if (node!!.value > pre.value) { 34 | pre.right = node 35 | } else { 36 | pre.left = node 37 | } 38 | } 39 | 40 | return root 41 | } 42 | 43 | 44 | // recursion 45 | private fun insert1(root: TreeNode?, target: Int): TreeNode? { 46 | if (root == null) { 47 | val node = TreeNode(target) 48 | return node 49 | } 50 | 51 | if (root.value > target) { 52 | root.left = insert(root.left, target) 53 | } 54 | 55 | if (root.value < target) { 56 | root.right = insert(root.right, target) 57 | } 58 | 59 | return root 60 | } 61 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/bst/Med_98_ValidateBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.bst 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/4 9 | * @desc: 10 | */ 11 | object Med_98_ValidateBST { 12 | fun isValidBST(root: TreeNode?): Boolean { 13 | return isValid(root) 14 | } 15 | 16 | // in order traversal and check if is sorted 17 | private fun isValid(root: TreeNode?): Boolean { 18 | if (root == null) return true 19 | 20 | val stk = LinkedList() 21 | stk.addLast(root) 22 | var node: TreeNode? = null 23 | val list = mutableListOf() 24 | var pre: TreeNode? = null 25 | 26 | while (!stk.isEmpty()) { 27 | node = stk.getLast() // peek 28 | 29 | if (node != null) { 30 | node = stk.removeLast() 31 | // in order 32 | 33 | node?.right?.let { 34 | stk.addLast(it) 35 | } 36 | 37 | 38 | stk.addLast(node) 39 | stk.addLast(null) 40 | 41 | node?.left?.let { 42 | stk.addLast(it) 43 | } 44 | } else { 45 | stk.removeLast() 46 | node = stk.removeLast()?:continue 47 | 48 | if (pre != null && pre.value >= node.value) { 49 | return false 50 | } 51 | pre = node 52 | } 53 | } 54 | 55 | return true 56 | } 57 | 58 | // in order traversal and check if is sorted 59 | private fun isValid1(root: TreeNode?): Boolean { 60 | if (root == null) return true 61 | 62 | val stk = LinkedList() 63 | stk.addLast(root) 64 | var node: TreeNode? = null 65 | val list = mutableListOf() 66 | 67 | while (!stk.isEmpty()) { 68 | node = stk.getLast() // peek 69 | 70 | if (node != null) { 71 | node = stk.removeLast() 72 | // in order 73 | 74 | node?.right?.let { 75 | stk.addLast(it) 76 | } 77 | 78 | 79 | stk.addLast(node) 80 | stk.addLast(null) 81 | 82 | node?.left?.let { 83 | stk.addLast(it) 84 | } 85 | } else { 86 | stk.removeLast() 87 | node = stk.removeLast() 88 | 89 | node?.let { 90 | list.add(it.value) 91 | } 92 | } 93 | } 94 | 95 | // traversal and check 96 | var pre = list[0] 97 | for (i in 1..(list.size - 1)) { 98 | if (pre >= list[i]) { 99 | return false 100 | } 101 | pre = list[i] 102 | } 103 | 104 | return true 105 | } 106 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/Easy_062_UniquePath.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp 2 | 3 | import com.boycoder.utils.WrongResultException 4 | import com.boycoder.utils.asserts 5 | 6 | /** 7 | For the path(m, n), it comes from path(m - 1, n) and path(m, n - 1) 8 | 1. recursion 9 | 2. recursion + cache + state compress 10 | 3. dp 11 | */ 12 | object Easy_062_UniquePath { 13 | var cache: IntArray = intArrayOf() 14 | val BASE = 101 15 | 16 | fun uniquePaths(m: Int, n: Int): Int { 17 | return path3(m, n) 18 | } 19 | 20 | // dp 21 | private fun path3(m: Int, n: Int): Int { 22 | // dp with init 23 | val dp: Array = Array(m + 1){ IntArray(n + 1){1} } 24 | 25 | for (i in 2..m) { 26 | for (j in 2..n) { 27 | dp[i][j] = dp[i][j - 1] + dp[i - 1][j] 28 | } 29 | } 30 | 31 | return dp[m][n] 32 | } 33 | 34 | // out of time 35 | private fun path1(m: Int, n: Int): Int { 36 | if (m == 1 || n == 1) return 1 37 | 38 | return path1(m, n - 1) + path1(m - 1, n) 39 | } 40 | 41 | // recursion + cache + state compress 42 | private fun path2(state: Int): Int { 43 | val m = state / BASE 44 | val n = state % BASE 45 | 46 | if (m == 1 || n == 1) return 1 47 | 48 | if (cache[state] != 0) { 49 | return cache[state] 50 | } 51 | 52 | val res = path2(m * BASE + n - 1) + path2((m - 1) * BASE + n) 53 | cache[state] = res 54 | 55 | return res 56 | } 57 | } 58 | fun main() { 59 | val res = Easy_062_UniquePath.uniquePaths(3, 7) 60 | asserts(res, 28) 61 | println(res) 62 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/Easy_070_ClimbStairs.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/3 8 | * @desc: 9 | */ 10 | object Easy_070_ClimbStairs { 11 | private val cache = hashMapOf() 12 | fun climbStairs(n: Int): Int { 13 | return climb2(n) 14 | } 15 | 16 | // dp 17 | private fun climb(n: Int): Int { 18 | if (n == 1) return 1 19 | if (n == 2) return 2 20 | 21 | val dp = IntArray(n + 1) 22 | 23 | dp[1] = 1 24 | dp[2] = 2 25 | 26 | for (i in 3..n) { 27 | dp[i] = dp[i - 1] + dp[i - 2] 28 | } 29 | 30 | return dp[n] 31 | } 32 | 33 | /** 34 | * This problems is similar to [com.boycoder.problems.dp.knapsack.Med_377_CombinationSumIV.count3] 35 | * We got array:[1,2], and target n 36 | * Get all the permutations count. 37 | * So it become a knapsack problem. 38 | */ 39 | private fun climb2(n: Int): Int { 40 | val dp = IntArray(n + 1) 41 | 42 | dp[0] = 1 43 | 44 | for (i in 1..n) { 45 | for (j in 1..2) 46 | if (i >= j) { 47 | dp[i] = dp[i] + dp[i - j] 48 | } 49 | } 50 | 51 | return dp[n] 52 | } 53 | 54 | // recursion + cache 55 | private fun climb1(n: Int): Int { 56 | if (n == 1) return 1 57 | if (n == 2) return 2 58 | 59 | if (cache.getOrDefault(n, 0) != 0) { 60 | return cache.getOrDefault(n, 0) 61 | } 62 | 63 | val v = climb(n - 1) + climb(n - 2) 64 | cache.put(n, v) 65 | 66 | return v 67 | } 68 | } 69 | 70 | fun main() { 71 | val res = Easy_070_ClimbStairs.climbStairs(3) 72 | asserts(res, 3) 73 | println(res) 74 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/Easy_509_Fibonacci.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/3 8 | * @desc: 9 | */ 10 | object Easy_509_Fibonacci { 11 | private val cache = IntArray(31) 12 | 13 | fun fib(n: Int): Int { 14 | return count(n) 15 | } 16 | 17 | // dp + space optimize 18 | // a little bit like slide window 19 | private fun count(n: Int): Int { 20 | if (n == 0) return 0 21 | if (n == 1 || n == 2) return 1 22 | 23 | var dp1 = 1 24 | var dp2 = 1 25 | 26 | for (i in 3..n) { 27 | val temp = dp1 + dp2 28 | dp1 = dp2 29 | dp2 = temp 30 | } 31 | 32 | return dp2 33 | } 34 | 35 | // dp 36 | private fun count3(n: Int): Int { 37 | if (n == 0) return 0 38 | if (n == 1 || n == 2) return 1 39 | 40 | val dp = IntArray(31) 41 | dp[1] = 1 42 | dp[2] = 1 43 | 44 | for (i in 3..n) { 45 | dp[i] = dp[i - 1] + dp[i - 2] 46 | } 47 | 48 | return dp[n] 49 | } 50 | 51 | // recursion + cache 52 | private fun count2(n: Int): Int { 53 | if (n == 0) return 0 54 | if (n == 1 || n == 2) return 1 55 | 56 | if (cache[n] != 0) { 57 | return cache[n] 58 | } 59 | val v = count(n - 1) + count(n - 2) 60 | cache[n] = v 61 | return v 62 | } 63 | 64 | // recursion 65 | private fun count1(n: Int): Int { 66 | if (n == 0) return 0 67 | if (n == 1 || n == 2) return 1 68 | return count(n - 1) + count(n - 2) 69 | } 70 | } 71 | 72 | fun main() { 73 | val res = Easy_509_Fibonacci.fib(4) 74 | asserts(res, 4) 75 | println(res) 76 | 77 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/Easy_746_ClimbStairsCost.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/3 8 | * @desc: 9 | */ 10 | object Easy_746_ClimbStairsCost { 11 | private var cost: IntArray = intArrayOf() 12 | private var cache = intArrayOf() 13 | 14 | fun minCostClimbingStairs(cost: IntArray): Int { 15 | this.cost = cost 16 | cache = IntArray(cost.size + 1) 17 | return min(cost.size) 18 | } 19 | 20 | // dp 21 | private fun min(n: Int): Int { 22 | val dp = IntArray(n + 1) 23 | dp[0] = cost[0] 24 | dp[1] = cost[1] 25 | 26 | for (i in 2..n) { 27 | val cost = cost.getOrNull(i)?:0 28 | dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost 29 | } 30 | 31 | return dp[n] 32 | } 33 | 34 | /** 35 | * To get to n, we need to pay: Math.min(dfs(n - 1) + cost[n], dfs(n - 2) + cost[n]) 36 | */ 37 | private fun min1(n: Int): Int { 38 | if (n == 0) return cost[0] 39 | if (n == 1) return cost[1] 40 | 41 | if (cache[n] != 0) { 42 | return cache[n] 43 | } 44 | 45 | val v = cost.getOrNull(n)?:0 46 | val res = Math.min(min1(n - 1) + v, min1(n - 2) + v) 47 | cache[n] = res 48 | return res 49 | } 50 | } 51 | 52 | fun main() { 53 | val res = Easy_746_ClimbStairsCost.minCostClimbingStairs(intArrayOf(1, 100, 1, 1, 1, 100, 1, 1, 100, 1)) 54 | asserts(res, 6) 55 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/Med_096_UniqueBST.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/3 8 | * @desc: 9 | */ 10 | /** 11 | Ideal 12 | For an array, we pick a position to generate the BST, 13 | the left side become left child, right side become right child. 14 | For the child, it can split until it become 1 15 | */ 16 | object Med_096_UniqueBST { 17 | private var cache = intArrayOf() 18 | fun numTrees(n: Int): Int { 19 | // cache = IntArray(n + 1) 20 | return num(n) 21 | } 22 | 23 | // dp 24 | private fun num(n: Int): Int { 25 | val dp = IntArray(n + 1) 26 | 27 | dp[0] = 1 28 | dp[1] = 1 29 | 30 | for (i in 2..n) { 31 | for (j in 0 until i) { 32 | dp[i] = dp[i] + dp[j] * dp[i - j - 1] 33 | } 34 | } 35 | 36 | return dp[n] 37 | } 38 | 39 | // recursion + cache 40 | private fun num2(n: Int): Int { 41 | if (n == 0) return 1 42 | if (n == 1) return 1 43 | if (n == 2) return 2 44 | 45 | if (cache[n] != 0) { 46 | return cache[n] 47 | } 48 | 49 | var count = 0 50 | for (i in 0 until n) { 51 | count = count + num2(i) * num2(n - i - 1) 52 | } 53 | 54 | cache[n] = count 55 | 56 | return count 57 | } 58 | 59 | // recursion 60 | private fun num1(n: Int): Int { 61 | if (n == 0) return 1 62 | if (n == 1) return 1 63 | if (n == 2) return 2 64 | 65 | var count = 0 66 | for (i in 0 until n) { 67 | count = count + num1(i) * num1(n - i - 1) 68 | } 69 | 70 | return count 71 | } 72 | } 73 | 74 | fun main() { 75 | val res = Med_096_UniqueBST.numTrees(3) 76 | asserts(res, 5) 77 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/Med_343_IntegerBreak.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/3 8 | * @desc: 9 | */ 10 | /** 11 | 6 7 8 9 12 | 1, 5 1,6 1,7 1,8 13 | 3, 3 2,5 2,6 2,7 14 | 3,4 3,5 3,6 15 | 4,5 16 | 17 | break(i) = (2 * max(break(i - 2), i - 2), 3 * max(break(i - 3), i - 3) 18 | */ 19 | object Med_343_IntegerBreak { 20 | private var cache = intArrayOf() 21 | 22 | fun integerBreak(n: Int): Int { 23 | cache = IntArray(n + 1) 24 | 25 | return maxInt(n) 26 | } 27 | 28 | /** 29 | dp 30 | */ 31 | private fun maxInt(n: Int): Int { 32 | // space can be optimize 33 | val dp = IntArray(n + 1) 34 | dp[2] = 1 35 | 36 | 37 | for (i in 3..n) { 38 | val index = (i + 1) / 2 39 | for (j in 2..index) { 40 | dp[i] = Math.max(dp[i], Math.max(dp[i - j], i - j) * Math.max(dp[j], j)) 41 | } 42 | } 43 | 44 | return dp[n] 45 | } 46 | 47 | // recursion + cache 48 | private fun maxInt1(n: Int): Int { 49 | if (n == 2) return 1 50 | if (n == 3) return 2 51 | if (n == 4) return 4 52 | 53 | if (cache[n] != 0) { 54 | return cache[n] 55 | } 56 | 57 | var res = 0 58 | val index = (n + 1) / 2 59 | 60 | for (i in 2..index) { 61 | res = Math.max(res, Math.max(maxInt1(n - i), n - i) * Math.max(maxInt1(i), i)) 62 | } 63 | 64 | cache[n] = res 65 | 66 | return res 67 | } 68 | } 69 | 70 | fun main() { 71 | val res = Med_343_IntegerBreak.integerBreak(8) 72 | asserts(res, 18) 73 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/editdistance/Easy_392_IsSubSequence.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.editdistance 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/17 8 | * @desc: https://leetcode-cn.com/problems/is-subsequence/ 9 | * 1. two pointer 10 | * 2. dfs 11 | * 3. dp 12 | */ 13 | object Easy_392_IsSubSequence { 14 | var array1 = charArrayOf() 15 | var array2 = charArrayOf() 16 | 17 | fun isSubsequence(s: String, t: String): Boolean { 18 | array1 = s.toCharArray() 19 | array2 = t.toCharArray() 20 | return dp() 21 | } 22 | 23 | /** 24 | * The basic case of Edit Distance 25 | * 26 | * dp[i][j] means the LCS length 27 | * Check dp[array1.size][array2.size] == array1.size 28 | */ 29 | private fun dp(): Boolean { 30 | val dp = Array(array1.size + 1) { IntArray(array2.size + 1)} 31 | 32 | for (i in 1..array1.size) { 33 | for (j in 1..array2.size) { 34 | if (array1[i - 1] == array2[j - 1]) { 35 | dp[i][j] = dp[i - 1][j - 1] + 1 36 | } else { 37 | /** 38 | * Here is little bit tricky 39 | * | 40 | * abc 41 | * ahbgdc 42 | * | 43 | * Because array1[2] != array2[3] 44 | * So dp[2][3] = dp[2][2] 45 | */ 46 | dp[i][j] = dp[i][j - 1] 47 | } 48 | } 49 | } 50 | 51 | return dp[array1.size][array2.size] == array1.size 52 | } 53 | 54 | /** 55 | * dp 56 | * 57 | * Bottom to top 58 | * 59 | * dp[i][j] means the result of array1[0, i-1] array2[0, j-1] 60 | */ 61 | private fun dp1(): Boolean { 62 | val dp = Array(array1.size) { BooleanArray(array2.size) } 63 | 64 | // init when i = 0 65 | for (j in 0 until array2.size) { 66 | if (array2[j] == array1[0]) { 67 | for (k in j until array2.size) { 68 | dp[0][k] = true 69 | } 70 | break 71 | } 72 | } 73 | 74 | // init when j = 0, by default is false 75 | 76 | for (i in 1 until array1.size) { 77 | for (j in 1 until array2.size) { 78 | if (dp[i][j - 1]) { 79 | dp[i][j] = true 80 | } else { 81 | if (array1[i] == array2[j]) { 82 | dp[i][j] = dp[i - 1][j - 1] 83 | } 84 | } 85 | } 86 | } 87 | 88 | return dp[array1.size - 1][array2.size - 1] 89 | } 90 | 91 | /** 92 | * dfs 93 | * 94 | * Try to break it down into smaller problems 95 | */ 96 | private fun isValid1(end1: Int, end2: Int): Boolean { 97 | if (end1 < 0) { 98 | return true 99 | } else if (end2 < 0) { 100 | return false 101 | } 102 | 103 | return if (array1[end1] == array2[end2]) { 104 | isValid1(end1 - 1, end2 - 1) 105 | } else { 106 | isValid1(end1, end2 - 1) 107 | } 108 | } 109 | 110 | /** 111 | * check s is subsequence of t. 112 | */ 113 | private fun isValid(s: String, t: String): Boolean { 114 | // two pointer go through two string. 115 | var start1 = 0 116 | var start2 = 0 117 | 118 | val array1 = s.toCharArray() 119 | val array2 = t.toCharArray() 120 | 121 | while (start1 < s.length && start2 < t.length) { 122 | if (array1[start1] == array2[start2]) { 123 | start1++ 124 | start2++ 125 | } else { 126 | start2++ 127 | } 128 | } 129 | 130 | return start1 == s.length 131 | } 132 | } 133 | 134 | fun main() { 135 | val res = Easy_392_IsSubSequence.isSubsequence("abc", "ahbgdc") 136 | asserts(res, true) 137 | val res1 = Easy_392_IsSubSequence.isSubsequence("axc", "ahbgdc") 138 | asserts(res1, false) 139 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/editdistance/Hard_115_DistinctSubSequence.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.editdistance 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/19 8 | * @desc: https://leetcode-cn.com/problems/distinct-subsequences/ 9 | * 10 | * dfs 11 | * dp 12 | */ 13 | object Hard_115_DistinctSubSequence { 14 | private var array1 = charArrayOf() 15 | private var array2 = charArrayOf() 16 | 17 | private val memo = hashMapOf() 18 | fun numDistinct(s: String, t: String): Int { 19 | array1 = s.toCharArray() 20 | array2 = t.toCharArray() 21 | memo.clear() 22 | return count2() 23 | } 24 | 25 | /** 26 | * dp[i][j] means array1[0, i -1] and array2[0, j - 1] count 27 | */ 28 | private fun count2(): Int { 29 | val dp = Array(array1.size + 1) { IntArray(array2.size + 1)} 30 | 31 | // init when t is empty string 32 | for (i in 0..array1.size) { 33 | dp[i][0] = 1 34 | } 35 | 36 | // init when s is empty string 37 | for (j in 1..array2.size) { 38 | dp[0][j] = 0 39 | } 40 | 41 | for (i in 1..array1.size) { 42 | for (j in 1..array2.size) { 43 | if (array1[i - 1] == array2[j - 1]) { 44 | // use (i - 1) or not 45 | dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j] 46 | } else { 47 | dp[i][j] = dp[i - 1][j] 48 | } 49 | } 50 | } 51 | 52 | return dp[array1.size][array2.size] 53 | } 54 | 55 | /** 56 | * dfs + cache 57 | */ 58 | private fun count1(end1: Int, end2: Int): Int { 59 | // base case 60 | if (end2 < 0) { 61 | return 1 62 | } 63 | 64 | if (end1 < 0) { 65 | return 0 66 | } 67 | 68 | val key = "${end1}.${end2}" 69 | if (memo.containsKey(key)) { 70 | return memo.getOrDefault(key, 0) 71 | } 72 | 73 | var res = 0 74 | if (array1[end1] == array2[end2]) { 75 | res = count1(end1 - 1, end2 - 1) + count1(end1 - 1, end2) 76 | } else { 77 | res = count1(end1 - 1, end2) 78 | } 79 | 80 | memo[key] = res 81 | return res 82 | } 83 | 84 | /** 85 | * dfs 86 | */ 87 | private fun count(end1: Int, end2: Int): Int { 88 | // base case 89 | if (end2 < 0) { 90 | return 1 91 | } 92 | 93 | if (end1 < 0) { 94 | return 0 95 | } 96 | 97 | if (array1[end1] == array2[end2]) { 98 | return count(end1 - 1, end2 - 1) + count(end1 - 1, end2) 99 | } else { 100 | return count(end1 - 1, end2) 101 | } 102 | } 103 | } 104 | 105 | fun main() { 106 | val res = Hard_115_DistinctSubSequence.numDistinct("rabbbit", "rabbit") 107 | asserts(res, 3) 108 | val res1 = Hard_115_DistinctSubSequence.numDistinct("babgbag", "bag") 109 | asserts(res1, 5) 110 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/editdistance/Hard_72_EditDistance.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.editdistance 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/20 8 | * @desc: https://leetcode-cn.com/problems/edit-distance/ 9 | */ 10 | object Hard_72_EditDistance { 11 | var array1 = charArrayOf() 12 | var array2 = charArrayOf() 13 | 14 | val memo = hashMapOf() 15 | 16 | fun minDistance(word1: String, word2: String): Int { 17 | array1 = word1.toCharArray() 18 | array2 = word2.toCharArray() 19 | return distance2() 20 | } 21 | 22 | /** 23 | * dp 24 | * 25 | * dp[i][j] means array1[0, i - 1] array2[0, j - 1] edit distance 26 | */ 27 | private fun distance2(): Int { 28 | val dp = Array(array1.size + 1) { IntArray(array2.size + 1) } 29 | 30 | // init when word2 is empty string 31 | for (i in 0..array1.size) { 32 | dp[i][0] = i 33 | } 34 | 35 | // init when word1 is empty string 36 | for (j in 0..array2.size) { 37 | dp[0][j] = j 38 | } 39 | 40 | // calculation 41 | for (i in 1..array1.size) { 42 | for (j in 1..array2.size) { 43 | if (array1[i - 1] == array2[j - 1]) { 44 | dp[i][j] = dp[i - 1][j - 1] 45 | } else { 46 | // delete word1 47 | val res1 = dp[i - 1][j] + 1 48 | // delete word2 49 | val res2 = dp[i][j - 1] + 1 50 | // replace 51 | val res3 = dp[i - 1][j - 1] + 1 52 | dp[i][j] = Math.min(res1, Math.min(res2, res3)) 53 | } 54 | } 55 | } 56 | 57 | return dp[array1.size][array2.size] 58 | } 59 | 60 | /** 61 | * dfs + cache 62 | */ 63 | private fun distance1(end1: Int, end2: Int): Int { 64 | if (end1 < 0) { 65 | return end2 + 1 66 | } 67 | 68 | if (end2 < 0) { 69 | return end1 + 1 70 | } 71 | 72 | var result = 0 73 | val key = "${end1}.${end2}" 74 | if (memo.containsKey(key)) { 75 | return memo.getOrDefault(key, 0) 76 | } 77 | 78 | if (array1[end1] == array2[end2]) { 79 | result = distance(end1 - 1, end2 - 1) 80 | } else { 81 | // replace 82 | val res1 = distance(end1 - 1, end2 -1) + 1 83 | // delete word1 84 | val res2 = distance(end1 - 1, end2) + 1 85 | // delete word2 86 | val res3 = distance(end1, end2 - 1) + 1 87 | 88 | result = Math.min(res1, Math.min(res2, res3)) 89 | } 90 | 91 | memo[key] = result 92 | return result 93 | } 94 | 95 | 96 | /** 97 | * dfs 98 | * 99 | * Try to break it into smaller problems 100 | */ 101 | private fun distance(end1: Int, end2: Int): Int { 102 | if (end1 < 0) { 103 | return end2 + 1 104 | } 105 | 106 | if (end2 < 0) { 107 | return end1 + 1 108 | } 109 | 110 | if (array1[end1] == array2[end2]) { 111 | return distance(end1 - 1, end2 - 1) 112 | } else { 113 | // replace 114 | val res1 = distance(end1 - 1, end2 -1) + 1 115 | // delete word1 116 | val res2 = distance(end1 - 1, end2) + 1 117 | // delete word2 118 | val res3 = distance(end1, end2 - 1) + 1 119 | 120 | return Math.min(res1, Math.min(res2, res3)) 121 | } 122 | } 123 | } 124 | 125 | fun main() { 126 | val res = Hard_72_EditDistance.minDistance("horse", "ros") 127 | asserts(res, 3) 128 | val res1 = Hard_72_EditDistance.minDistance("intention", "execution") 129 | asserts(res1, 5) 130 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/editdistance/Med_583_DeleteForTwoString.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.editdistance 2 | 3 | import com.boycoder.problems.dp.subsequence.Med_1143_LongestCommonSubsequence 4 | import com.boycoder.utils.asserts 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/19 9 | * @desc: 10 | */ 11 | object Med_583_DeleteForTwoString { 12 | var array1 = charArrayOf() 13 | var array2 = charArrayOf() 14 | 15 | val memo = hashMapOf() 16 | fun minDistance(word1: String, word2: String): Int { 17 | array1 = word1.toCharArray() 18 | array2 = word2.toCharArray() 19 | 20 | return distance3() 21 | } 22 | 23 | /** 24 | * dp[i][j] means array1[0, i - 1] array2[0, j - 1] distance 25 | */ 26 | private fun distance3(): Int { 27 | val dp = Array(array1.size + 1) {IntArray(array2.size + 1)} 28 | 29 | // init when word2 is empty string 30 | for (i in 0..array1.size) { 31 | dp[i][0] = i 32 | } 33 | 34 | // init when word1 is empty string 35 | for (j in 0..array2.size) { 36 | dp[0][j] = j 37 | } 38 | 39 | for (i in 1..array1.size) { 40 | for (j in 1..array2.size) { 41 | if (array1[i - 1] == array2[j - 1]) { 42 | dp[i][j] = dp[i - 1][j - 1] 43 | } else { 44 | val res1 = dp[i - 1][j] + 1 45 | val res2 = dp[i][j - 1] + 1 46 | val res3 = dp[i - 1][j - 1] + 2 47 | dp[i][j] = Math.min(res1, Math.min(res2, res3)) 48 | } 49 | } 50 | } 51 | 52 | return dp[array1.size][array2.size] 53 | } 54 | 55 | /** 56 | * dfs + cache 57 | */ 58 | private fun distance2(end1: Int, end2: Int): Int { 59 | if (end1 < 0) { 60 | return end2 + 1 61 | } 62 | 63 | if (end2 < 0) { 64 | return end1 + 1 65 | } 66 | 67 | val key = "${end1}.${end2}" 68 | if (memo.containsKey(key)) { 69 | return memo.getOrDefault(key, 0) 70 | } 71 | 72 | var result = 0 73 | 74 | if (array1[end1] == array2[end2]) { 75 | result = distance1(end1 - 1, end2 - 1) 76 | } else { 77 | val res1 = distance1(end1 - 1, end2) + 1 78 | val res2 = distance1(end1, end2 - 1) + 1 79 | val res3 = distance1(end1 - 1, end2 - 1) + 2 80 | result = Math.min(res1, Math.min(res2, res3)) 81 | } 82 | 83 | memo[key] = result 84 | return result 85 | } 86 | 87 | /** 88 | * dfs 89 | */ 90 | private fun distance1(end1: Int, end2: Int): Int { 91 | if (end1 < 0) { 92 | return end2 + 1 93 | } 94 | 95 | if (end2 < 0) { 96 | return end1 + 1 97 | } 98 | 99 | if (array1[end1] == array2[end2]) { 100 | return distance1(end1 - 1, end2 - 1) 101 | } else { 102 | val res1 = distance1(end1 - 1, end2) + 1 103 | val res2 = distance1(end1, end2 - 1) + 1 104 | val res3 = distance1(end1 - 1, end2 - 1) + 2 105 | return Math.min(res1, Math.min(res2, res3)) 106 | } 107 | } 108 | 109 | /** 110 | * Calculate the size of LCS and return m + n - 2 * LCS 111 | */ 112 | private fun distance(word1: String, word2: String): Int { 113 | val lcs = Med_1143_LongestCommonSubsequence.longestCommonSubsequence(word1, word2) 114 | return word1.length + word2.length - 2 * lcs 115 | } 116 | } 117 | 118 | fun main() { 119 | val res = Med_583_DeleteForTwoString.minDistance("sea", "eat") 120 | asserts(res, 2) 121 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/editdistance/Med_647_PalindromicSubString.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.editdistance 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/20 8 | * @desc: https://leetcode-cn.com/problems/palindromic-substrings/ 9 | */ 10 | object Med_647_PalindromicSubString { 11 | var array = charArrayOf() 12 | fun countSubstrings(s: String): Int { 13 | array = s.toCharArray() 14 | return count() 15 | } 16 | 17 | private fun count(): Int { 18 | val memo = Array(array.size){ IntArray(array.size){ -1 } } 19 | var res = 0 20 | // split every sub string and check it by using dfs 21 | for (start in 0 until array.size) { 22 | for (end in start until array.size) { 23 | res = res + (if (dfs1(memo, start, end)) 1 else 0) 24 | } 25 | } 26 | 27 | return res 28 | } 29 | 30 | private fun dfs1(memo: Array, start: Int, end: Int): Boolean { 31 | if (start == end) return true 32 | if (start + 1 == end) return array[start] == array[end] 33 | 34 | if (memo[start][end] != -1) { 35 | return memo[start][end] == 1 36 | } 37 | 38 | val res = if (array[start] == array[end]) dfs(start + 1, end - 1) else false 39 | memo[start][end] = if (res) 1 else 0 40 | 41 | return res 42 | } 43 | 44 | private fun dfs(start: Int, end: Int): Boolean { 45 | if (start == end) return true 46 | if (start + 1 == end) return array[start] == array[end] 47 | 48 | return if (array[start] == array[end]) dfs(start + 1, end - 1) else false 49 | } 50 | } 51 | 52 | fun main() { 53 | val res = Med_647_PalindromicSubString.countSubstrings("abc") 54 | asserts(res, 3) 55 | val res1 = Med_647_PalindromicSubString.countSubstrings("aaa") 56 | asserts(res1, 6) 57 | } 58 | -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/knapsack/Med_322_CoinChange.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.knapsack 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/15 8 | * @desc: https://leetcode-cn.com/problems/coin-change/ 9 | * Similar to the [Med_279_PerfectSquares.find4] 10 | * This problem can use bfs too. 11 | */ 12 | object Med_322_CoinChange { 13 | private var min = Int.MAX_VALUE 14 | private var coins = intArrayOf() 15 | 16 | private var memo = hashMapOf() 17 | 18 | fun coinChange(coins: IntArray, amount: Int): Int { 19 | this.coins = coins 20 | val res = change3(amount) 21 | return if (res == Int.MAX_VALUE) -1 else res 22 | } 23 | 24 | /** 25 | * coins[] is value[] and weight[], and amount is capacity. 26 | * We need to know the min amount that fit the knapsack. 27 | * dp[j] means: to fill the j capacity knapsack, min amount is dp[j] 28 | * This is similar to [Med_377_CombinationSumIV.count3] 29 | */ 30 | private fun change3(amount: Int): Int { 31 | val dp = IntArray(amount + 1) { Int.MAX_VALUE } 32 | dp[0] = 0 33 | 34 | // loop coins 35 | for (i in 0 until coins.size) { 36 | // loop capacity 37 | for (j in coins[i]..amount) { 38 | if (dp[j - coins[i]] != Int.MAX_VALUE) { 39 | // if it's initialized 40 | dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1) 41 | } 42 | } 43 | } 44 | 45 | return dp[amount] 46 | } 47 | 48 | /** 49 | * dp 50 | * bottom to top 51 | */ 52 | private fun change2(amount: Int): Int { 53 | val dp = IntArray(amount + 1) 54 | dp[0] = 0 55 | 56 | /** 57 | * this loop is equal to the recursion [change1] 58 | */ 59 | for (i in 1..amount) { 60 | // This loop will count dp[1]/dp[2]/dp[3]...dp[amount] 61 | var min = Int.MAX_VALUE 62 | for (j in 0 until coins.size) { 63 | if (i >= coins[j] && dp[i - coins[j]] < min) { 64 | min = dp[i - coins[j]] + 1 65 | } 66 | } 67 | dp[i] = min 68 | } 69 | 70 | return dp[amount] 71 | } 72 | 73 | /** 74 | * dfs + cache 75 | * For every coin, we can choose more than one time. 76 | */ 77 | private fun change1(amount: Int): Int { 78 | if (amount < 0) { 79 | return Int.MAX_VALUE 80 | } 81 | 82 | if (amount == 0) { 83 | return 0 84 | } 85 | 86 | val key = "${amount}" 87 | if (memo.containsKey(key)) { 88 | return memo.getOrDefault(key, Int.MAX_VALUE) 89 | } 90 | 91 | var min = Int.MAX_VALUE 92 | for (i in 0 until coins.size) { 93 | var res = change1(amount - coins[i]) 94 | if (res != Int.MAX_VALUE) { 95 | res++ 96 | } 97 | min = Math.min(min, res) 98 | } 99 | 100 | memo[key] = min 101 | return min 102 | } 103 | 104 | /** 105 | * For every coin, we can choose more than one time. 106 | */ 107 | private fun change(size: Int, amount: Int) { 108 | if (amount == 0) { 109 | if (size < min) { 110 | min = size 111 | } 112 | return 113 | } 114 | 115 | if (amount < 0) { 116 | return 117 | } 118 | 119 | for (i in 0 until coins.size) { 120 | change(size + 1, amount - coins[i]) 121 | } 122 | } 123 | } 124 | 125 | fun main() { 126 | val res = Med_322_CoinChange.coinChange(intArrayOf(1, 2, 5), 11) 127 | asserts(res, 3) 128 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/knapsack/Med_377_CombinationSumIV.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.knapsack 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/15 8 | * @desc: https://leetcode-cn.com/problems/combination-sum-iv/ 9 | */ 10 | object Med_377_CombinationSumIV { 11 | var array = intArrayOf() 12 | var target = 0 13 | 14 | var count = 0 15 | var memo = hashMapOf() 16 | 17 | fun combinationSum4(nums: IntArray, target: Int): Int { 18 | this.array = nums 19 | this.target = target 20 | this.count = 0 21 | return count3() 22 | } 23 | 24 | /** 25 | * dp[i] means, to get target i, we can get dp[i] permutations 26 | */ 27 | private fun count3(): Int { 28 | val dp = IntArray(target + 1) 29 | dp[0] = 1 30 | 31 | /** 32 | * To get permutations count, we loop capacity then loop value 33 | * This is different from [Med_518_CoinChangeII.find5] 34 | */ 35 | for (i in 1..target) { 36 | for (j in 0 until array.size) { 37 | if (i >= array[j]) 38 | dp[i] = dp[i] + dp[i - array[j]] 39 | } 40 | } 41 | 42 | return dp[target] 43 | } 44 | 45 | /** 46 | * dfs + cache 47 | */ 48 | private fun count2(target: Int): Int { 49 | if (target == 0) return 1 50 | 51 | if (memo.containsKey(target)) { 52 | return memo.getOrDefault(target, 0) 53 | } 54 | 55 | var res = 0 56 | 57 | /** 58 | * To get the permutations count. 59 | * Every time, we loop the whole array. 60 | */ 61 | for (num in array) { 62 | if (target >= num) { 63 | res = res + count2(target - num) 64 | } 65 | } 66 | memo[target] = res 67 | 68 | return res 69 | } 70 | 71 | /** 72 | * dfs 73 | */ 74 | private fun count1(target: Int): Int { 75 | if (target == 0) return 1 76 | var res = 0 77 | 78 | /** 79 | * To get the permutations count. 80 | * Every time, we loop the whole array. 81 | */ 82 | for (num in array) { 83 | if (target >= num) { 84 | res = res + count1(target - num) 85 | } 86 | } 87 | return res 88 | } 89 | 90 | /** 91 | * dfs that can get all the permutations 92 | * If we need the permutations, we can use a path to store. 93 | */ 94 | private fun count(target: Int) { 95 | if (target < 0) return 96 | if (target == 0) { 97 | count++ 98 | return 99 | } 100 | 101 | for (i in 0 until array.size) { 102 | 103 | count(target - array[i]) 104 | } 105 | } 106 | } 107 | 108 | fun main() { 109 | val res = Med_377_CombinationSumIV.combinationSum4(intArrayOf(1, 2, 3), 4) 110 | asserts(res, 7) 111 | val res1 = Med_377_CombinationSumIV.combinationSum4(intArrayOf(9), 3) 112 | asserts(res1, 0) 113 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/robber/Med_198_HouseRobber.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.robber 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: 9 | */ 10 | object Med_198_HouseRobber { 11 | private var array = intArrayOf() 12 | private var memo = intArrayOf() 13 | 14 | fun rob(nums: IntArray): Int { 15 | array = nums 16 | memo = IntArray(nums.size){ -1 } 17 | return dp() 18 | } 19 | 20 | /** 21 | * dp 22 | * 23 | * bottom to top 24 | * 25 | * dp[i] means, for [0, 1] array, we can the max value 26 | * 27 | * dp[i] = Math.max(dp[i - 2] + array[i], dp[i - 1]) 28 | */ 29 | private fun dp(): Int { 30 | val size = array.size 31 | // corner case 32 | if (size == 1) return array[0] 33 | if (size == 2) return Math.max(array[1], array[0]) 34 | 35 | val dp = IntArray(array.size) { -1 } 36 | dp[0] = array[0] 37 | dp[1] = Math.max(array[1], array[0]) 38 | 39 | for (i in 2 until size) { 40 | dp[i] = Math.max(dp[i - 2] + array[i], dp[i - 1]) 41 | } 42 | 43 | return dp[size - 1] 44 | } 45 | 46 | /** 47 | * dfs + cache 48 | */ 49 | private fun dfs1(end: Int): Int { 50 | if (end == 0) return array[end] 51 | if (end == 1) return Math.max(array[0], array[1]) 52 | 53 | if (memo[end] != -1) { 54 | return memo[end] 55 | } 56 | 57 | val res = Math.max(dfs(end - 2) + array[end], dfs(end - 1)) 58 | 59 | memo[end] = res 60 | return res 61 | } 62 | 63 | /** 64 | * dfs 65 | * 66 | * Break the array into smaller array 67 | */ 68 | private fun dfs(end: Int): Int { 69 | if (end == 0) return array[end] 70 | if (end == 1) return Math.max(array[0], array[1]) 71 | 72 | /** 73 | * If we take the end, then we can take (end - 1), we can just take (end - 2) 74 | */ 75 | val res = Math.max(dfs(end - 2) + array[end], dfs(end - 1)) 76 | 77 | return res 78 | } 79 | } 80 | 81 | fun main() { 82 | val res = Med_198_HouseRobber.rob(intArrayOf(1,2,3,1)) 83 | asserts(res, 4) 84 | val res1 = Med_198_HouseRobber.rob(intArrayOf(2,7,9,3,1)) 85 | asserts(res1, 12) 86 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/robber/Med_213_HouseRobberII.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.robber 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: https://leetcode-cn.com/problems/house-robber-ii/ 9 | */ 10 | object Med_213_HouseRobberII { 11 | 12 | private var array = intArrayOf() 13 | private var memo = intArrayOf() 14 | 15 | /** 16 | * In this case, because the head and tail of array is connected. 17 | * So, the max value is max(dfs(0, end - 1), dfs(1, end)), and end = size - 1 18 | */ 19 | fun rob(nums: IntArray): Int { 20 | // Be careful here, ArrayIndexOutOfBoundsException 21 | if (nums.size == 1) return nums[0] 22 | 23 | this.array = nums 24 | this.memo = IntArray(array.size){ -1 } 25 | val end = nums.size - 1 26 | val memo1 = IntArray(array.size){ -1 } 27 | val memo2 = IntArray(array.size){ -1 } 28 | // return Math.max(dfs1(0, end - 1, memo1), dfs1(1, end, memo2)) 29 | return Math.max(dp(0, end - 1), dp(1, end)) 30 | } 31 | 32 | private fun dp(start: Int, end: Int): Int { 33 | val dp = IntArray(end + 1) { -1 } 34 | 35 | if (end == start) return array[start] 36 | if (end - start == 1) return Math.max(array[start], array[end]) 37 | 38 | dp[start] = array[start] 39 | dp[start + 1] = Math.max(array[start], array[start + 1]) 40 | 41 | // calculate dp[start + 1] to dp[end] 42 | for (i in (start + 2)..end) { 43 | dp[i] = Math.max(dp[i - 2] + array[i], dp[i - 1]) 44 | } 45 | 46 | return dp[end] 47 | } 48 | 49 | /** 50 | * Because the dfs1 will be called two times from outside. 51 | * So, we can not just use one memo array to cache. 52 | * To solve this problem, we can pass two memo array. 53 | */ 54 | private fun dfs1(start: Int, end: Int, memo: IntArray): Int { 55 | if (end == start) return array[end] 56 | if (end == start + 1) return Math.max(array[start], array[start + 1]) 57 | 58 | if (memo[end] != -1) { 59 | return memo[end] 60 | } 61 | /** 62 | * If we take the end, then we can take (end - 1), we can just take (end - 2) 63 | */ 64 | val res = Math.max(dfs1(start, end - 2, memo) + array[end], dfs1(start, end - 1, memo)) 65 | 66 | memo[end] = res 67 | return res 68 | } 69 | 70 | /** 71 | * dfs 72 | * 73 | * Break the array into smaller array 74 | * Time out 75 | */ 76 | private fun dfs(start: Int, end: Int): Int { 77 | if (end == start) return array[end] 78 | if (end == start + 1) return Math.max(array[start], array[start + 1]) 79 | 80 | /** 81 | * If we take the end, then we can take (end - 1), we can just take (end - 2) 82 | */ 83 | val res = Math.max(dfs(start, end - 2) + array[end], dfs(start, end - 1)) 84 | 85 | return res 86 | } 87 | } 88 | 89 | fun main() { 90 | val res = Med_213_HouseRobberII.rob(intArrayOf(2,3,2)) 91 | asserts(res, 3) 92 | val res1 = Med_213_HouseRobberII.rob(intArrayOf(1,2,3,1)) 93 | asserts(res1, 4) 94 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/stock/Med_121_SellStock.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.stock 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: 9 | */ 10 | object Med_121_SellStock { 11 | fun maxProfit(array: IntArray): Int { 12 | return max3(array) 13 | } 14 | 15 | /** 16 | * dp 17 | * 18 | * dp[i][0] means hold stock the max profit 19 | * dp[i][1] means sell stock the max profit 20 | * 21 | * dp[i][0] = max(dp[i - 1][0], -price[i]) 22 | * dp[i][1] = max(dp[1 - 1][1], dp[i - 1][0] + price[i]) 23 | */ 24 | private fun max3(price: IntArray): Int { 25 | val dp = Array(price.size) { IntArray(2) } 26 | 27 | // first day to hold stock 28 | dp[0][0] = -price[0] 29 | 30 | // first day to not hold stock 31 | dp[0][1] = 0 32 | 33 | for (i in 1 until price.size) { 34 | dp[i][0] = Math.max(dp[i - 1][0], -price[i]) 35 | dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + price[i]) 36 | } 37 | 38 | // max profit must sell stock 39 | return dp[price.size - 1][1] 40 | } 41 | 42 | /** 43 | * dfs 44 | * Try to break the array into smaller array. 45 | * When array.size = 1, low = array[0], and profit = 0. 46 | * Use res[0] for lowest price, and res[1] for the max profit 47 | */ 48 | private fun max2(array: IntArray, end: Int): IntArray { 49 | if (end == 0) return intArrayOf(array[0], 0) 50 | 51 | val res = max2(array, end - 1) 52 | val max = Math.max(res[1], array[end] - res[0]) 53 | val low = Math.min(array[end], res[0]) 54 | 55 | return intArrayOf(low, max) 56 | } 57 | 58 | /** 59 | * Greedy 60 | * There is no need to loop the array two times. 61 | */ 62 | private fun max1(array: IntArray): Int { 63 | var low = Int.MAX_VALUE 64 | var max = 0 65 | 66 | for (i in array) { 67 | low = Math.min(low, i) 68 | max = Math.max(max, i - low) 69 | } 70 | 71 | return max 72 | } 73 | 74 | /** 75 | * Brute Force 76 | * Two loop, buy loop the [0, size) 77 | * sell loop [i + 1, size) 78 | * Then we find the max profit 79 | */ 80 | private fun max(array: IntArray): Int { 81 | var max = 0 82 | 83 | for (buy in 0 until array.size) { 84 | for (sell in buy + 1 until array.size) { 85 | if (array[sell] - array[buy] > max) { 86 | max = array[sell] - array[buy] 87 | } 88 | } 89 | } 90 | 91 | return max 92 | } 93 | } 94 | 95 | fun main() { 96 | val res = Med_121_SellStock.maxProfit(intArrayOf(7, 1, 5, 3, 6, 4)) 97 | asserts(res, 5) 98 | val res1 = Med_121_SellStock.maxProfit(intArrayOf(7,6,4,3,1)) 99 | asserts(res1, 0) 100 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/stock/Med_122_SellStockII.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.stock 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/ 9 | */ 10 | object Med_122_SellStockII { 11 | fun maxProfit(array: IntArray): Int { 12 | return max1(array) 13 | } 14 | 15 | /** 16 | * dp 17 | * 18 | * dp[i][0] means hold stock, the max profit 19 | * dp[i][1] means not hold stock, the max profit 20 | */ 21 | private fun max1(price: IntArray): Int { 22 | val dp = Array(price.size) {IntArray(2)} 23 | 24 | // first day hold stock 25 | dp[0][0] = -price[0] 26 | // first day not hold stock 27 | dp[0][1] = 0 28 | 29 | for (i in 1 until price.size) { 30 | // yesterday already hold or yesterday not hold, but buy today 31 | dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - price[i]) 32 | // yesterday already not hold or yesterday hold, but today sell 33 | dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + price[i]) 34 | } 35 | 36 | return dp[price.size - 1][1] 37 | } 38 | 39 | /** 40 | * Greedy 41 | * Collect all the raising range 42 | */ 43 | private fun max(price: IntArray): Int { 44 | var max = 0 45 | for (i in 1 until price.size) { 46 | if (price[i] > price[i - 1]) { 47 | max = max + price[i] - price[i - 1] 48 | } 49 | } 50 | 51 | return max 52 | } 53 | } 54 | 55 | fun main() { 56 | val res = Med_122_SellStockII.maxProfit(intArrayOf(7, 1, 5, 3, 6, 4)) 57 | asserts(res, 7) 58 | val res1 = Med_122_SellStockII.maxProfit(intArrayOf(1,2,3,4,5)) 59 | asserts(res1, 4) 60 | val res2 = Med_122_SellStockII.maxProfit(intArrayOf(7,6,4,3,1)) 61 | asserts(res2, 0) 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/stock/Med_123_SellStockIII.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.stock 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/ 9 | */ 10 | object Med_123_SellStockIII { 11 | fun maxProfit(array: IntArray): Int { 12 | return max(array) 13 | } 14 | 15 | /** 16 | * dp 17 | * 18 | * dp[i][0] means no operation 19 | * dp[i][1] means first time buy 20 | * dp[i][2] means first time sell 21 | * dp[i][3] means second time buy 22 | * dp[i][4] means second time sell 23 | * 24 | * dp[i][0] = 0 25 | * dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - price[i]) 26 | * dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + price[i]) 27 | * dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - price[i]) 28 | * dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + price[i]) 29 | */ 30 | private fun max(price: IntArray): Int { 31 | val dp = Array(price.size) { IntArray(5) } 32 | 33 | // init 34 | dp[0][0] = 0 35 | dp[0][1] = -price[0] 36 | dp[0][2] = 0 37 | dp[0][3] = -price[0] 38 | dp[0][4] = 0 39 | 40 | for (i in 1 until price.size) { 41 | dp[i][0] = 0 42 | dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - price[i]) 43 | dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + price[i]) 44 | dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - price[i]) 45 | dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + price[i]) 46 | } 47 | 48 | return dp[price.size - 1][4] 49 | } 50 | } 51 | 52 | fun main() { 53 | val res = Med_123_SellStockIII.maxProfit(intArrayOf(3, 3, 5, 0, 0, 3, 1, 4)) 54 | asserts(res, 6) 55 | val res1 = Med_123_SellStockIII.maxProfit(intArrayOf(1, 2, 3, 4, 5)) 56 | asserts(res1, 4) 57 | val res2 = Med_123_SellStockIII.maxProfit(intArrayOf(7, 6, 4, 3, 1)) 58 | asserts(res2, 0) 59 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/stock/Med_188_SellStockIV.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.stock 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/ 9 | * This problem is based on [Med_123_SellStockIII] 10 | */ 11 | object Med_188_SellStockIV { 12 | fun maxProfit(k: Int, array: IntArray): Int { 13 | return max(k, array) 14 | } 15 | 16 | /** 17 | * dp[i][2 * k + 1] 18 | * 19 | * dp[i][0] means no operation 20 | * dp[i][1] means first time buy 21 | * dp[i][2] means first time sell 22 | * dp[i][3] means second time buy 23 | * dp[i][4] means second time sell 24 | * 25 | * dp[i][0] = 0 26 | * dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - price[i]) 27 | * dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + price[i]) 28 | * dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - price[i]) 29 | * dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + price[i]) 30 | */ 31 | private fun max(k: Int, price: IntArray): Int { 32 | val dp = Array(price.size) { IntArray(2 * k + 1) } 33 | 34 | // init 35 | for (i in 0..2 * k) { 36 | when { 37 | i == 0 -> dp[0][i] = 0 38 | i % 2 == 1 -> dp[0][i] = -price[0] 39 | i % 2 == 0 -> dp[0][i] = 0 40 | } 41 | } 42 | 43 | for (i in 1 until price.size) { 44 | for(j in 0..2 * k) { 45 | when { 46 | j == 0 -> dp[i][j] = 0 47 | j % 2 == 1 -> dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - price[i]) 48 | j % 2 == 0 -> dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + price[i]) 49 | } 50 | } 51 | } 52 | 53 | return dp[price.size - 1][2*k] 54 | } 55 | 56 | } 57 | 58 | fun main() { 59 | val res = Med_188_SellStockIV.maxProfit(2, intArrayOf(2, 4, 1)) 60 | asserts(res, 2) 61 | val res1 = Med_188_SellStockIV.maxProfit(2, intArrayOf(3, 2, 6, 5, 0, 3)) 62 | asserts(res1, 7) 63 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/stock/Med_309_SellStockCoolDown.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.stock 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ 9 | */ 10 | object Med_309_SellStockCoolDown { 11 | fun maxProfit(array: IntArray): Int { 12 | return max(array) 13 | } 14 | 15 | /** 16 | * dp[i][j] 17 | * 18 | * dp[i][0] means hold stock 19 | * dp[i][1] means not hold stock, and after cool down 20 | * dp[i][2] means not hold stock, today sell stock 21 | * dp[i][3] means not hold stock, today is cool down day 22 | * 23 | * from state 1 and 3 24 | * dp[i][0] = Math.max(dp[i - 1][0], Math.max(dp[i - 1][1] - price[i], dp[i - 1][3] - price[i])) 25 | * from itself or 3 26 | * dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][3]) 27 | * from state 0 28 | * dp[i][2] = dp[i - 1][0] + price[i] 29 | * from state 2 30 | * dp[i][3] = dp[i - 1][2] 31 | */ 32 | private fun max(price: IntArray): Int { 33 | val dp = Array(price.size) { IntArray(4) } 34 | 35 | // init 36 | dp[0][0] = -price[0] 37 | dp[0][1] = 0 38 | dp[0][2] = 0 39 | dp[0][3] = 0 40 | 41 | for (i in 1 until price.size) { 42 | dp[i][0] = Math.max(dp[i - 1][0], Math.max(dp[i - 1][1] - price[i], dp[i - 1][3] - price[i])) 43 | dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][3]) 44 | dp[i][2] = dp[i - 1][0] + price[i] 45 | dp[i][3] = dp[i - 1][2] 46 | } 47 | 48 | return Math.max(dp[price.size - 1][1], Math.max(dp[price.size - 1][2], dp[price.size - 1][3])) 49 | } 50 | 51 | } 52 | 53 | fun main() { 54 | val res = Med_309_SellStockCoolDown.maxProfit(intArrayOf(1,2,3,0,2)) 55 | asserts(res, 3) 56 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/stock/Med_714_SellStockFee.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.stock 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: 9 | */ 10 | object Med_714_SellStockFee { 11 | /** 12 | * dp[i][j] 13 | * 14 | * dp[i][0] means hold stock 15 | * dp[i][1] means not hold stock 16 | */ 17 | fun maxProfit(prices: IntArray, fee: Int): Int { 18 | val dp = Array(prices.size) { IntArray(2) } 19 | 20 | // init 21 | dp[0][0] = -prices[0] 22 | dp[0][1] = 0 23 | 24 | for (i in 1 until prices.size) { 25 | dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]) 26 | dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee) 27 | } 28 | 29 | // careful here 30 | return Math.max(dp[prices.size - 1][0], dp[prices.size - 1][1]) 31 | } 32 | } 33 | 34 | fun main() { 35 | val res = Med_714_SellStockFee.maxProfit(intArrayOf(1, 3, 2, 8, 4, 9), 2) 36 | asserts(res, 8) 37 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/subsequence/Easy_53_MaxSubArray.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.subsequence 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/17 8 | * @desc: https://leetcode-cn.com/problems/maximum-subarray/ 9 | */ 10 | object Easy_53_MaxSubArray { 11 | 12 | private var array = intArrayOf() 13 | 14 | fun maxSubArray(nums: IntArray): Int { 15 | if (nums.size == 0) return 0 16 | this.array = nums 17 | return find1() 18 | } 19 | 20 | /** 21 | * dp 22 | * 23 | * dp[i] means the max sum that end with (i - 1) 24 | * dp[i] = if (dp[i - 1] < 0) array[i] else dp[i - 1] + array[i] 25 | * or 26 | * dp[i] = Math.max(dp[i - 1] + array[i], array[i]) 27 | */ 28 | private fun find1(): Int { 29 | val dp = IntArray(array.size) 30 | //init 31 | dp[0] = array[0] 32 | var max = dp[0] 33 | 34 | // calculate dp[i] and get max value 35 | for (i in 1 until array.size) { 36 | dp[i] = Math.max(dp[i - 1] + array[i], array[i]) 37 | max = Math.max(max, dp[i]) 38 | } 39 | 40 | return max 41 | } 42 | 43 | /** 44 | * Greedy + two pointer 45 | * -2,1,-3,4,-1,2,1,-5,4 46 | * | 47 | */ 48 | private fun find(): Int{ 49 | val size = array.size 50 | 51 | var max = Int.MIN_VALUE 52 | var left = 0 53 | var right = 0 54 | 55 | 56 | while (left < size && right < size) { 57 | var sum = 0 58 | // here can be optimized 59 | for (i in left..right) { 60 | sum = sum + array[i] 61 | } 62 | max = Math.max(max, sum) 63 | 64 | // here control the window slide 65 | if (sum < 0) { 66 | right++ 67 | left = right 68 | } else { 69 | right++ 70 | } 71 | } 72 | 73 | return max 74 | } 75 | } 76 | 77 | fun main() { 78 | val res = Easy_53_MaxSubArray.maxSubArray(intArrayOf(-2,1,-3,4,-1,2,1,-5,4)) 79 | asserts(res, 6) 80 | val res1 = Easy_53_MaxSubArray.maxSubArray(intArrayOf(4,-1,2,1)) 81 | asserts(res1, 6) 82 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/subsequence/Med_1035_UncrossedLines.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.subsequence 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/17 8 | * @desc: https://leetcode-cn.com/problems/uncrossed-lines/ 9 | * This is similar to [Med_1143_LongestCommonSubsequence] 10 | * DP + DFS 11 | */ 12 | object Med_1035_UncrossedLines { 13 | fun maxUncrossedLines(nums1: IntArray, nums2: IntArray): Int { 14 | return dp(nums1, nums2) 15 | } 16 | 17 | /** 18 | * dp 19 | * 20 | * dp[i][j] means nums1[0, i -1], nums2[0, j - 1] LCS size 21 | */ 22 | private fun dp(nums1: IntArray, nums2: IntArray): Int { 23 | val size1 = nums1.size 24 | val size2 = nums2.size 25 | 26 | // dp array + init 27 | val dp = Array(size1 + 1){IntArray(size2 + 1) { 0 } } 28 | 29 | for (i in 1..size1) { 30 | for (j in 1..size2) { 31 | if (nums1[i - 1] == nums2[j - 1]) { 32 | dp[i][j] = dp[i - 1][j - 1] + 1 33 | } else { 34 | dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]) 35 | } 36 | } 37 | } 38 | 39 | return dp[size1][size2] 40 | } 41 | } 42 | 43 | fun main() { 44 | val res = Med_1035_UncrossedLines.maxUncrossedLines(intArrayOf(1, 4, 2), intArrayOf(1, 2, 4)) 45 | asserts(res, 2) 46 | val res1 = Med_1035_UncrossedLines.maxUncrossedLines(intArrayOf(2,5,1,2,5), intArrayOf(10,5,2,1,5,2)) 47 | asserts(res1, 3) 48 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/subsequence/Med_300_LongestIncreasingSubsequence.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.subsequence 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: https://leetcode-cn.com/problems/longest-increasing-subsequence/ 9 | */ 10 | object Med_300_LongestIncreasingSubsequence { 11 | /** 12 | * dp 13 | * 14 | * dp[i] means [0, i] array LIS size 15 | * Try to break this problem into smaller problems 16 | */ 17 | fun find(array: IntArray): Int { 18 | // init default value as 1 19 | val dp = IntArray(array.size) { 1 } 20 | for (i in 1 until array.size) { 21 | for (j in 0 until i) { 22 | if (array[i] > array[j]) { 23 | // dp[i] default value is 1 24 | dp[i] = Math.max(dp[i], dp[j] + 1) 25 | } 26 | } 27 | } 28 | 29 | return dp[array.size - 1] 30 | } 31 | } 32 | 33 | fun main() { 34 | val res = Med_300_LongestIncreasingSubsequence.find(intArrayOf(10,9,2,5,3,7,101,18)) 35 | asserts(res, 4) 36 | val res1 = Med_300_LongestIncreasingSubsequence.find(intArrayOf(0,1,0,3,2,3)) 37 | asserts(res1, 4) 38 | val res2 = Med_300_LongestIncreasingSubsequence.find(intArrayOf(7,7,7,7,7,7,7)) 39 | asserts(res2, 1) 40 | } 41 | -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/subsequence/Med_674_LongestIncreasingSubsequenceII.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.subsequence 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: https://leetcode-cn.com/problems/longest-continuous-increasing-subsequence/ 9 | * 10 | */ 11 | object Med_674_LongestIncreasingSubsequenceII { 12 | 13 | fun longestSize(array: IntArray): Int { 14 | return find1(array) 15 | } 16 | 17 | 18 | /** 19 | * dp 20 | * 21 | * This problem can be break into smaller problems. 22 | * dp[i] means, the size of LIS that end with array[i] 23 | * if (array[i + 1] > array[i]) dp[i + 1] = dp[i] + 1 24 | * 25 | * For the longest subsequence could end with any index 26 | * so we need to store the max value of dp[i] 27 | */ 28 | private fun find(array: IntArray): Int { 29 | // init every dp with 1 30 | val dp = IntArray(array.size) { 1 } 31 | 32 | var max = 1 33 | 34 | for (i in 1 until array.size) { 35 | if (array[i] > array[i - 1]) { 36 | dp[i] = dp[i - 1] + 1 37 | } 38 | 39 | max = Math.max(max, dp[i]) 40 | } 41 | 42 | return max 43 | } 44 | 45 | /** 46 | * Greedy 47 | * 48 | * Use a loop go through the array. 49 | * count = 1 50 | * count = if (array[i] > array[i - 1]) count++ else 1 51 | * mark the max value 52 | */ 53 | private fun find1(array: IntArray): Int { 54 | var count = 1 55 | var max = 1 56 | 57 | for (i in 1 until array.size) { 58 | if (array[i] > array[i - 1]) { 59 | count++ 60 | } else { 61 | count = 1 62 | } 63 | 64 | max = Math.max(count, max) 65 | } 66 | 67 | return max 68 | } 69 | 70 | /** 71 | * Brute Force + pruning 72 | * We assume the longest subsequence should start from i 73 | * And we loop j [i + 1, size), if (array[i] > array[i - 1]) count++ else break 74 | */ 75 | private fun find2(array: IntArray): Int { 76 | var max = 0 77 | 78 | for (i in 0 until array.size) { 79 | var count = 1 80 | for (j in i + 1 until array.size) { 81 | if (array[j] > array[j - 1]) { 82 | count++ 83 | } else { 84 | break 85 | } 86 | } 87 | max = Math.max(count, max) 88 | } 89 | return max 90 | } 91 | } 92 | 93 | fun main() { 94 | val res = Med_674_LongestIncreasingSubsequenceII.longestSize(intArrayOf(1, 3, 5, 4, 7)) 95 | asserts(res, 3) 96 | val res1 = Med_674_LongestIncreasingSubsequenceII.longestSize(intArrayOf(2, 2, 2, 2, 2)) 97 | asserts(res1, 1) 98 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/dp/subsequence/Med_718_LongestSubArray.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.dp.subsequence 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/16 8 | * @desc: https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray/ 9 | * dfs + dp 10 | */ 11 | object Med_718_LongestSubArray { 12 | 13 | fun longestSize(array1: IntArray, array2: IntArray): Int { 14 | return find1(array1, array2) 15 | } 16 | 17 | private fun find1(array1: IntArray, array2: IntArray): Int { 18 | val dp = Array(array1.size) {IntArray(array2.size) { 0 } } 19 | var max = 0 20 | 21 | // init when i = 0 22 | for (j in 0 until array2.size) { 23 | if (array2[j] == array1[0]) { 24 | dp[0][j] = 1 25 | } 26 | } 27 | 28 | // init when j = 0 29 | for (i in 0 until array1.size) { 30 | if (array1[i] == array2[0]) { 31 | dp[i][0] = 1 32 | } 33 | } 34 | 35 | for (i in 1 until array1.size) { 36 | for (j in 1 until array2.size) { 37 | if (array1[i] == array2[j]) { 38 | dp[i][j] = dp[i - 1][j - 1] + 1 39 | } 40 | max = Math.max(max, dp[i][j]) 41 | } 42 | } 43 | 44 | return max 45 | } 46 | 47 | 48 | /** 49 | * dp 50 | * 51 | * dp[i][j] means array1[0, i - 1] and array2[0, j - 1] longest size 52 | */ 53 | private fun find(array1: IntArray, array2: IntArray): Int { 54 | val dp = Array(array1.size + 1) { IntArray(array2.size + 1) { 0 } } 55 | var max = 0 56 | 57 | for (i in 1..array1.size) { 58 | for (j in 1..array2.size) { 59 | if (array1[i - 1] == array2[j - 1]) { 60 | dp[i][j] = dp[i - 1][j - 1] + 1 61 | } 62 | 63 | max = Math.max(max, dp[i][j]) 64 | } 65 | } 66 | 67 | return max 68 | } 69 | 70 | } 71 | 72 | fun main() { 73 | val res = Med_718_LongestSubArray.longestSize(intArrayOf(1, 2, 3, 2, 1), intArrayOf(3, 2, 1, 4, 7)) 74 | asserts(res, 3) 75 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/greedy/Easy_445_AssignCookie.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.greedy 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/4 6 | * @desc: 7 | */ 8 | object Easy_445_AssignCookie { 9 | 10 | fun findContentChildren(g: IntArray, s: IntArray): Int { 11 | g.sort() 12 | s.sort() 13 | var i = g.size - 1 14 | var j = s.size - 1 15 | 16 | while (i >= 0 && j >= 0) { 17 | if (g[i] <= s[j]) { 18 | i-- 19 | j-- 20 | } else { 21 | i-- 22 | } 23 | } 24 | return s.size - 1 - j 25 | } 26 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/hash/Easy_001_TwoSum.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.hash 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/4 6 | * @desc: 7 | */ 8 | object Easy_001_TwoSum { 9 | fun twoSum(nums: IntArray, target: Int): IntArray { 10 | val map = mutableMapOf() 11 | 12 | nums.forEachIndexed { index, value -> 13 | if (map.contains(target - value)) { 14 | return intArrayOf(map[target - value]!!, index) 15 | } 16 | map.put(value, index) 17 | } 18 | return intArrayOf() 19 | } 20 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/hash/Easy_202_HappyNumber.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.hash 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/happy-number/ 7 | */ 8 | object Easy_202_HappyNumber { 9 | fun isHappy(n: Int): Boolean { 10 | if (n < 0) return false 11 | 12 | val set = mutableSetOf() 13 | return check(n, set) 14 | } 15 | 16 | // tail recursion 17 | private fun check(n: Int, set: MutableSet): Boolean { 18 | if (n == 1) return true 19 | if (set.contains(n)) return false 20 | set.add(n) 21 | 22 | var sum = 0 23 | var num = n 24 | val digits = ArrayList() 25 | 26 | while (num > 0) { 27 | digits.add(num % 10) 28 | num = num / 10 29 | } 30 | 31 | var newNum = 0 32 | digits.forEach { 33 | newNum = it * it + newNum 34 | } 35 | 36 | return check(newNum, set) 37 | } 38 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/hash/Easy_242_Anagram.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.hash 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/valid-anagram/ 7 | */ 8 | object Easy_242_Anagram { 9 | fun isAnagram(s: String, t: String): Boolean { 10 | val array1 = s.toCharArray() 11 | val array2 = t.toCharArray() 12 | 13 | val result = IntArray(26) 14 | 15 | for (char in array1) { 16 | result[char - 'a']++ 17 | } 18 | 19 | for (char in array2) { 20 | result[char - 'a']-- 21 | } 22 | 23 | return !result.any { it != 0 } 24 | } 25 | 26 | fun isAnagram1(s: String, t: String): Boolean { 27 | val map: MutableMap = mutableMapOf() 28 | 29 | s.toList().forEach{ 30 | map.put(it, map.getOrDefault(it, 0) + 1) 31 | } 32 | t.toList().forEach { 33 | map.put(it, map.getOrDefault(it, 0) - 1) 34 | } 35 | 36 | return !map.values.any { it != 0 } 37 | } 38 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/hash/Easy_349_IntersectionTwoArray.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.hash 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/intersection-of-two-arrays/ 7 | */ 8 | object Easy_349_IntersectionTwoArray { 9 | fun intersection(nums1: IntArray, nums2: IntArray): IntArray { 10 | val set1 = mutableSetOf() 11 | val set2 = mutableSetOf() 12 | val result = ArrayList() 13 | 14 | for (i in nums1) { 15 | set1.add(i) 16 | } 17 | 18 | for (j in nums2) { 19 | set2.add(j) 20 | } 21 | 22 | set1.forEach { 23 | if (set2.contains(it)) { 24 | result.add(it) 25 | } 26 | } 27 | 28 | return result.toIntArray() 29 | } 30 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/hash/Med_15_3Sum.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.hash 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/3sum/ 7 | */ 8 | object Med_15_3Sum { 9 | fun threeSum(nums: IntArray): List> { 10 | nums.sort() 11 | 12 | var i = 0 13 | val list: MutableList> = mutableListOf() 14 | 15 | while (i < nums.size) { 16 | if (nums[i] > 0) return list 17 | var left = i + 1 18 | var right = nums.size - 1 19 | 20 | while (left < right) { 21 | if (nums[i] + nums[left] + nums[right] == 0) { 22 | list.add(listOf(nums[i], nums[left], nums[right])) 23 | while (left < right && nums[right] == nums.getOrNull(right - 1)) { 24 | right-- 25 | } 26 | while (left < right && nums[left] == nums.getOrNull(left + 1)) { 27 | left++ 28 | } 29 | left++ 30 | right-- 31 | } else if (nums[i] + nums[left] + nums[right] > 0) { 32 | while (left < right && nums[right] == nums.getOrNull(right - 1)) { 33 | right-- 34 | } 35 | right-- 36 | } else if (nums[i] + nums[left] + nums[right] < 0) { 37 | while (left < right && nums[left] == nums.getOrNull(left + 1)) { 38 | left++ 39 | } 40 | left++ 41 | } 42 | } 43 | 44 | while (nums[i] == nums.getOrNull(i + 1)) { 45 | i++ 46 | } 47 | 48 | i++ 49 | } 50 | 51 | return list 52 | } 53 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/hash/Med_18_4Sum.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.hash 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/4sum/ 7 | */ 8 | object Med_18_4Sum { 9 | fun fourSum(nums: IntArray, target: Int): List> { 10 | nums.sort() 11 | val list: MutableList> = mutableListOf() 12 | 13 | var i = 0 14 | var j = 0 15 | while (i < nums.size) { 16 | j = i + 1 17 | while (j < nums.size - 1) { 18 | 19 | var left = j + 1 20 | var right = nums.size -1 21 | while (left < right) { 22 | val sum = nums[i] + nums[j] + nums[left] + nums[right] 23 | 24 | if (sum > target) { 25 | while (left < right && nums[right] == nums[right - 1]) { 26 | right-- 27 | } 28 | right-- 29 | } else if (sum < target) { 30 | while (left < right && nums[left] == nums[left + 1]) { 31 | left++ 32 | } 33 | left++ 34 | } else { 35 | list.add(listOf(nums[i], nums[j], nums[left], nums[right])) 36 | while (left < right && nums[right] == nums[right - 1]) { 37 | right-- 38 | } 39 | right-- 40 | 41 | while (left < right && nums[left] == nums[left + 1]) { 42 | left++ 43 | } 44 | left++ 45 | } 46 | } 47 | while (j < nums.size - 1 && nums[j] == nums[j + 1]) { 48 | j++ 49 | } 50 | j++ 51 | 52 | } 53 | while (i < nums.size - 1 && nums[i] == nums[i + 1]) { 54 | i++ 55 | } 56 | i++ 57 | } 58 | return list 59 | } 60 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/hash/Med_383_RansomNote.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.hash 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/ransom-note/ 7 | */ 8 | object Med_383_RansomNote { 9 | fun canConstruct(ransomNote: String, magazine: String): Boolean { 10 | val array1 = IntArray(26) 11 | val array2 = IntArray(26) 12 | 13 | for (char in magazine.toCharArray()) { 14 | array1[char - 'a']++ 15 | } 16 | 17 | for (char in ransomNote.toCharArray()) { 18 | array1[char - 'a']-- 19 | } 20 | 21 | return !array1.any { it < 0 } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/hash/Med_454_FourSum.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.hash 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Med_454_FourSum { 9 | fun fourSumCount(nums1: IntArray, nums2: IntArray, nums3: IntArray, nums4: IntArray): Int { 10 | val map1 = hashMapOf() 11 | var res = 0 12 | 13 | 14 | for (i in nums1) { 15 | for (j in nums2) { 16 | map1.put(i + j, map1.getOrDefault(i + j, 0) + 1) 17 | } 18 | } 19 | 20 | for (i in nums3) { 21 | for (j in nums4) { 22 | if (map1.contains(-i -j)) { 23 | res = res + map1[-i-j]!! 24 | } 25 | } 26 | } 27 | 28 | return res 29 | } 30 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/linkedList/Easy_203_RemoveLinkedListElements.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.linkedList 2 | 3 | import com.boycoder.basis.datastructure.ListNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/4 8 | * @desc: 9 | */ 10 | object Easy_203_RemoveLinkedListElements { 11 | 12 | fun removeElements(head: ListNode?, target: Int): ListNode? { 13 | if (head == null) return head 14 | val dummy = ListNode(-1, head) 15 | 16 | var slow: ListNode? = dummy 17 | var fast: ListNode? = head 18 | while (fast != null && slow != null) { 19 | if (fast.value == target) { 20 | slow.next = fast.next 21 | fast = fast.next 22 | } else { 23 | slow = fast 24 | fast = fast.next 25 | } 26 | } 27 | 28 | return dummy.next 29 | } 30 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/linkedList/Easy_206_ReverseLinkedList.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.linkedList 2 | 3 | import com.boycoder.basis.datastructure.ListNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: https://leetcode-cn.com/problems/reverse-linked-list/ 9 | */ 10 | object Easy_206_ReverseLinkedList { 11 | fun reverseList(head: ListNode?): ListNode? { 12 | if (head == null) return head 13 | return reverseRecur(head) 14 | } 15 | 16 | fun reverseRecur(head: ListNode): ListNode? { 17 | if (head.next == null) return head 18 | 19 | var result = reverseRecur(head.next!!) 20 | 21 | var next = head.next!! 22 | next.next = head 23 | head.next = null 24 | return result 25 | } 26 | 27 | /** 28 | * Ideal 29 | *1. Two pointer 30 | * 31 | * 1 -> 2 -> 3 -> 4 -> 5 -> null 32 | * 33 | * null <- 1 -> 2 -> 3 -> 4 -> 5 -> null 34 | */ 35 | fun reverseList1(head: ListNode?): ListNode? { 36 | if (head == null) return head 37 | 38 | var pre: ListNode? = null 39 | var cur = head 40 | var next: ListNode? = null 41 | 42 | while (cur != null) { 43 | next = cur.next 44 | cur.next = pre 45 | pre = cur 46 | cur = next 47 | } 48 | 49 | return pre 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/boycoder/problems/linkedList/Med_19_RemoveNthFromEnd.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.linkedList 2 | 3 | import com.boycoder.basis.datastructure.ListNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_19_RemoveNthFromEnd { 11 | fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { 12 | var dummy = ListNode(0, head) 13 | 14 | var right = dummy 15 | var left = dummy 16 | 17 | // let right pointer go faster 18 | var step = n 19 | while (step > 0) { 20 | right = right.next!! 21 | step-- 22 | } 23 | 24 | // then let right pointer go to the end 25 | while (right.next != null) { 26 | right = right.next!! 27 | left = left.next!! 28 | } 29 | 30 | // delete the Nth node 31 | left.next = left.next!!.next 32 | 33 | return dummy.next 34 | } 35 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/linkedList/Med_24_SwapNodeInPair.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.linkedList 2 | 3 | import com.boycoder.basis.datastructure.ListNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: https://leetcode-cn.com/problems/swap-nodes-in-pairs/ 9 | */ 10 | object Med_24_SwapNodeInPair { 11 | fun swapPairs(head: ListNode?): ListNode? { 12 | if (head == null || head.next == null) return head 13 | 14 | var dummy = ListNode(0, head) 15 | var pre = dummy 16 | var cur = head 17 | 18 | while (cur != null && cur.next != null) { 19 | 20 | val next = cur.next!! 21 | val nextCur = next.next 22 | 23 | pre.next = next 24 | next.next = cur 25 | cur.next = nextCur 26 | 27 | pre = cur 28 | cur = nextCur 29 | } 30 | 31 | return dummy.next 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/com/boycoder/problems/monotone/Easy_155_MinStack.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.monotone 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | class Easy_155_MinStack { 9 | 10 | /** initialize your data structure here. */ 11 | val array = mutableListOf() 12 | val minArray = mutableListOf() 13 | 14 | fun push(x: Int) { 15 | array.add(x) 16 | val top = minArray.getOrNull(minArray.size - 1)?: Int.MAX_VALUE 17 | val min = Math.min(top, x) 18 | minArray.add(min) 19 | } 20 | 21 | fun pop() { 22 | array.removeAt(array.size - 1) 23 | minArray.removeAt(minArray.size - 1) 24 | } 25 | 26 | fun top(): Int { 27 | return array.get(array.size - 1) 28 | } 29 | 30 | fun getMin(): Int { 31 | return minArray.get(minArray.size - 1) 32 | } 33 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/monotone/Hard_42_TrapRainWater.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.monotone 2 | 3 | import com.boycoder.utils.asserts 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Hard_42_TrapRainWater { 12 | private var array = intArrayOf() 13 | fun trap(height: IntArray): Int { 14 | array = height 15 | return count1() 16 | } 17 | 18 | /** 19 | * Monotone stack 20 | */ 21 | private fun count1(): Int { 22 | var sum = 0 23 | // put index 24 | val stk = LinkedList() 25 | 26 | for (i in 0 until array.size) { 27 | while (!stk.isEmpty() && array[stk.getLast()] < array[i]) { 28 | val cur = stk.removeLast() 29 | if (stk.isEmpty()) break 30 | 31 | val left = stk.getLast() 32 | val right = i 33 | val low = Math.min(array[left], array[right]) 34 | if (low > array[cur]) { 35 | sum = sum + (right - left - 1) * (low - array[cur]) 36 | } 37 | } 38 | stk.addLast(i) 39 | } 40 | 41 | return sum 42 | } 43 | 44 | /** 45 | * Brute Force 46 | * 47 | * Count every col of water and then sum then up. 48 | */ 49 | fun count(): Int { 50 | var sum = 0 51 | 52 | for (i in 0 until array.size) { 53 | if (i == 0 || i == array.size - 1) { 54 | continue 55 | } 56 | 57 | // highest index to the left and right 58 | var left = i 59 | var right = i 60 | 61 | // here can be optimized by dp 62 | for (j in i - 1 downTo 0) { 63 | if (array[j] > array[left]) { 64 | left = j 65 | } 66 | } 67 | 68 | // here can be optimized by dp 69 | for (k in i + 1 until array.size) { 70 | if (array[k] > array[right]) { 71 | right = k 72 | } 73 | } 74 | 75 | val low = Math.min(array[left], array[right]) 76 | 77 | if (low > array[i]) { 78 | sum = sum + (low - array[i]) 79 | } 80 | } 81 | 82 | return sum 83 | } 84 | } 85 | 86 | fun main() { 87 | val res = Hard_42_TrapRainWater.trap(intArrayOf(0,1,0,2,1,0,1,3,2,1,2,1)) 88 | asserts(res, 6) 89 | val res1 = Hard_42_TrapRainWater.trap(intArrayOf(4,2,0,3,2,5)) 90 | asserts(res1, 9) 91 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/monotone/Med_1438_LongestSubArrayLimitedAbs.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.monotone 2 | 3 | import java.util.* 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_1438_LongestSubArrayLimitedAbs { 11 | fun longestSubarray(nums: IntArray, limit: Int): Int { 12 | // 4, 3, 2, 1 13 | val maxQueue = LinkedList() 14 | // 1, 2, 3, 4 15 | val minQueue = LinkedList() 16 | val len = nums.size 17 | var left = 0 18 | var right = 0 19 | var max = 0 20 | 21 | while (right < len) { 22 | while (!maxQueue.isEmpty() && maxQueue.getLast() < nums[right]) { 23 | maxQueue.removeLast() 24 | } 25 | 26 | while (!minQueue.isEmpty() && minQueue.getLast() > nums[right]) { 27 | minQueue.removeLast() 28 | } 29 | 30 | maxQueue.addLast(nums[right]) 31 | minQueue.addLast(nums[right]) 32 | 33 | while (maxQueue.getFirst() - minQueue.getFirst() > limit) { 34 | if (maxQueue.getFirst() == nums[left]) maxQueue.removeFirst() 35 | if (minQueue.getFirst() == nums[left]) minQueue.removeFirst() 36 | left++ 37 | } 38 | 39 | max = Math.max(max, right - left + 1) 40 | right++ 41 | } 42 | 43 | return max 44 | } 45 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/monotone/Med_739_DailyTemperatures.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.monotone 2 | 3 | import com.boycoder.utils.assertCollection 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Med_739_DailyTemperatures { 12 | private var array = intArrayOf() 13 | 14 | fun dailyTemperatures(temperatures: IntArray): IntArray { 15 | array = temperatures 16 | return find1() 17 | } 18 | 19 | /** 20 | * Brute Force 21 | */ 22 | private fun find(): IntArray { 23 | val res = IntArray(array.size) 24 | 25 | // calculate every index 26 | for (i in 0 until array.size) { 27 | for (j in i + 1 until array.size) { 28 | if (array[j] > array[i]) { 29 | res[i] = j - i 30 | break 31 | } 32 | } 33 | } 34 | 35 | return res 36 | } 37 | 38 | private fun find1(): IntArray { 39 | val res = IntArray(array.size) 40 | // 4, 3, 3, 2, 1 41 | val stk = ArrayDeque() 42 | 43 | for (i in 0 until array.size) { 44 | while (!stk.isEmpty() && array[stk.getLast()] < array[i]) { 45 | val index = stk.removeLast() 46 | res[index] = i - index 47 | } 48 | 49 | stk.addLast(i) 50 | } 51 | 52 | return res 53 | } 54 | } 55 | 56 | fun main() { 57 | val res = Med_739_DailyTemperatures.dailyTemperatures(intArrayOf(73, 74, 75, 71, 69, 72, 76, 73)) 58 | assertCollection(res.asList(), intArrayOf(1, 1, 4, 2, 1, 1, 0, 0).asList()) 59 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/monotone/Med_MaxQueue.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.monotone 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof/ 7 | * 8 | * This data structure should work as a normal queue, 9 | * and it can get the max value O(n) 10 | * 11 | * So, we should use a normal queue to hold the elements. 12 | * and another data structure to hold the max elements. 13 | */ 14 | class Med_MaxQueue() { 15 | 16 | private var queue: ArrayDeque 17 | private var max: ArrayDeque 18 | 19 | init { 20 | queue = ArrayDeque() 21 | max = ArrayDeque() 22 | } 23 | 24 | /** 25 | * get max 26 | */ 27 | fun max_value(): Int { 28 | if (max.size == 0) { 29 | return -1 30 | } 31 | 32 | return max.first() 33 | } 34 | 35 | /** 36 | * add last 37 | */ 38 | fun push_back(value: Int) { 39 | queue.addLast(value) 40 | 41 | if (max.size == 0) { 42 | max.addLast(value) 43 | return 44 | } 45 | 46 | // maintain the max queue 47 | // 4 2 1 add 3 48 | // 4 add 3 49 | while (max.size > 0 && max.last() < value) { 50 | max.removeLast() 51 | } 52 | 53 | max.addLast(value) 54 | } 55 | 56 | /** 57 | * remove first 58 | */ 59 | fun pop_front(): Int { 60 | if (queue.size == 0) { 61 | return -1 62 | } 63 | 64 | val res = queue.removeFirst() 65 | if (res == max.first()) { 66 | max.removeFirst() 67 | } 68 | 69 | return res 70 | } 71 | } 72 | 73 | fun main() { 74 | val queue = Med_MaxQueue() 75 | queue.push_back(1) 76 | queue.push_back(2) 77 | println(queue.max_value()) 78 | queue.pop_front() 79 | println(queue.max_value()) 80 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/pointers/Easy_35_SearchInsertPosition.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.pointers 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/21 8 | * @desc: 9 | */ 10 | object Easy_35_SearchInsertPosition { 11 | 12 | /** 13 | * 1, 3, 5, 6 14 | * | | 15 | * 16 | * 1, 3, 5, 6 17 | * | | 18 | * 19 | * 1, 3, 5, 6 20 | * | | 21 | * 1, 3, 5, 6 22 | * | | 23 | */ 24 | fun searchInsert(nums: IntArray, target: Int): Int { 25 | var left = 0 26 | var right = nums.size - 1 27 | 28 | while (left <= right) { 29 | val mid = (right + left) /2 30 | if (target > nums[mid]) { 31 | left = mid + 1 32 | } else if (target < nums[mid]) { 33 | right = mid - 1 34 | } else { 35 | return mid 36 | } 37 | } 38 | 39 | return left 40 | } 41 | } 42 | 43 | fun main() { 44 | val res = Easy_35_SearchInsertPosition.searchInsert(intArrayOf(1,3,5,6), 5) 45 | asserts(res, 2) 46 | val res1 = Easy_35_SearchInsertPosition.searchInsert(intArrayOf(1,3,5,6), 2) 47 | asserts(res1, 1) 48 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/stack/Easy_1047_RemoveDuplicate.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.stack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Easy_1047_RemoveDuplicate { 9 | fun removeDuplicates(s: String): String { 10 | val stk = ArrayDeque() 11 | val array = s.toCharArray() 12 | 13 | for (char in array) { 14 | if (stk.isEmpty()) { 15 | stk.addLast(char) 16 | } else if (stk.last() == char) { 17 | stk.removeLast() 18 | } else if (stk.last() != char) { 19 | stk.addLast(char) 20 | } 21 | } 22 | 23 | return stk.joinToString("") 24 | } 25 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/stack/Easy_20_ValidParentheses.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.stack 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/4 8 | * @desc: 9 | */ 10 | object Easy_20_ValidParentheses { 11 | fun isValid(s: String): Boolean { 12 | val stk = ArrayDeque() 13 | val array = s.toCharArray() 14 | 15 | for (char in array) { 16 | if (isLeft(char)) { 17 | stk.addLast(char) 18 | } else { 19 | if (stk.isEmpty()) return false 20 | 21 | if (!isMatching(stk.removeLast(), char)) { 22 | return false 23 | } 24 | } 25 | } 26 | 27 | return stk.isEmpty() 28 | } 29 | 30 | private fun isMatching(left: Char, right: Char): Boolean { 31 | return when (left) { 32 | '(' -> right == ')' 33 | '[' -> right == ']' 34 | '{' -> right == '}' 35 | else -> false 36 | } 37 | } 38 | 39 | private fun isLeft(char: Char): Boolean { 40 | return when(char) { 41 | '(' -> true 42 | '[' -> true 43 | '{' -> true 44 | else -> false 45 | } 46 | } 47 | } 48 | 49 | fun main() { 50 | val s = "()[]{}" 51 | val res = Easy_20_ValidParentheses.isValid(s) 52 | val res1 = Easy_20_ValidParentheses.isValid("([)]") 53 | val res2 = Easy_20_ValidParentheses.isValid("{[]}") 54 | asserts(res, true) 55 | asserts(res1, false) 56 | asserts(res2, true) 57 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/stack/Easy_225_ImpStackUseQueue.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.stack 2 | 3 | import java.util.* 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Easy_225_ImpStackUseQueue { 11 | 12 | /** Initialize your data structure here. */ 13 | val queue = LinkedList() 14 | val backup = LinkedList() 15 | 16 | /** Push element x onto stack. */ 17 | fun push(x: Int) { 18 | queue.addLast(x) 19 | } 20 | 21 | /** Removes the element on top of the stack and returns that element. */ 22 | fun pop(): Int { 23 | return popByType(true) 24 | } 25 | 26 | private fun popByType(isOut: Boolean): Int { 27 | var value = 0 28 | while (!queue.isEmpty()) { 29 | if (queue.size == 1) { 30 | value = queue.removeFirst() 31 | if (!isOut) { 32 | backup.addLast(value) 33 | } 34 | } else { 35 | backup.addLast(queue.removeFirst()) 36 | } 37 | } 38 | 39 | while (!backup.isEmpty()) { 40 | queue.addLast(backup.removeFirst()) 41 | } 42 | 43 | return value 44 | } 45 | 46 | /** Get the top element. */ 47 | fun top(): Int { 48 | return popByType(false) 49 | } 50 | 51 | /** Returns whether the stack is empty. */ 52 | fun empty(): Boolean { 53 | return queue.isEmpty() && backup.isEmpty() 54 | } 55 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/stack/Easy_232_ImpQueueUseStack.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.stack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Easy_232_ImpQueueUseStack { 9 | /** Initialize your data structure here. */ 10 | val outStk = ArrayDeque() 11 | val inStk = ArrayDeque() 12 | 13 | /** Push element x to the back of queue. */ 14 | fun push(x: Int) { 15 | inStk.addLast(x) 16 | } 17 | 18 | /** Removes the element from in front of queue and returns that element. */ 19 | fun pop(): Int { 20 | if (empty()) { 21 | return 0 // throw exception 22 | } 23 | 24 | // make sure in stack empty 25 | while (!inStk.isEmpty()) { 26 | outStk.addLast(inStk.removeLast()) 27 | } 28 | 29 | return outStk.removeLast() 30 | } 31 | 32 | /** Get the front element. */ 33 | fun peek(): Int { 34 | var value = pop() 35 | outStk.addLast(value) 36 | 37 | return value 38 | } 39 | 40 | /** Returns whether the queue is empty. */ 41 | fun empty(): Boolean { 42 | return inStk.isEmpty() && outStk.isEmpty() 43 | } 44 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/stack/Hard_239_SlidingWindowMax.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.stack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Hard_239_SlidingWindowMax { 9 | fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { 10 | val queue = MaxQueue() 11 | val list = mutableListOf() 12 | 13 | // fill the window with [0, k -1] 14 | for (i in 0 until k) { 15 | queue.addLast(nums[i]) 16 | } 17 | list.add(queue.max()) 18 | 19 | for (i in k until nums.size) { 20 | queue.removeIfMatch(nums[i - k]) 21 | queue.addLast(nums[i]) 22 | 23 | list.add(queue.max()) 24 | } 25 | 26 | return list.toIntArray() 27 | } 28 | 29 | fun maxSlidingWindow1(nums: IntArray, k: Int): IntArray { 30 | val queue = MaxQueue() 31 | val list = mutableListOf() 32 | 33 | var left = 0 34 | for (right in 0 until nums.size) { 35 | queue.addLast(nums[right]) 36 | if (right < k - 1) { 37 | continue 38 | } 39 | 40 | // window full 41 | if (right == k - 1) { 42 | list.add(queue.max()) 43 | } else { 44 | // bigger 45 | val leftEdge = nums[right - k] 46 | if (leftEdge == queue.max()) { 47 | queue.removeFront() 48 | list.add(queue.max()) 49 | } else { 50 | list.add(queue.max()) 51 | } 52 | } 53 | } 54 | 55 | return list.toIntArray() 56 | } 57 | } 58 | 59 | /** 60 | This is a queue only keep the max value at the front 61 | 5, 5, 4, 3, 2 62 | */ 63 | class MaxQueue { 64 | val queue = ArrayDeque() 65 | 66 | fun addLast(x: Int) { 67 | if (queue.isEmpty()) { 68 | queue.addLast(x) 69 | } else { 70 | while (!queue.isEmpty() && queue.last() < x) { 71 | queue.removeLast() 72 | } 73 | queue.addLast(x) 74 | } 75 | } 76 | 77 | fun removeFront(): Int { 78 | return queue.removeFirst() 79 | } 80 | 81 | fun removeIfMatch(x: Int) { 82 | if (!queue.isEmpty() && x == max()) { 83 | removeFront() 84 | } 85 | } 86 | 87 | fun max(): Int { 88 | return queue.first() 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/stack/Med_150_ReversePolishNotation.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.stack 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Med_150_ReversePolishNotation { 9 | fun evalRPN(tokens: Array): Int { 10 | val stk = ArrayDeque() 11 | 12 | for (s in tokens) { 13 | if (!isOprator(s)) { 14 | stk.addLast(s) 15 | } else { 16 | val res = calculate(stk.removeLast().toInt(), stk.removeLast().toInt(), s) 17 | stk.addLast(res.toString()) 18 | } 19 | } 20 | 21 | return stk.removeLast().toInt() 22 | } 23 | 24 | private fun calculate(b: Int, a: Int, oprator: String): Int { 25 | return when (oprator) { 26 | "+" -> a + b 27 | "-" -> a - b 28 | "*" -> a * b 29 | "/" -> a / b 30 | else -> { 0 } // throw exception 31 | } 32 | } 33 | 34 | private fun isOprator(s: String): Boolean { 35 | return s == "+" || s == "-" || s == "*" || s == "/" 36 | } 37 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/stack/Med_347_TopKFrequentElements.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.stack 2 | 3 | import java.util.* 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_347_TopKFrequentElements { 11 | fun topKFrequent(nums: IntArray, k: Int): IntArray { 12 | val map = hashMapOf() 13 | val heap = PriorityQueue>(k, {o1, o2 -> 14 | // sorted by value, and min heap 15 | o2.value - o1.value 16 | }) 17 | 18 | for (i in nums) { 19 | map.put(i, map.getOrDefault(i, 0) + 1) 20 | } 21 | 22 | map.entries.forEach { 23 | heap.offer(it) 24 | } 25 | 26 | val array = IntArray(k) 27 | for (i in 0 until k) { 28 | array[i] = heap.poll().key 29 | } 30 | 31 | return array; 32 | } 33 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/string/Easy_28_ImplementStr.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.string 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: 7 | */ 8 | object Easy_28_ImplementStr { 9 | 10 | /** 11 | * Implementation for the Rabin Karp in Kotlin 12 | * 13 | * "hello" 14 | * "ll" -> 'l' * 31 + 'l' * 31^0 15 | */ 16 | fun strStr(s: String, pat: String): Int { 17 | 18 | // get the power of last digits 19 | // calculate the hash of pat 20 | // compare the hash 21 | val sLength = s.length 22 | val pLength = pat.length 23 | if (pLength == 0) { 24 | return 0 25 | } 26 | if (sLength < pLength) { 27 | return -1 28 | } 29 | 30 | var power = 1 31 | val mod = Math.pow(2.0, 20.0).toInt() 32 | 33 | for (i in 0 until pLength) { 34 | power = power * 31 % mod 35 | } 36 | 37 | var pHash = 0 38 | for (i in 0 until pLength) { 39 | pHash = (pHash * 31 + pat.get(i).toInt()) % mod 40 | } 41 | 42 | var sHash = 0 43 | for (j in 0 until sLength) { 44 | 45 | sHash = (sHash * 31 + s.get(j).toInt()) % mod 46 | 47 | if (j < pLength - 1) { 48 | continue 49 | } 50 | 51 | if (j >= pLength) { 52 | sHash = sHash - ((power * s.get(j - pLength).toInt()) % mod) 53 | 54 | if (sHash < 0) { 55 | sHash = sHash + mod 56 | } 57 | } 58 | 59 | if (sHash == pHash) { 60 | // double check 61 | if (s.substring(j - pLength + 1, j + 1).equals(pat)) { 62 | return j - pLength + 1 63 | } 64 | } 65 | } 66 | return -1 67 | } 68 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/string/Easy_344_ReverseString.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.string 2 | 3 | import com.boycoder.utils.asserts 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/4 8 | * @desc: 9 | */ 10 | object Easy_344_ReverseString { 11 | 12 | fun reverse(array: CharArray) { 13 | if (array.isEmpty()) return 14 | 15 | var left = 0 16 | var right = array.size - 1 17 | 18 | while (left < right) { 19 | val temp = array[left] 20 | array[left] = array[right] 21 | array[right] = temp 22 | left++ 23 | right-- 24 | } 25 | } 26 | } 27 | 28 | fun main() { 29 | val array = charArrayOf('h', 'e', 'l', 'l', 'o') 30 | Easy_344_ReverseString.reverse(array) 31 | 32 | var res = true 33 | val array1 = charArrayOf('o', 'l', 'l', 'e', 'h') 34 | for (i in 0 until array.size) { 35 | if (array1[i] != array[i]) { 36 | res = false 37 | } 38 | } 39 | 40 | asserts(res, true) 41 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/string/Med_151_ReverseWordInString.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.string 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/22 6 | * @desc: https://leetcode-cn.com/problems/reverse-words-in-a-string/ 7 | */ 8 | object Med_151_ReverseWordInString { 9 | fun reverseWords(s: String): String { 10 | var string = s.trim() 11 | 12 | var array = string.toCharArray() 13 | // remove space 14 | var left = 0 15 | var right = 0 16 | 17 | while (left <= right && right < array.size) { 18 | if (array[right] != ' ') { 19 | array[left] = array[right] 20 | left++ 21 | right++ 22 | } else { 23 | while (array[right + 1] == ' ') { 24 | right++ 25 | } 26 | array[left] = array[right] 27 | left++ 28 | right++ 29 | } 30 | } 31 | if (left < array.size) { 32 | array = array.slice(0..left-1).toCharArray() 33 | } 34 | 35 | // reverse the whole array 36 | reverse(array, 0, array.size - 1) 37 | 38 | left = 0 39 | right = 0 40 | while (right < array.size) { 41 | if (right == array.size - 1) { 42 | reverse(array, left, right) 43 | right++ 44 | left = right 45 | } else if (array[right + 1] == ' ') { 46 | reverse(array, left, right) 47 | right = right + 2 48 | left = right 49 | } else { 50 | right++ 51 | } 52 | } 53 | 54 | return array.joinToString("") 55 | } 56 | 57 | private fun reverse(array: CharArray, i: Int, j: Int) { 58 | var left = i 59 | var right = j 60 | 61 | while (left <= right) { 62 | val temp = array[left] 63 | array[left] = array[right] 64 | array[right] = temp 65 | left++ 66 | right-- 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_101_SymmetricTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Easy_101_SymmetricTree { 12 | fun isSymmetric(root: TreeNode?): Boolean { 13 | return isSym(root?.left, root?.right) 14 | } 15 | 16 | private fun isSym(node1: TreeNode?, node2: TreeNode?): Boolean { 17 | // case that need return 18 | if (node1 == null && node2 == null) { 19 | return true 20 | } else if (node1 != null && node2 == null) { 21 | return false 22 | } else if (node1 == null && node2 != null) { 23 | return false 24 | } else if (node1!!.value != node2!!.value) { 25 | return false 26 | } else { 27 | // node1 node2 not null then we need to compare with recursion 28 | val outSide = isSym(node1.left, node2.right) 29 | val inSide = isSym(node1.right, node2.left) 30 | return outSide && inSide 31 | } 32 | } 33 | 34 | private fun isSym1(root: TreeNode?): Boolean { 35 | if (root == null) return true 36 | 37 | val queue = LinkedList() 38 | queue.addLast(root) 39 | 40 | while (!queue.isEmpty()) { 41 | val size = queue.size 42 | 43 | val temps = mutableListOf() 44 | for (i in 0 until size) { 45 | val node = queue.removeFirst() 46 | if (node != null) { 47 | temps.add(node.left) 48 | temps.add(node.right) 49 | 50 | queue.addLast(node.left) 51 | queue.addLast(node.right) 52 | } 53 | } 54 | 55 | // for every level, we check the level Symmetric 56 | if (!isListSym(temps)) { 57 | return false 58 | } 59 | } 60 | 61 | return true 62 | } 63 | 64 | private fun isSame(node1: TreeNode?, node2: TreeNode?): Boolean { 65 | if (node1 == null && node2 == null) { 66 | return true 67 | } else if (node1 != null && node2 != null && node1.value == node2.value) { 68 | return true 69 | } else { 70 | return false 71 | } 72 | } 73 | 74 | private fun isListSym(list: List): Boolean { 75 | if (list.isEmpty()) return true 76 | 77 | var left = 0 78 | val size = list.size 79 | var right = size - 1 80 | 81 | while (left <= right) { 82 | if (!isSame(list[left], list[right])) { 83 | return false 84 | } 85 | left++ 86 | right-- 87 | } 88 | 89 | return true 90 | } 91 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_104_MaxDepthBinaryTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Easy_104_MaxDepthBinaryTree { 11 | fun maxDepth(root: TreeNode?): Int { 12 | return getDepth(root) 13 | } 14 | 15 | // level traversal 16 | private fun getDepth(root: TreeNode?): Int { 17 | if (root == null) return 0 18 | 19 | val queue = ArrayDeque() 20 | queue.addLast(root) 21 | var node: TreeNode? = null 22 | var depth = 0 23 | 24 | while (!queue.isEmpty()) { 25 | depth++ 26 | val size = queue.size 27 | for (i in 0 until size) { 28 | node = queue.removeFirst() 29 | node.left?.let { 30 | queue.addLast(it) 31 | } 32 | 33 | node.right?.let { 34 | queue.addLast(it) 35 | } 36 | } 37 | } 38 | return depth 39 | } 40 | 41 | // recursion post order is preferred, pre order is not 42 | private fun getDepth1(root: TreeNode?): Int { 43 | if (root == null) return 0 44 | 45 | val left = getDepth(root.left) 46 | val right = getDepth(root.right) 47 | 48 | return 1 + Math.max(left, right) 49 | } 50 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_110_BalancedBinaryTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Easy_110_BalancedBinaryTree { 11 | fun isBalanced(root: TreeNode?): Boolean { 12 | val depth = getDepth(root) 13 | return depth != -1 14 | } 15 | 16 | /** 17 | * use recursion post order 18 | * count every left depth and right depth 19 | * check if abs(left - right) > 1, if so return -1, means not balanced 20 | */ 21 | private fun getDepth(root: TreeNode?): Int { 22 | if (root == null) return 0 23 | 24 | val left = getDepth(root.left) 25 | val right = getDepth(root.right) 26 | 27 | if (left == -1 || right == -1) return -1 28 | 29 | if (Math.abs(left - right) > 1) return -1 30 | 31 | return Math.max(left, right) + 1 32 | } 33 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_111_MinDepthBinaryTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Easy_111_MinDepthBinaryTree { 11 | fun minDepth(root: TreeNode?): Int { 12 | if (root == null) return 0 13 | return getDepth(root) 14 | } 15 | 16 | /** 17 | * level traversal 18 | * 19 | * We count the depth from 0 20 | * and when we meet a leaf node, we stop the counting 21 | */ 22 | private fun getDepth(root: TreeNode): Int { 23 | val queue = ArrayDeque() 24 | queue.add(root) 25 | var depth = 0 26 | 27 | while (!queue.isEmpty()) { 28 | val size = queue.size 29 | depth++ 30 | for (i in 0 until size) { 31 | val node = queue.removeFirst() 32 | if (node.left == null && node.right == null) { 33 | return depth 34 | } 35 | 36 | if (node.left != null) queue.addLast(node.left!!) 37 | if (node.right != null) queue.addLast(node.right!!) 38 | } 39 | } 40 | 41 | return depth 42 | } 43 | 44 | /** 45 | * dfs 46 | * 47 | * can be optimized using cache 48 | */ 49 | private fun getDepth1(root: TreeNode): Int { 50 | if (root.left == null && root.right == null){ 51 | return 1 52 | } else if (root.left == null && root.right != null) { 53 | return getDepth(root.right!!) + 1 54 | } else if (root.left != null && root.right == null) { 55 | return getDepth(root.left!!) + 1 56 | } else { 57 | return Math.min(getDepth(root.left!!), getDepth(root.right!!)) + 1 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_112_PathSum.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Easy_112_PathSum { 11 | fun hasPathSum(root: TreeNode?, targetSum: Int): Boolean { 12 | if (root == null) return false 13 | return hasSum(root, targetSum) 14 | } 15 | 16 | private fun hasSum(root: TreeNode, targetSum: Int): Boolean { 17 | if (root.left == null && root.right == null) { 18 | return targetSum == root.value 19 | } 20 | 21 | var flag = false 22 | val target = targetSum - root.value 23 | if (root.left != null) { 24 | flag = flag or hasSum(root.left!!, target) 25 | } 26 | 27 | if (root.right != null) { 28 | flag = flag or hasSum(root.right!!, target) 29 | } 30 | return flag 31 | } 32 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_144_BinaryTreePreorder.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | import kotlin.collections.ArrayDeque 6 | 7 | /** 8 | * @Author: zhutao 9 | * @datetime: 2021/6/4 10 | * @desc: 11 | */ 12 | object Easy_144_BinaryTreePreorder { 13 | fun preorderTraversal(root: TreeNode?): List { 14 | val list = mutableListOf() 15 | 16 | // doTraversal(root, list) 17 | // doTraversalLoop(root, list) 18 | traversal(root, list) // recommended!!! 19 | return list 20 | } 21 | 22 | 23 | private fun traversal(root: TreeNode?, list: MutableList) { 24 | if (root == null) return 25 | 26 | val stk = LinkedList() 27 | stk.addLast(root) 28 | 29 | var node: TreeNode? = null 30 | 31 | while (!stk.isEmpty()) { 32 | node = stk.getLast() // peek 33 | 34 | // the same with if else, but no more null check 35 | node?.let { 36 | // always remove first 37 | stk.removeLast() 38 | 39 | if (it.right != null) stk.addLast(it.right) // right 40 | if (it.left != null) stk.addLast(it.left) // left 41 | 42 | stk.addLast(it) // root 43 | stk.addLast(null) 44 | }?: run { 45 | stk.removeLast() 46 | stk.removeLast()?.apply{ 47 | list.add(value) 48 | } 49 | } 50 | } 51 | } 52 | 53 | private fun doTraversalLoop(root: TreeNode?, list: MutableList) { 54 | if (root == null) return 55 | val stk = ArrayDeque() 56 | 57 | var node: TreeNode? = null 58 | stk.addLast(root) 59 | 60 | while (!stk.isEmpty()) { 61 | node = stk.removeLast() 62 | list.add(node.value) 63 | 64 | // here is the key, add right node then left node 65 | node.right?.let { 66 | stk.addLast(it) 67 | } 68 | 69 | 70 | node.left?.let { 71 | stk.addLast(it) 72 | } 73 | } 74 | 75 | return 76 | } 77 | 78 | private fun doTraversal(root: TreeNode?, list: MutableList) { 79 | if (root == null) return 80 | 81 | list.add(root.value) 82 | doTraversal(root.left, list) 83 | doTraversal(root.right, list) 84 | } 85 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_145_BinaryTreePostorderTraversal.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Easy_145_BinaryTreePostorderTraversal { 12 | fun postorderTraversal(root: TreeNode?): List { 13 | val list = mutableListOf() 14 | if (root == null) return list 15 | 16 | // Here we can not use ArrayDeque, it dose allow null element 17 | val stk: Deque = LinkedList() 18 | stk.addLast(root) 19 | var node: TreeNode? = null 20 | 21 | while (!stk.isEmpty()) { 22 | node = stk.getLast() 23 | 24 | if (node != null) { 25 | // always remove first 26 | node = stk.removeLast() 27 | 28 | stk.addLast(node) 29 | stk.addLast(null) 30 | 31 | if (node?.right != null) stk.addLast(node!!.right) 32 | if (node?.left != null) stk.addLast(node!!.left) 33 | } else { 34 | stk.removeLast() 35 | node = stk.removeLast() 36 | list.add(node!!.value) 37 | } 38 | } 39 | return list 40 | } 41 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_226_InvertBinaryTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | import kotlin.collections.ArrayDeque 6 | 7 | /** 8 | * @Author: zhutao 9 | * @datetime: 2021/6/22 10 | * @desc: 11 | */ 12 | object Easy_226_InvertBinaryTree { 13 | fun invertTree(root: TreeNode?): TreeNode? { 14 | invert(root) 15 | return root 16 | } 17 | 18 | // loop with level order 19 | private fun invert(root: TreeNode?) { 20 | if (root == null) return 21 | 22 | val queue = ArrayDeque() 23 | queue.addLast(root) 24 | 25 | while (!queue.isEmpty()) { 26 | val node = queue.removeFirst() 27 | 28 | val temp = node.left 29 | node.left = node.right 30 | node.right = temp 31 | 32 | if (node.left != null) queue.addLast(node.left!!) 33 | if (node.right != null) queue.addLast(node.right!!) 34 | } 35 | } 36 | 37 | // loop with post order 38 | private fun invert5(root: TreeNode?) { 39 | if (root == null) return 40 | 41 | val stk = LinkedList() 42 | stk.addLast(root) 43 | var node: TreeNode? = null 44 | 45 | while (!stk.isEmpty()) { 46 | node = stk.getLast() 47 | 48 | node?.let { 49 | stk.removeLast() 50 | stk.addLast(it) 51 | stk.addLast(null) 52 | 53 | if (it.right != null) stk.addLast(it.right) 54 | if (it.left != null) stk.addLast(it.left) 55 | }?: run { 56 | stk.removeLast() 57 | stk.removeLast()?.let { 58 | val temp = it.left 59 | it.left = it.right 60 | it.right = temp 61 | } 62 | } 63 | } 64 | } 65 | 66 | // loop with pre order 67 | private fun invert4(root: TreeNode?) { 68 | if (root == null) return 69 | 70 | val stk = LinkedList() 71 | stk.addLast(root) 72 | var node: TreeNode? = null 73 | 74 | while (!stk.isEmpty()) { 75 | node = stk.getLast() 76 | 77 | node?.let { 78 | stk.removeLast() 79 | 80 | if (it.right != null) stk.addLast(it.right) 81 | if (it.left != null) stk.addLast(it.left) 82 | 83 | stk.addLast(it) 84 | stk.addLast(null) 85 | }?: run { 86 | stk.removeLast() 87 | stk.removeLast()?.let { 88 | val temp = it.left 89 | it.left = it.right 90 | it.right = temp 91 | } 92 | } 93 | } 94 | } 95 | 96 | 97 | // recursion post order 98 | private fun invert3(root: TreeNode?) { 99 | if (root == null) return 100 | 101 | invert(root.left) 102 | invert(root.right) 103 | 104 | val temp = root.left 105 | root.left = root.right 106 | root.right = temp 107 | } 108 | 109 | // recursion inorder 110 | private fun invert2(root: TreeNode?) { 111 | if (root == null) return 112 | 113 | invert(root.left) 114 | 115 | val temp = root.left 116 | root.left = root.right 117 | root.right = temp 118 | 119 | 120 | invert(root.left) 121 | } 122 | 123 | // recursion pre order 124 | private fun invert1(root: TreeNode?) { 125 | if (root == null) return 126 | 127 | val temp = root.left 128 | root.left = root.right 129 | root.right = temp 130 | 131 | invert(root.left) 132 | invert(root.right) 133 | } 134 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_257_BinaryTreePaths.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Easy_257_BinaryTreePaths { 12 | fun binaryTreePaths(root: TreeNode?): List { 13 | 14 | val list = mutableListOf() 15 | if (root == null) return list 16 | 17 | val nodes = mutableListOf() 18 | nodes.add(root) 19 | findPath(root, nodes, list) 20 | return list 21 | } 22 | 23 | /** 24 | * dfs 25 | */ 26 | private fun findPath(root: TreeNode, nodes: MutableList, list: MutableList) { 27 | 28 | // time to collect result. 29 | if (root.left == null && root.right == null) { 30 | val s = nodes.joinToString(separator = "->", transform = { 31 | it.value.toString() 32 | }) 33 | list.add(s) 34 | return 35 | } 36 | 37 | // find left 38 | if (root.left != null) { 39 | nodes.add(root.left!!) 40 | findPath(root.left!!, nodes, list) 41 | nodes.remove(root.left) // back tracking 42 | } 43 | 44 | // find right 45 | if (root.right != null) { 46 | nodes.add(root.right!!) 47 | findPath(root.right!!, nodes, list) 48 | nodes.remove(root.right) // back tracking 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_404_SumOfLeftLeaves.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Easy_404_SumOfLeftLeaves { 12 | var sum = 0 13 | 14 | fun sumOfLeftLeaves(root: TreeNode?): Int { 15 | sum(root) 16 | return sum 17 | } 18 | 19 | // loop pre order 20 | private fun sum(root: TreeNode?) { 21 | if (root == null) return 22 | 23 | val stk = LinkedList() 24 | stk.addLast(root) 25 | var node: TreeNode? = null 26 | 27 | while (!stk.isEmpty()) { 28 | node = stk.getLast() // peek 29 | 30 | if (node != null) { 31 | stk.removeLast() 32 | 33 | if (node.left != null) stk.addLast(node.left) 34 | if (node.right != null) stk.addLast(node.right) 35 | 36 | stk.addLast(node) 37 | stk.addLast(null) 38 | } else { 39 | stk.removeLast() 40 | stk.removeLast()?.let { 41 | if (it.left != null && it.left!!.left == null && it.left!!.right == null) { 42 | sum = sum + it.left!!.value 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | private fun sum1(root: TreeNode?) { 50 | if (root == null) return 51 | 52 | if (root.left != null && root.left!!.left == null && root.left!!.right == null) { 53 | sum = sum + root.left!!.value 54 | } 55 | 56 | if (root.left != null) sum(root.left) 57 | if (root.right != null) sum(root.right) 58 | } 59 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_559_MaxDepthNAryTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | /** 4 | * @Author: zhutao 5 | * @datetime: 2021/6/23 6 | * @desc: 7 | */ 8 | object Easy_559_MaxDepthNAryTree { 9 | fun maxDepth(root: Node?): Int { 10 | return getDepth(root) 11 | } 12 | 13 | // level traversal 14 | private fun getDepth(root: Node?): Int { 15 | if (root == null) return 0 16 | 17 | val queue = ArrayDeque() 18 | queue.addLast(root) 19 | var depth = 0 20 | 21 | while (!queue.isEmpty()) { 22 | val size = queue.size 23 | depth++ 24 | 25 | for (i in 0 until size) { 26 | queue.removeFirst().children.forEach { child -> 27 | if (child != null) { 28 | queue.addLast(child) 29 | } 30 | } 31 | } 32 | } 33 | 34 | return depth 35 | } 36 | 37 | 38 | // recursion 39 | private fun getDepth1(root: Node?): Int { 40 | if (root == null) return 0 41 | 42 | var max = 0 43 | root.children.forEach { 44 | max = Math.max(getDepth(it), max) 45 | } 46 | 47 | return max + 1 48 | } 49 | } 50 | 51 | data class Node(var value: Int, var children: List) -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_617_MergeTwoBinaryTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Easy_617_MergeTwoBinaryTree { 12 | fun mergeTrees(root1: TreeNode?, root2: TreeNode?): TreeNode? { 13 | return doMerge1(root1, root2) 14 | } 15 | 16 | /** 17 | * loop + stack 18 | */ 19 | private fun doMerge1(root1: TreeNode?, root2: TreeNode?): TreeNode? { 20 | if (root1 == null) { 21 | return root2 22 | } 23 | 24 | if (root2 == null) { 25 | return root1 26 | } 27 | 28 | val stk = LinkedList() 29 | stk.addLast(root2) 30 | stk.addLast(root1) 31 | 32 | while (!stk.isEmpty()) { 33 | val node1 = stk.removeLast() 34 | val node2 = stk.removeLast() 35 | node1.value = node1.value + node2.value 36 | 37 | if (node1.left != null && node2.left != null) { 38 | stk.addLast(node2.left) 39 | stk.addLast(node1.left) 40 | } 41 | 42 | if (node1.right != null && node2.right != null) { 43 | stk.addLast(node2.right) 44 | stk.addLast(node1.right) 45 | } 46 | 47 | if (node1.left == null && node2.left != null) { 48 | node1.left = node2.left 49 | } 50 | 51 | if (node1.right == null && node2.right != null) { 52 | node1.right = node2.right 53 | } 54 | } 55 | 56 | return root1 57 | } 58 | 59 | private fun doMerge(root1: TreeNode?, root2: TreeNode?): TreeNode? { 60 | if (root1 == null && root2 == null) { 61 | return null 62 | } else if (root1 == null && root2 != null) { 63 | return root2 64 | } else if (root1 != null && root2 == null) { 65 | return root1 66 | } else if (root1 != null && root2 != null) { 67 | root1.value = root1.value + root2.value 68 | root1.left = doMerge(root1.left, root2.left) 69 | root1.right = doMerge(root1.right, root2.right) 70 | return root1 71 | } 72 | 73 | // can not reach here 74 | return null 75 | } 76 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Easy_94_BinaryTreeInorderTraversal.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | import kotlin.collections.ArrayDeque 6 | 7 | /** 8 | * @Author: zhutao 9 | * @datetime: 2021/6/22 10 | * @desc: 11 | */ 12 | object Easy_94_BinaryTreeInorderTraversal { 13 | fun inorderTraversal(root: TreeNode?): List { 14 | val list = mutableListOf() 15 | if (root == null) return list 16 | 17 | val stk = LinkedList() 18 | stk.addLast(root) 19 | var node: TreeNode? = null 20 | 21 | while (!stk.isEmpty()) { 22 | node = stk.getLast() 23 | 24 | // the same with the if else, but no more null check 25 | node?.let { 26 | // always remove first 27 | stk.removeLast() 28 | 29 | if (it.right != null) stk.addLast(it.right) // right 30 | 31 | stk.addLast(it) // root 32 | stk.addLast(null) 33 | 34 | if (it.left != null) stk.addLast(it.left) // left 35 | }?: run { 36 | stk.removeLast() 37 | stk.removeLast()?.apply { 38 | list.add(value) 39 | } 40 | } 41 | } 42 | 43 | return list 44 | } 45 | 46 | 47 | fun inorderTraversal1(root: TreeNode?): List { 48 | val list = mutableListOf() 49 | val stk = ArrayDeque() 50 | 51 | var node: TreeNode? = root 52 | 53 | while (node != null || !stk.isEmpty()) { 54 | if (node != null) { 55 | stk.addLast(node) 56 | node = node.left 57 | } else { 58 | // really traversal 59 | node = stk.removeLast() 60 | list.add(node.value) 61 | 62 | node = node.right 63 | } 64 | } 65 | return list 66 | } 67 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Med_102_BinaryTreeLevelOrderTraversal.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_102_BinaryTreeLevelOrderTraversal { 11 | fun levelOrder(root: TreeNode?): List> { 12 | val list: MutableList> = mutableListOf() 13 | if (root == null) return list 14 | 15 | val queue = ArrayDeque() 16 | 17 | var node = root 18 | queue.addLast(root) 19 | 20 | while (!queue.isEmpty()) { 21 | val size = queue.size 22 | val temps = mutableListOf() 23 | 24 | 25 | for (i in 0 until size) { 26 | node = queue.removeFirst() 27 | temps.add(node.value) 28 | 29 | if (node.left != null) queue.addLast(node.left!!) 30 | if (node.right != null) queue.addLast(node.right!!) 31 | } 32 | 33 | list.add(temps) 34 | } 35 | return list 36 | } 37 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Med_106_ConstructBinaryTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_106_ConstructBinaryTree { 11 | 12 | 13 | /** 14 | * inorder: 9,3,15,20,7 15 | * postorder:9,15,7,20,3 16 | * 17 | * 9 3 15,20,7 18 | * 9 15,7,20 3 19 | * | | \ 20 | * 9 3 15, 20 7 21 | * 9 3 15,7, 20 22 | * / | \ 23 | * 15 20 7 24 | * 25 | * 26 | */ 27 | fun buildTree(inorder: IntArray, postorder: IntArray): TreeNode? { 28 | if (inorder.size == 0 || postorder.size == 0) return null 29 | 30 | val pRoot = postorder.size - 1 31 | val iRoot = inorder.indexOf(postorder[pRoot]) 32 | 33 | var node = TreeNode(postorder[pRoot]) 34 | 35 | if (postorder.size == 1) { 36 | return node 37 | } 38 | 39 | if (iRoot == 0) { 40 | node.right = buildTree(inorder.slice(1..(inorder.size - 1)).toIntArray(), postorder.slice(0..(postorder.size - 2)).toIntArray()) 41 | } else if (iRoot == inorder.size - 1) { 42 | node.left = buildTree(inorder.slice(0..(inorder.size - 2)).toIntArray(), postorder.slice(0..(postorder.size - 2)).toIntArray()) 43 | } else { 44 | // in the middle 45 | val leftSize = iRoot 46 | val rightSize = inorder.size - 1 - iRoot 47 | node.left = buildTree(inorder.slice(0..(leftSize - 1)).toIntArray(), postorder.slice(0..(leftSize - 1)).toIntArray()) 48 | node.right = buildTree(inorder.slice((iRoot + 1)..(inorder.size - 1)).toIntArray(), postorder.slice(leftSize..(postorder.size - 2)).toIntArray()) 49 | } 50 | 51 | return node 52 | } 53 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Med_222_CompleteTreeNodeCount.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | import java.util.* 5 | 6 | /** 7 | * @Author: zhutao 8 | * @datetime: 2021/6/22 9 | * @desc: 10 | */ 11 | object Med_222_CompleteTreeNodeCount { 12 | fun countNodes(root: TreeNode?): Int { 13 | return count1(root) 14 | } 15 | 16 | private fun count1(root: TreeNode?): Int { 17 | if (root == null) return 0 18 | 19 | var count = 0 20 | val queue = LinkedList() 21 | queue.addLast(root) 22 | 23 | while (!queue.isEmpty()) { 24 | val node = queue.removeFirst() 25 | count++ 26 | 27 | node.left?.let { 28 | queue.addLast(it) 29 | } 30 | 31 | node.right?.let { 32 | queue.addLast(it) 33 | } 34 | } 35 | 36 | return count 37 | } 38 | 39 | /** 40 | * post order 41 | */ 42 | private fun count(root: TreeNode?): Int { 43 | if (root == null) return 0 44 | 45 | val left = count(root.left) 46 | val right = count(root.right) 47 | 48 | return left + right + 1 49 | } 50 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Med_236_LowestCommonAncestor.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | * 10 | * 1. Recursion + post order 11 | * 2. Maintain every node's parent in map 12 | * and find visit every p node parent put into the set. 13 | * Then visit every q node parent check if into the set. 14 | */ 15 | object Med_236_LowestCommonAncestor { 16 | 17 | fun lowestCommonAncestor(root: TreeNode?, p: TreeNode?, q: TreeNode?): TreeNode? { 18 | if (root == null || p == null || q == null) return null 19 | return find(root, p, q) 20 | } 21 | 22 | // recursion + post order 23 | private fun find(root: TreeNode?, p: TreeNode, q: TreeNode): TreeNode? { 24 | if (root == null) return null 25 | 26 | val left = find(root.left, p, q) 27 | val right = find(root.right, p, q) 28 | 29 | if (root.value == p.value || root.value == q.value) return root 30 | 31 | if (left != null && right != null) { 32 | return root 33 | } else if (left == null && right != null) { 34 | return right 35 | } else if (left != null && right == null) { 36 | return left 37 | } else { 38 | return null 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Med_513_BottomLeftTreeValue.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_513_BottomLeftTreeValue { 11 | var maxLevel = 0 12 | var value = 0 13 | fun findBottomLeftValue(root: TreeNode?): Int { 14 | findLeft(root) 15 | return value 16 | } 17 | 18 | private fun findLeft(root: TreeNode?) { 19 | if (root == null) return 20 | 21 | val queue = ArrayDeque() 22 | queue.addLast(root) 23 | var node = root 24 | 25 | while (!queue.isEmpty()) { 26 | val size = queue.size 27 | 28 | for (i in 0 until size) { 29 | node = queue.removeFirst() 30 | if (i == 0) { 31 | maxLevel++ 32 | value = node.value 33 | } 34 | 35 | node.left?.let { 36 | queue.addLast(it) 37 | } 38 | 39 | node.right?.let { 40 | queue.addLast(it) 41 | } 42 | } 43 | } 44 | } 45 | 46 | // recursion with back tracking 47 | private fun findLeft1(root: TreeNode?, level: Int) { 48 | if (root == null) return 49 | 50 | var mLevel = level 51 | if (mLevel > maxLevel) { 52 | maxLevel = mLevel 53 | value = root.value 54 | } 55 | 56 | // we check left node first, so we can find bottom left 57 | // if change the order, we can find bottom right 58 | if (root.left != null) { 59 | mLevel++ 60 | findLeft1(root.left, mLevel) 61 | mLevel-- 62 | } 63 | 64 | if (root.right != null) { 65 | mLevel++ 66 | findLeft1(root.right, mLevel) 67 | mLevel-- 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/com/boycoder/problems/tree/Med_654_MaxBinaryTree.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.problems.tree 2 | 3 | import com.boycoder.basis.datastructure.TreeNode 4 | 5 | /** 6 | * @Author: zhutao 7 | * @datetime: 2021/6/22 8 | * @desc: 9 | */ 10 | object Med_654_MaxBinaryTree { 11 | 12 | fun constructMaximumBinaryTree(nums: IntArray): TreeNode? { 13 | val size = nums.size 14 | if (size == 0) return null 15 | var max = -1 16 | var index = 0 17 | nums.forEachIndexed { indx, i -> 18 | if (i > max) { 19 | max = i 20 | index = indx 21 | } 22 | } 23 | 24 | val node = TreeNode(max) 25 | if (size == 1) return node 26 | 27 | if (index == 0) { 28 | node.right = constructMaximumBinaryTree(nums.slice(1..(size - 1)).toIntArray()) 29 | } else if (index == size - 1) { 30 | node.left = constructMaximumBinaryTree(nums.slice(0..(size - 2)).toIntArray()) 31 | } else { 32 | node.left = constructMaximumBinaryTree(nums.slice(0..(index - 1)).toIntArray()) 33 | node.right = constructMaximumBinaryTree(nums.slice((index + 1)..(size - 1)).toIntArray()) 34 | } 35 | 36 | return node 37 | } 38 | } -------------------------------------------------------------------------------- /src/com/boycoder/utils/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.utils 2 | 3 | fun asserts(res: Any?, expect: Any?) { 4 | if (res != expect) { 5 | throw WrongResultException("\n" + 6 | "=================\n" + 7 | "res: $res \nexpect: $expect") 8 | } else { 9 | println("success") 10 | } 11 | } 12 | 13 | fun assertCollection(res: Collection, expect: Collection) { 14 | if (res.size != res.size) { 15 | throw WrongResultException("\n" + 16 | "=================\n" + 17 | "res: $res \nexpect: $expect") 18 | } 19 | 20 | val iterator1 = res.iterator() 21 | val iterator2 = expect.iterator() 22 | while (iterator1.hasNext() && iterator2.hasNext()) { 23 | val v1 = iterator1.next() 24 | val v2 = iterator2.next() 25 | 26 | if (v1 != v2) { 27 | throw WrongResultException("\n" + 28 | "=================\n" + 29 | "res: $res \nexpect: $expect") 30 | } 31 | } 32 | 33 | println("success") 34 | } 35 | 36 | /** 37 | * increase 38 | */ 39 | fun isSorted(array: IntArray): Boolean { 40 | for (i in 1 until array.size) { 41 | if (array[i - 1] > array[i]) { 42 | return false 43 | } 44 | } 45 | 46 | return true 47 | } -------------------------------------------------------------------------------- /src/com/boycoder/utils/WrongResultException.kt: -------------------------------------------------------------------------------- 1 | package com.boycoder.utils 2 | 3 | class WrongResultException(msg: String): Exception(msg) { 4 | } --------------------------------------------------------------------------------