├── .gitattributes ├── .gitignore ├── docs └── img │ ├── qrcode.png │ ├── wechat.png │ ├── ds-summary.png │ └── algo-summary.png ├── src └── main │ └── java │ ├── datastrcture │ ├── ListNode.java │ ├── TreeNode.java │ └── KnaryTreeNode.java │ └── algorithm │ ├── basic │ ├── iterative │ │ ├── ReverseLinkedList.java │ │ ├── FindMiddleOfList.java │ │ ├── MergeTwoSortedList.java │ │ └── ReorderList.java │ └── iterativetree │ │ ├── FlattenTreeToList.java │ │ ├── PopulateNextRight.java │ │ ├── FlattenMultiLevelList.java │ │ └── PopulateNextRight2.java │ ├── recursion │ └── tree │ │ ├── pathsum │ │ └── MaxPathSumNodeToNode.java │ │ ├── lca │ │ ├── LCA.java │ │ ├── LCAOfBST.java │ │ ├── LCAOfKNode.java │ │ ├── LCAOfKnaryTree.java │ │ ├── LCAOfKnaryMNode.java │ │ ├── LCA3.java │ │ └── LCA2.java │ │ └── construct │ │ ├── BSTFromPre.java │ │ ├── ConstructFromInPre.java │ │ ├── ConstructFromInPost.java │ │ └── ConstructFromPrePost.java │ ├── pointers │ ├── twosum │ │ ├── TwoSumSorted.java │ │ ├── TwoSum.java │ │ ├── TwoSumSmaller.java │ │ ├── TwoSumGreater.java │ │ ├── TwoSumClosest.java │ │ ├── TwoSumAllPairs.java │ │ ├── ThreeSumSmaller.java │ │ ├── ThreeSumGreater.java │ │ ├── ThreeSumClosest.java │ │ ├── TwoSumDifference.java │ │ ├── KSum.java │ │ ├── TwoSumDesign.java │ │ ├── TwoSumAllUniquePairs.java │ │ ├── FourSum.java │ │ └── ThreeSum.java │ └── merge │ │ └── MergeKSortedList.java │ └── sort │ └── QuickSort.java ├── pom.xml ├── .github └── workflows │ └── gradle.yml ├── README-EN.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | .gitbook.yaml merge=ours 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | coding-interview.iml 3 | out 4 | target 5 | src/main/resources 6 | src/test -------------------------------------------------------------------------------- /docs/img/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdong1995/Algorithm-Patterns/HEAD/docs/img/qrcode.png -------------------------------------------------------------------------------- /docs/img/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdong1995/Algorithm-Patterns/HEAD/docs/img/wechat.png -------------------------------------------------------------------------------- /docs/img/ds-summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdong1995/Algorithm-Patterns/HEAD/docs/img/ds-summary.png -------------------------------------------------------------------------------- /docs/img/algo-summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdong1995/Algorithm-Patterns/HEAD/docs/img/algo-summary.png -------------------------------------------------------------------------------- /src/main/java/datastrcture/ListNode.java: -------------------------------------------------------------------------------- 1 | package datastrcture; 2 | 3 | public class ListNode { 4 | public int val; 5 | public ListNode next; 6 | 7 | public ListNode(int val) { 8 | this.val = val; 9 | this.next = null; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/datastrcture/TreeNode.java: -------------------------------------------------------------------------------- 1 | package datastrcture; 2 | 3 | public class TreeNode { 4 | public int val; 5 | public TreeNode left; 6 | public TreeNode right; 7 | 8 | public TreeNode(int val) { 9 | this.val = val; 10 | this.left = null; 11 | this.right = null; 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/datastrcture/KnaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package datastrcture; 2 | 3 | import java.util.*; 4 | 5 | public class KnaryTreeNode { 6 | public int key; 7 | public List children; 8 | 9 | public KnaryTreeNode(int key) { 10 | this.key = key; 11 | this.children = new ArrayList<>(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | 1.8 9 | 1.8 10 | 11 | 12 | groupId 13 | coding-interview 14 | 1.0-SNAPSHOT 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Build 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 1.8 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 1.8 23 | - name: Build with Maven 24 | run: mvn -B package --file pom.xml 25 | -------------------------------------------------------------------------------- /src/main/java/algorithm/basic/iterative/ReverseLinkedList.java: -------------------------------------------------------------------------------- 1 | package algorithm.basic.iterative; 2 | 3 | import datastrcture.*; 4 | 5 | /** 6 | * Given one single linked list, return the reversed list. 7 | */ 8 | public class ReverseLinkedList { 9 | public ListNode reverseList(ListNode head) { 10 | if (head == null) { 11 | return null; 12 | } 13 | ListNode prev = null; 14 | ListNode cur = head; 15 | ListNode next = null; 16 | 17 | while (cur != null) { 18 | next = cur.next; 19 | cur.next = prev; 20 | prev = cur; 21 | cur = next; 22 | } 23 | return prev; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/pathsum/MaxPathSumNodeToNode.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.pathsum; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | public class MaxPathSumNodeToNode { 6 | // sum = the largest sum path that ends at root 7 | private void helper(TreeNode root, int[] max, int sum) { 8 | // base case 9 | if (root == null) { 10 | return; 11 | } 12 | 13 | if (sum < 0) { 14 | sum = root.val; 15 | } else { 16 | sum += root.val; 17 | ; 18 | } 19 | max[0] = Math.max(max[0], sum); // pre-order traversal 20 | 21 | helper(root.left, max, sum); 22 | helper(root.right, max, sum); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/algorithm/basic/iterative/FindMiddleOfList.java: -------------------------------------------------------------------------------- 1 | package algorithm.basic.iterative; 2 | 3 | import datastrcture.*; 4 | 5 | /** 6 | * Given one linked list, find the middle node of the linked list. 7 | * If there are even number of nodes, return the left middle one. 8 | */ 9 | public class FindMiddleOfList { 10 | public ListNode findMiddle(ListNode head) { 11 | if (head == null || head.next == null) { 12 | return head; 13 | } 14 | 15 | ListNode slow = head; 16 | ListNode fast = head; 17 | // odd number: slow stop at n+1, fast stop at 2n+1 -> fast.next = null 18 | // even number: slow stop at n, fast stop at 1+2(n-1)=2n-1 -> fast.next.next = null 19 | while (fast.next != null && fast.next.next != null) { 20 | slow = slow.next; 21 | fast = fast.next.next; 22 | } 23 | return slow; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSumSorted.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | /** 4 | * Given an sorted array of integers nums and an integer target, 5 | * return indices of the two numbers such that they add up to target. 6 | */ 7 | public class TwoSumSorted { 8 | // Two Pointers: Time O(n), Space O(1) 9 | public int[] twoSumSorted(int[] nums, int target) { 10 | if (nums == null || nums.length == 0) { 11 | return new int[]{-1, -1}; 12 | } 13 | int left = 0; 14 | int right = nums.length - 1; 15 | while (left < right) { 16 | int sum = nums[left] + nums[right]; 17 | if (sum == target) { 18 | return new int[]{left, right}; 19 | } else if (sum > target) { 20 | right--; 21 | } else { 22 | left++; 23 | } 24 | } 25 | return new int[]{-1, -1}; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSum.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Given an unsorted array of integers nums and an integer target, 8 | * return indices of the two numbers such that they add up to target. 9 | */ 10 | public class TwoSum { 11 | // HashMap: Time O(n), Space O(n) 12 | public int[] twoSum(int[] array, int target) { 13 | if (array == null || array.length == 0) { 14 | return new int[]{-1, -1}; 15 | } 16 | Map seen = new HashMap<>(); // 17 | for (int i = 0; i < array.length; i++) { 18 | if (seen.containsKey(target - array[i])) { 19 | return new int[]{i, seen.get(target - array[i])}; 20 | } else { 21 | seen.put(array[i], i); 22 | } 23 | } 24 | return new int[]{-1, -1}; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/algorithm/basic/iterative/MergeTwoSortedList.java: -------------------------------------------------------------------------------- 1 | package algorithm.basic.iterative; 2 | 3 | import datastrcture.*; 4 | 5 | /** 6 | * Given two sorted lists, return the merged list of the given two lists. 7 | */ 8 | public class MergeTwoSortedList { 9 | public ListNode mergeTwoList(ListNode one, ListNode two) { 10 | // use dummy head to handle the corner case 11 | ListNode dummy = new ListNode(0); 12 | ListNode tail = dummy; 13 | 14 | while (one != null || two != null) { 15 | if (one.val < two.val) { 16 | tail.next = one; 17 | one = one.next; 18 | } else { 19 | tail.next = two; 20 | two = two.next; 21 | } 22 | tail = tail.next; 23 | } 24 | 25 | if (one != null) { 26 | tail.next = one; 27 | } else { 28 | tail.next = two; 29 | } 30 | return dummy.next; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/lca/LCA.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.lca; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | /** 6 | * Given two nodes in a binary tree, find their lowest common ancestor. 7 | * The given two nodes are guaranteed to be in the binary tree. 8 | */ 9 | public class LCA { 10 | // Recursion: Time O(n), Space O(height) 11 | public TreeNode lca(TreeNode root, TreeNode p, TreeNode q) { 12 | // base case 13 | if (root == null || root == p || root == q) { 14 | return root; 15 | } 16 | 17 | // step1: ask a value from my left and right, respectively 18 | TreeNode left = lca(root.left, p, q); 19 | TreeNode right = lca(root.right, p, q); 20 | 21 | // step2+3: what should we do in the current level and return to parent 22 | if (left != null && right != null) { // current root is the LCA 23 | return root; 24 | } 25 | return left == null ? right : left; 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/QuickSort.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | public class QuickSort { 4 | private void quickSort(int[] array, int left, int right) { 5 | while (left < right) { 6 | int pivot = partition(array, left, right); 7 | if (pivot - left < right - pivot) { // pivot < (right - left) / 2 8 | quickSort(array, left, pivot - 1); // sort left part with small size 9 | left = pivot + 1; 10 | } else { 11 | quickSort(array, pivot + 1, right); 12 | right = pivot - 1; 13 | } 14 | } 15 | } 16 | 17 | private int partition(int[] array, int left, int right) { 18 | if (left == right) { 19 | return left; 20 | } 21 | return left + 1; 22 | } 23 | 24 | public static void main(String[] args) { 25 | int[] array = {1, 3, -1, 5, -8, 0, 11, 9}; 26 | QuickSort test = new QuickSort(); 27 | test.quickSort(array, 0, array.length - 1); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/lca/LCAOfBST.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.lca; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | /** 6 | * Given two TreeNodes in a binary search tree. 7 | * Find their lowest common ancestor. 8 | * The two nodes are guaranteed to be in the binary search tree. 9 | */ 10 | public class LCAOfBST { 11 | public TreeNode lcaOfBST(TreeNode root, TreeNode p, TreeNode q) { 12 | // base case 13 | if (root == null) { 14 | return root; 15 | } 16 | 17 | if (root.val < p.val && root.val < q.val) { 18 | // case 1: root.val < p.val && root.val < q.val -> p and q in root.right 19 | return lcaOfBST(root.right, p, q); 20 | } else if (root.val > p.val && root.val > q.val) { 21 | // case 2: root.val > p.val && root.val > q.val -> p and q in root.left 22 | return lcaOfBST(root.left, p, q); 23 | } else { 24 | // case 3: p < root < q || p > root > q -> root is LCA 25 | return root; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/lca/LCAOfKNode.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.lca; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * Given K nodes in a binary tree, find their lowest common ancestor. 9 | */ 10 | public class LCAOfKNode { 11 | // Recursion: Time O(n), Space O(max(k, height)) 12 | public TreeNode lcaOfKNode(TreeNode root, List nodes) { 13 | // use hashset to optimize the validation of finding node 14 | Set nodeSet = new HashSet<>(nodes); 15 | return findLCA(root, nodeSet); 16 | } 17 | 18 | private TreeNode findLCA(TreeNode root, Set nodeSet) { 19 | // base case 20 | if (root == null || nodeSet.contains(root)) { // root == p || q 21 | return root; 22 | } 23 | 24 | TreeNode left = findLCA(root.left, nodeSet); 25 | TreeNode right = findLCA(root.right, nodeSet); 26 | 27 | if (left != null && right != null) { 28 | return root; 29 | } 30 | return left == null ? right : left; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSumSmaller.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Determine the number of pairs of elements in a given array 7 | * that sum to a value smaller than the given target number. 8 | */ 9 | public class TwoSumSmaller { 10 | // Sort + TwoPointers 11 | // Time O(nlogn + n) = O(n), Space O(n) 12 | public int twoSumSmaller(int[] array, int target) { 13 | if (array == null || array.length == 0) { 14 | return 0; 15 | } 16 | Arrays.sort(array); 17 | int left = 0; 18 | int right = array.length - 1; 19 | int count = 0; 20 | 21 | while (left < right) { 22 | int sum = array[left] + array[right]; 23 | if (sum < target) { 24 | // all the pair that right in the range (left, right] 25 | // will be smaller than target 26 | count += right - left; 27 | left++; 28 | } else { 29 | right--; 30 | } 31 | } 32 | return count; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSumGreater.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Given an array of integers, find how many pairs in the array such that their sum is bigger than a specific target number. 7 | * Please return the number of pairs. 8 | */ 9 | public class TwoSumGreater { 10 | // Sort + TwoPointers 11 | // Time O(nlogn + n) = O(n), Space O(n) 12 | public int twoSumGreater(int[] array, int target) { 13 | if (array == null || array.length == 0) { 14 | return 0; 15 | } 16 | Arrays.sort(array); 17 | int left = 0; 18 | int right = array.length - 1; 19 | int count = 0; 20 | 21 | while (left < right) { 22 | int sum = array[left] + array[right]; 23 | if (sum > target) { 24 | // all the pair that left in the range [left, right) 25 | // will be greater than target 26 | count += right - left; 27 | right--; 28 | } else { 29 | left++; 30 | } 31 | } 32 | return count; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/construct/BSTFromPre.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.construct; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | public class BSTFromPre { 6 | public TreeNode bstFromPre(int[] preorder) { 7 | // assumption: preorder.length >= 1 8 | return construct(preorder, 0, preorder.length - 1); 9 | } 10 | 11 | private TreeNode construct(int[] pre, int preLeft, int preRight) { 12 | // base case 13 | if (preLeft > preRight) { // the null after leaf node 14 | return null; 15 | } 16 | // find root from pre-order array to divide into left and right parts 17 | TreeNode root = new TreeNode(pre[preLeft]); 18 | int leftSize = 0; 19 | int leftIdx = preLeft; 20 | while (leftIdx + 1 < preRight && pre[leftIdx] < root.val) { 21 | leftSize++; 22 | leftIdx++; 23 | } 24 | // build left and right subtree recursively 25 | root.left = construct(pre, preLeft + 1, preLeft + leftSize); 26 | root.right = construct(pre, preLeft + leftSize + 1, preRight); 27 | 28 | return root; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/algorithm/basic/iterativetree/FlattenTreeToList.java: -------------------------------------------------------------------------------- 1 | package algorithm.basic.iterativetree; 2 | 3 | import datastrcture.*; 4 | 5 | /** 6 | * Given a binary tree, flatten it to a linked list in-place. 7 | */ 8 | 9 | public class FlattenTreeToList { 10 | public void flatten(TreeNode root) { 11 | if (root == null) { // base case 12 | return; 13 | } 14 | TreeNode cur = root; 15 | while (cur != null) { // traverse down along the rightmost path 16 | TreeNode next = cur.right; // store next node 17 | // flatten the left subtree to insert between cur and cur.right 18 | if (cur.left != null) { 19 | // find the tail of the right subtree of cur.left 20 | TreeNode tail = cur.left; 21 | while (tail.right != null) { 22 | tail = tail.right; 23 | } 24 | // connect the tail to stored next 25 | tail.right = next; 26 | cur.right = cur.left; 27 | cur.left = null; // delink 28 | } 29 | cur = cur.right; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSumClosest.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Find the pair of elements in a given array that sum to a value that is closest to the given target number. 7 | * Return the min difference. 8 | */ 9 | public class TwoSumClosest { 10 | // Sort + Two Pointers 11 | // Time O(nlogn + n) = O(nlogn), Space O(n) 12 | public int twoSumClosest(int[] array, int target) { 13 | if (array == null || array.length == 0) { 14 | return -1; 15 | } 16 | Arrays.sort(array); 17 | 18 | int minDiff = Integer.MAX_VALUE; 19 | 20 | int left = 0; 21 | int right = array.length - 1; 22 | 23 | while (left < right) { 24 | int sum = array[left] + array[right]; 25 | minDiff = Math.min(Math.abs(target - sum), minDiff); 26 | if (sum == target) { 27 | return 0; 28 | } else if (sum < target) { 29 | left++; 30 | } else { 31 | right--; 32 | } 33 | } 34 | return minDiff == Integer.MAX_VALUE ? -1 : minDiff; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSumAllPairs.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Find all pairs of elements in a given array that sum to the given target number. 7 | * Return all the pairs of indices. 8 | */ 9 | public class TwoSumAllPairs { 10 | // HashMap: Time O(n), Space O(n) 11 | public List> twoSumAllPairs(int[] array, int target) { 12 | List> res = new ArrayList<>(); 13 | Map> visited = new HashMap<>(); 14 | // key = number, value = list of indices 15 | 16 | for (int i = 0; i < array.length; i++) { 17 | int diff = target - array[i]; 18 | List indices = visited.get(diff); 19 | if (indices != null) { 20 | // enumerate all possible indices pairs of current (array[i], diff) pair 21 | for (int idx : indices) { 22 | res.add(Arrays.asList(i, idx)); 23 | } 24 | } 25 | visited.putIfAbsent(array[i], new ArrayList<>()); 26 | visited.get(array[i]).add(i); 27 | } 28 | return res; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/lca/LCAOfKnaryTree.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.lca; 2 | 3 | import datastrcture.KnaryTreeNode; 4 | 5 | /** 6 | * Given two nodes in a K-nary tree, find their lowest common ancestor. 7 | * The given two nodes are guaranteed to be in the K-nary tree. 8 | */ 9 | public class LCAOfKnaryTree { 10 | // Recursion: Time O(n), Space O(height) 11 | public KnaryTreeNode lcaOfKnaryTree(KnaryTreeNode root, KnaryTreeNode p, KnaryTreeNode q) { 12 | // base case 13 | if (root == null || root == p || root == q) { 14 | return root; 15 | } 16 | 17 | int count = 0; // store how many nodes between p and q we have found 18 | KnaryTreeNode temp = null; // initialize the lca to return in current subtree 19 | 20 | for (KnaryTreeNode child : root.children) { 21 | KnaryTreeNode res = lcaOfKnaryTree(child, p, q); 22 | if (res != null) { 23 | count++; // found one node, update the counter 24 | if (count == 2) { // already found two nodes, current root is LCA 25 | return root; 26 | } 27 | temp = res; // update the lca result 28 | } 29 | } 30 | 31 | return temp; // temp == p || q || null 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/ThreeSumSmaller.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Given an array of n integers nums and an integer target. 7 | * Find the number of triplets whose sum is smaller than target. 8 | */ 9 | public class ThreeSumSmaller { 10 | // Sort + TwoPointers 11 | // Time O(n^2 + nlogn) = O(n^2), Space O(n) for sort 12 | public int threeSumSmaller(int[] array, int target) { 13 | if (array == null || array.length < 3) { 14 | return 0; 15 | } 16 | Arrays.sort(array); 17 | int count = 0; 18 | 19 | for (int i = 0; i < array.length; i++) { 20 | count += countTwoSumSmaller(array, i, target - array[i]); 21 | } 22 | return count; 23 | } 24 | 25 | private int countTwoSumSmaller(int[] array, int start, int target) { 26 | // count number of pairs that sum to target 27 | int count = 0; 28 | int left = start + 1; 29 | int right = array.length - 1; 30 | while (left < right) { 31 | int sum = array[left] + array[right]; 32 | if (sum < target) { 33 | count += right - left; 34 | } else { 35 | right--; 36 | } 37 | } 38 | return count; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/ThreeSumGreater.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Given an array of positive integers representing side length of triangle. 7 | * Take three numbers each time to construct a triangle. 8 | * Count how many triangles we could construct using the given numbers. 9 | */ 10 | public class ThreeSumGreater { 11 | // Sort + TwoPointers 12 | // Time O(n^2 + nlogn) = O(n^2), Space O(n) for sort 13 | public int threeSumGreater(int[] array, int target) { 14 | if (array == null || array.length < 3) { 15 | return 0; 16 | } 17 | Arrays.sort(array); 18 | int count = 0; 19 | 20 | for (int i = 0; i < array.length; i++) { 21 | count += countTwoSumGreater(array, i, target - array[i]); 22 | } 23 | return count; 24 | } 25 | 26 | private int countTwoSumGreater(int[] array, int start, int target) { 27 | // count number of pairs that sum to target 28 | int count = 0; 29 | int left = start + 1; 30 | int right = array.length - 1; 31 | while (left < right) { 32 | int sum = array[left] + array[right]; 33 | if (sum > target) { 34 | count += right - left; 35 | } else { 36 | left++; 37 | } 38 | } 39 | return count; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/ThreeSumClosest.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Given an array nums of n integers and an integer target. 7 | * Find three integers in nums such that the sum is closest to target. 8 | * Return the sum of the three integers. 9 | */ 10 | public class ThreeSumClosest { 11 | // Sort + TwoPointers 12 | // Time O(n^2 + nlogn) = O(n^2), Space O(n) for sort 13 | public int threeSumCloset(int[] array, int target) { 14 | if (array == null || array.length == 0) { 15 | return 0; 16 | } 17 | int minDiff = Integer.MAX_VALUE; // only need to maintain global minDiff with + or - 18 | Arrays.sort(array); 19 | 20 | for (int i = 0; i < array.length; i++) { 21 | // find two sum closest to target - array[i] 22 | int left = i + 1; 23 | int right = array.length - 1; 24 | while (left < right) { 25 | int sum = array[left] + array[right] + array[i]; 26 | // update minDiff for each pair 27 | if (Math.abs(target - sum) < Math.abs(minDiff)) { 28 | minDiff = target - sum; // minDiff will be positive or negative 29 | } 30 | if (sum == target) { 31 | return target; 32 | } else if (sum > target) { 33 | right--; 34 | } else { 35 | left++; 36 | } 37 | } 38 | } 39 | return target - minDiff; // sum will always be target - minDiff 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/lca/LCAOfKnaryMNode.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.lca; 2 | 3 | import datastrcture.KnaryTreeNode; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * Given N nodes in a K-nary tree, find their lowest common ancestor. 9 | * The N nodes are guaranteed to be in the K-nary tree. 10 | */ 11 | public class LCAOfKnaryMNode { 12 | public KnaryTreeNode knaryLCAofMNode(KnaryTreeNode root, List nodes) { 13 | // sanity check 14 | if (root == null || nodes == null || nodes.size() == 0) { 15 | return null; 16 | } 17 | Set nodeSet = new HashSet<>(nodes); 18 | return findLCA(root, nodeSet); 19 | } 20 | 21 | private KnaryTreeNode findLCA(KnaryTreeNode root, Set nodeSet) { 22 | // base case 23 | if (root == null || nodeSet.contains(root)) { 24 | return root; 25 | } 26 | 27 | int count = 0; 28 | KnaryTreeNode temp = null; // initialize the lca to return in current subtree 29 | 30 | for (KnaryTreeNode child : root.children) { 31 | KnaryTreeNode res = findLCA(child, nodeSet); 32 | if (res != null) { 33 | count++; 34 | if (count > 1) { 35 | // found more than one node, similar to count == 2 for two nodes 36 | // that means current root is LCA of multiple nodes 37 | return root; 38 | } 39 | temp = res; // record the found non-null node 40 | } 41 | } 42 | return temp; // found only one node or null 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/algorithm/basic/iterativetree/PopulateNextRight.java: -------------------------------------------------------------------------------- 1 | package algorithm.basic.iterativetree; 2 | 3 | /** 4 | * You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. 5 | * The binary tree has the following definition: 6 | * class Node { 7 | * int val; 8 | * Node left; 9 | * Node right; 10 | * Node next; 11 | * } 12 | * 13 | * 14 | * Populate each next pointer to point to its next right node. 15 | * If there is no next right node, the next pointer should be set to NULL. 16 | * 17 | * Initially, all next pointers are set to NULL. 18 | */ 19 | public class PopulateNextRight { 20 | public Node connect(Node root) { 21 | if (root == null) { 22 | return root; 23 | } 24 | Node leftMost = root; 25 | while (leftMost.left != null) { 26 | // start from the leftmost node, connect current level 27 | Node cur = leftMost; 28 | while (cur != null) { // traverse the linked list of current level 29 | // connect for same root nodes 30 | cur.left.next = cur.right; 31 | // connect for inter-root nodes 32 | if (cur.next != null) { // avoid NPE 33 | cur.right.next = cur.next.left; 34 | } 35 | cur = cur.next; 36 | } 37 | leftMost = leftMost.left; 38 | // traverse the left most path to arrive last level 39 | } 40 | return root; 41 | } 42 | 43 | private static class Node { 44 | int val; 45 | Node left; 46 | Node right; 47 | Node next; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/algorithm/basic/iterativetree/FlattenMultiLevelList.java: -------------------------------------------------------------------------------- 1 | package algorithm.basic.iterativetree; 2 | 3 | /** 4 | * You are given a doubly linked list which in addition to the next and previous pointers, 5 | * it could have a child pointer, which may or may not point to a separate doubly linked list. 6 | * These child lists may have one or more children of their own, and so on. 7 | * 8 | * Flatten the list so that all the nodes appear in a single-level, doubly linked list. 9 | * You are given the head of the first level of the list. 10 | */ 11 | public class FlattenMultiLevelList { 12 | public Node flatten(Node head) { 13 | Node cur = head; 14 | while (cur != null) { 15 | // case 1: no child, no need to flatten child list 16 | if (cur.child == null) { 17 | cur = cur.next; 18 | continue; 19 | } 20 | // case 2: has child, find tail of child list, connect to next 21 | Node tail = cur.child; 22 | while (tail.next != null) { 23 | tail = tail.next; 24 | } 25 | // connect with the child list 26 | tail.next = cur.next; 27 | if (cur.next != null) { // avoid NPE, cur maybe the last node 28 | cur.next.prev = tail; 29 | } 30 | cur.next = cur.child; 31 | cur.child.prev = cur; 32 | cur.child = null; // delink 33 | // traverse next node 34 | cur = cur.next; 35 | } 36 | return head; 37 | } 38 | 39 | private static class Node { 40 | int val; 41 | Node next; 42 | Node prev; 43 | Node child; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/construct/ConstructFromInPre.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.construct; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class ConstructFromInPre { 9 | public TreeNode constructFromInPost(int[] pre, int[] in) { 10 | // assumption: pre.length = in.length >= 1 11 | Map nodeIndex = new HashMap<>(); 12 | // record the TreeNode and index pair to avoid duplicate search in recursion 13 | for (int i = 0; i < in.length; i++) { 14 | nodeIndex.put(in[i], i); 15 | } 16 | return construct(pre, 0, pre.length - 1, in, 0, in.length - 1, nodeIndex); 17 | } 18 | 19 | private TreeNode construct(int[] pre, int preLeft, int preRight, 20 | int[] in, int inLeft, int inRight, 21 | Map nodeIndex) { 22 | // base case 23 | if (preLeft > preRight) { // the null after leaf node 24 | return null; 25 | } 26 | // find the root node of current level to divide to left and right part 27 | TreeNode root = new TreeNode(pre[preLeft]); 28 | // find the index of root in in-order sequence 29 | int rootIdx = nodeIndex.get(root.val); 30 | int leftSize = rootIdx - inLeft; // record the size of left subtree 31 | // construct left and right recursively 32 | root.left = construct(pre, preLeft + 1, preLeft + leftSize, 33 | in, inLeft, rootIdx, nodeIndex); 34 | root.right = construct(pre, preLeft + leftSize + 1, preRight, 35 | in, rootIdx + 1, inRight, nodeIndex); 36 | return root; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/construct/ConstructFromInPost.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.construct; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class ConstructFromInPost { 9 | public TreeNode constructFromInPost(int[] post, int[] in) { 10 | // assumption: post.length = in.length >= 1 11 | Map nodeIndex = new HashMap<>(); 12 | // record the TreeNode and index pair to avoid duplicate search in recursion 13 | for (int i = 0; i < in.length; i++) { 14 | nodeIndex.put(in[i], i); 15 | } 16 | return construct(post, 0, post.length - 1, in, 0, in.length - 1, nodeIndex); 17 | } 18 | 19 | private TreeNode construct(int[] post, int postLeft, int postRight, 20 | int[] in, int inLeft, int inRight, 21 | Map nodeIndex) { 22 | // base case 23 | if (postLeft > postRight) { // the null after leaf node 24 | return null; 25 | } 26 | // find the root node of current level to divide to left and right part 27 | TreeNode root = new TreeNode(post[postRight]); 28 | // find the index of root in in-order sequence 29 | int rootIdx = nodeIndex.get(root.val); 30 | int leftSize = rootIdx - inLeft; // record the size of left subtree 31 | // construct left and right recursively 32 | root.left = construct(post, postLeft, postLeft + leftSize - 1, 33 | in, inLeft, rootIdx, nodeIndex); 34 | root.right = construct(post, postLeft + leftSize, postRight - 1, 35 | in, rootIdx + 1, inRight, nodeIndex); 36 | return root; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/lca/LCA3.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.lca; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Given two nodes in a binary tree (with parent pointer). 7 | * Find their lowest common ancestor. 8 | */ 9 | public class LCA3 { 10 | // Method 1: Find intersection of two LinkedList 11 | // Time O(height), Space O(1) 12 | public Node lca3(Node p, Node q) { 13 | Node node1 = p, node2 = q; 14 | // traver upside to find the intersection 15 | // when node1 reach end, set node1 to start point of node2, i.e. Node q 16 | // thus node1 and node2 will traverse same length (height_p + height_q) 17 | while (node1 != node2) { 18 | node1 = node1 == null ? q : node1.parent; // will cover corner case p == null 19 | node2 = node2 == null ? p : node2.parent; // will cover corner case q == null 20 | } 21 | return node1; 22 | } 23 | 24 | // Method 2: Set 25 | // Time O(height), Space O(height) 26 | public Node lca(Node p, Node q) { 27 | Set visited = new HashSet<>(); 28 | 29 | while (p != null) { 30 | visited.add(p); 31 | p = p.parent; 32 | } 33 | 34 | while (q != null) { 35 | if (visited.contains(q)) { 36 | return q; 37 | } 38 | q = q.parent; 39 | } 40 | return null; 41 | } 42 | 43 | static class Node { 44 | public int val; 45 | public Node left; 46 | public Node right; 47 | public Node parent; 48 | 49 | public Node(int val) { 50 | this.val = val; 51 | this.left = null; 52 | this.right = null; 53 | this.parent = null; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/construct/ConstructFromPrePost.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.construct; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | import java.util.*; 6 | 7 | public class ConstructFromPrePost { 8 | public TreeNode constructFromPrePost(int[] pre, int[] post) { 9 | // assumption: pre.length = post.length >= 1 10 | Map nodeIndex = new HashMap<>(); 11 | // record the TreeNode and index pair to avoid duplicate search in recursion 12 | for (int i = 0; i < post.length; i++) { 13 | nodeIndex.put(post[i], i); 14 | } 15 | return construct(pre, 0, pre.length - 1, post, 0, post.length - 1, nodeIndex); 16 | } 17 | 18 | private TreeNode construct(int[] pre, int preLeft, int preRight, 19 | int[] post, int postLeft, int postRight, 20 | Map nodeIndex) { 21 | // base case 22 | if (preLeft > preRight) { // the null after leaf node 23 | return null; 24 | } 25 | // find the root node of current level to divide to left and right part 26 | TreeNode root = new TreeNode(pre[preLeft]); 27 | // the second node in pre-order will be the left subtree root 28 | // avoid out of bound before get pre[preLeft + 1] 29 | if (preLeft + 1 > preRight) { 30 | return root; 31 | } 32 | // find the index of left root in post-order sequence 33 | int leftRootIdx = nodeIndex.get(pre[preLeft + 1]); 34 | int leftSize = leftRootIdx - postLeft + 1; // record the size of left subtree 35 | // construct left and right recursively 36 | root.left = construct(pre, preLeft + 1, preLeft + leftSize, 37 | post, postLeft, leftRootIdx, nodeIndex); 38 | root.right = construct(pre, preLeft + leftSize + 1, preRight, 39 | post, leftRootIdx + 1, postRight, nodeIndex); 40 | return root; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/algorithm/basic/iterativetree/PopulateNextRight2.java: -------------------------------------------------------------------------------- 1 | package algorithm.basic.iterativetree; 2 | 3 | /** 4 | * You are given a tree: 5 | * class Node { 6 | * int val; 7 | * Node left; 8 | * Node right; 9 | * Node next; 10 | * } 11 | * 12 | * 13 | * Populate each next pointer to point to its next right node. 14 | * If there is no next right node, the next pointer should be set to NULL. 15 | * 16 | * Initially, all next pointers are set to NULL. 17 | */ 18 | public class PopulateNextRight2 { 19 | public Node connect2(Node root) { 20 | if (root == null) { 21 | return null; 22 | } 23 | Node leftMost = root; 24 | while (leftMost != null) { 25 | // start from leftMost node to traverse current level 26 | // and connect the next level 27 | Node cur = leftMost; // current node at current level 28 | Node dummy = new Node(0); // used to store head of next level 29 | Node prev = dummy; // the leading node of next level 30 | // use dummy to avoid null check to simplify the implementation 31 | while (cur != null) { 32 | // traverse current level to connect next level node 33 | // populate prev.next to next node in the next level 34 | if (cur.left != null) { 35 | prev.next = cur.left; 36 | prev = prev.next; 37 | } 38 | if (cur.right != null) { 39 | prev.next = cur.right; 40 | prev = prev.next; 41 | } 42 | // move to next node in current level 43 | cur = cur.next; 44 | } 45 | // move leftMost to left most node (head) of next level 46 | leftMost = dummy.next; 47 | } 48 | return root; 49 | } 50 | 51 | private static class Node { 52 | int val; 53 | Node left; 54 | Node right; 55 | Node next; 56 | 57 | public Node(int val) { 58 | this.val = val; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/algorithm/basic/iterative/ReorderList.java: -------------------------------------------------------------------------------- 1 | package algorithm.basic.iterative; 2 | 3 | import datastrcture.*; 4 | 5 | /** 6 | * Given a singly linked list L_0 → L_1 → … → L_{n-1} → L_n 7 | * Reverse it to L_0 → L_n → L_{n-1} → … → L_1 → L_0 8 | */ 9 | public class ReorderList { 10 | public void reorderList(ListNode head) { 11 | if (head == null || head.next == null) { 12 | return; 13 | } 14 | ListNode mid = findMiddle(head); 15 | ListNode secondHead = reverseList(mid.next); 16 | mid.next = null; // de-link to avoid circle 17 | head = mergeTwoList(head, secondHead); 18 | } 19 | 20 | private ListNode findMiddle(ListNode head) { 21 | // assumption: head is not null 22 | ListNode slow = head; 23 | ListNode fast = head; 24 | // odd number: slow stop at n+1, fast stop at 2n+1 -> fast.next = null 25 | // even number: slow stop at n, fast stop at 1+2(n-1)=2n-1 -> fast.next.next = null 26 | while (fast.next != null && fast.next.next != null) { 27 | slow = slow.next; 28 | fast = fast.next.next; 29 | } 30 | return slow; 31 | } 32 | 33 | private ListNode reverseList(ListNode head) { 34 | // assumption: head is not null 35 | ListNode prev = null; 36 | ListNode cur = head; 37 | ListNode next = null; 38 | 39 | while (cur != null) { 40 | next = cur.next; 41 | cur.next = prev; 42 | prev = cur; 43 | cur = next; 44 | } 45 | return prev; 46 | } 47 | 48 | private ListNode mergeTwoList(ListNode one, ListNode two) { 49 | // use dummy head to handle the corner case, one is oddHead, two is evenHead 50 | ListNode dummy = new ListNode(0); 51 | ListNode tail = dummy; 52 | 53 | while (one != null && two != null) { 54 | tail.next = one; 55 | one = one.next; 56 | tail.next.next = two; 57 | two = two.next; 58 | tail = tail.next.next; 59 | } 60 | 61 | // first half sublist will be longer or equal than second half list 62 | if (one != null) { 63 | tail.next = one; 64 | } 65 | return dummy.next; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSumDifference.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Given an sorted array of integers, find two numbers that their difference equals to a target value. 7 | * Return a list with two number [num1, num2] that num1 is less than num2. 8 | */ 9 | public class TwoSumDifference { 10 | // Method 1: Two Pointers 11 | // Time O(n), Space O(1) 12 | public static int[] twoSumDifference(int[] array, int target) { 13 | if (array == null || array.length == 0) { 14 | return new int[0]; 15 | } 16 | target = Math.abs(target); // min - max = -1 * (max - min) 17 | int left = 0; 18 | int right = 1; 19 | 20 | while (left < right && right < array.length) { 21 | int diff = array[right] - array[left]; 22 | if (diff == target) { 23 | return new int[]{array[left], array[right]}; 24 | } else if (diff < target) { // right is not big enough 25 | right++; 26 | } else { // left is a little small 27 | left++; 28 | if (left == right) { // left and right can't be the same 29 | right++; 30 | } 31 | } 32 | } 33 | return new int[]{-1, -1}; 34 | } 35 | 36 | // Solution 2: HashSMap 37 | // Time O(n), Space O(n) 38 | public static int[] twoSumDifference2(int[] array, int target) { 39 | if (array == null || array.length == 0) { 40 | return new int[0]; 41 | } 42 | Map visited = new HashMap<>(); 43 | 44 | for (int i = 0; i < array.length; i++) { 45 | // the value added into map before array[i] will be smaller than array[i] 46 | if (visited.containsKey(array[i] - target)) { // target > 0 47 | return new int[]{array[i] - target, array[i]}; 48 | } else if (visited.containsKey(array[i] + target)) { // target < 0 49 | return new int[]{array[i] + target, array[i]}; 50 | } else { 51 | visited.put(array[i], i); 52 | } 53 | } 54 | return new int[]{-1, -1}; 55 | } 56 | 57 | public static void main(String[] args) { 58 | int[] nums = {0, 1, 4, 6, 9, 11, 16, 18, 20}; 59 | int[] targets = {8, -3, 21, -6}; 60 | for (int target : targets) { 61 | System.out.println(Arrays.toString(twoSumDifference(nums, target))); 62 | System.out.println(Arrays.toString(twoSumDifference2(nums, target))); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/algorithm/recursion/tree/lca/LCA2.java: -------------------------------------------------------------------------------- 1 | package algorithm.recursion.tree.lca; 2 | 3 | import datastrcture.TreeNode; 4 | 5 | /** 6 | * Given two nodes in a binary tree, find their lowest common ancestor. 7 | * The given two nodes may not in the binary tree. Return null if any of the nodes is not in the tree. 8 | */ 9 | public class LCA2 { 10 | // Method 1: Recursion + Find existence 11 | // Time O(n), Space O(height) 12 | public TreeNode lca2(TreeNode root, TreeNode p, TreeNode q) { 13 | if (root == null || p == null || q == null) { // sanity check 14 | return null; 15 | } 16 | TreeNode res = findLCA(root, p, q); 17 | // need to check whether res == p || q 18 | if (res == p) { 19 | // find q in the subtree of p to determine whether q in the tree 20 | return findLCA(p, q, q) != null ? res : null; 21 | } 22 | if (res == q) { 23 | // find p in the subtree of q to determine whether q in the tree 24 | return findLCA(q, p, p) != null ? res : null; 25 | } 26 | return res; // (res != p && res != q) || res == null 27 | } 28 | 29 | private TreeNode findLCA(TreeNode root, TreeNode p, TreeNode q) { 30 | // base case 31 | if (root == null || root == p || root == q) { 32 | return root; 33 | } 34 | TreeNode left = findLCA(root.left, p, q); 35 | TreeNode right = findLCA(root.right, p, q); 36 | 37 | if (left != null && right != null) { // current root is LCA 38 | return root; 39 | } 40 | return left != null ? left : right; 41 | } 42 | 43 | // Method 2: Recursion with counter 44 | // Time O(n), Space O(height) 45 | private int count = 0; // how many nodes between one and two we have found 46 | 47 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode one, TreeNode two) { 48 | // sanity check 49 | if (root == null || one == null || two == null) { 50 | return null; 51 | } 52 | TreeNode lca = getLCA(root, one, two); 53 | return count == 2 ? lca : null; 54 | } 55 | 56 | private TreeNode getLCA(TreeNode root, TreeNode one, TreeNode two) { 57 | // base case 58 | if (root == null) { 59 | return null; 60 | } 61 | // must go through all the recursion, can't return early 62 | TreeNode left = getLCA(root.left, one, two); 63 | TreeNode right = getLCA(root.right, one, two); 64 | 65 | if (root == one || root == two) { 66 | count++; 67 | return root; 68 | } 69 | if (left != null && right != null) { 70 | return root; 71 | } 72 | return left != null ? left : right; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/KSum.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Given an array of integers, an integer target and an integer K. 7 | * Find all unique pairs with size = K in the array which gives the sum of target. 8 | */ 9 | public class KSum { 10 | public static List> kSum(int[] array, int k, int target) { 11 | if (array == null || array.length < k || k < 2) { 12 | return new ArrayList<>(); 13 | } 14 | Arrays.sort(array); // sort array in main function 15 | return findKSum(array, k, target, 0); 16 | } 17 | 18 | private static List> findKSum(int[] array, int k, int target, int start) { 19 | // assumption: array.length > k && k >= 2 20 | // base case 21 | if (k == 2) { // 2 sum 22 | return findTwoSum(array, target, start); 23 | } 24 | // recursive reduction 25 | List> res = new ArrayList<>(); 26 | for (int i = start; i < array.length - k + 1; i++) { 27 | if (i > start && array[i] == array[i - 1]) { // skip duplicate for the fixed first number 28 | continue; 29 | } 30 | List> subPairs = findKSum(array, k - 1, target - array[i], i + 1); 31 | for (List pair : subPairs) { 32 | pair.add(0, array[i]); // k-1 sum + current fixed number -> k sum 33 | } 34 | res.addAll(subPairs); 35 | } 36 | return res; 37 | } 38 | 39 | private static List> findTwoSum(int[] array, int target, int start) { 40 | // find all unique two sum pairs with sum to target 41 | // assumption: array is sorted, array.length > 2 42 | List> res = new ArrayList<>(); 43 | int left = start; 44 | int right = array.length - 1; 45 | while (left < right) { 46 | int sum = array[left] + array[right]; 47 | if (sum == target) { 48 | res.add(new ArrayList<>(Arrays.asList(array[left++], array[right--]))); 49 | // skip duplicate 50 | while (left < right && array[left] == array[left - 1]) { 51 | left++; 52 | } 53 | while (left < right && array[right] == array[right + 1]) { 54 | right--; 55 | } 56 | } else if (sum > target) { 57 | right--; 58 | } else { 59 | left++; 60 | } 61 | } 62 | return res; 63 | } 64 | 65 | public static void main(String[] args) { 66 | int[] nums = {0, 1, 1, -4, 0, 6, 9, -11, 16, 8, 5, 0, 0}; 67 | int[] targets = {5, 0, -10, 16}; 68 | for (int target : targets) { 69 | System.out.println("Target is " + target); 70 | print(kSum(nums, 4, target)); 71 | } 72 | for (int target : targets) { 73 | System.out.println("Target is " + target); 74 | print(kSum(nums, 5, target)); 75 | } 76 | } 77 | 78 | private static void print(List> list) { 79 | if (list == null || list.size() == 0 || list.get(0) == null || list.get(0).size() == 0) { 80 | return; 81 | } 82 | for (List subList : list) { 83 | System.out.println(subList); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSumDesign.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Design and implement a TwoSum class. It should support the following operations: 7 | * add - Add the number to an internal data structure. 8 | * find - Find if there exists any pair of numbers which sum is equal to the value. 9 | */ 10 | public class TwoSumDesign { 11 | // Method 1: HashMap -> Optimize add 12 | // Time: add O(1), find O(n) 13 | // Space: O(n) 14 | Map counter; 15 | 16 | public TwoSumDesign() { 17 | this.counter = new HashMap<>(); 18 | } 19 | 20 | public void add(int number) { 21 | counter.put(number, counter.getOrDefault(number, 0) + 1); 22 | } 23 | 24 | public boolean find(int value) { 25 | for (int num : counter.keySet()) { 26 | int diff = value - num; 27 | int countNeeded = num == diff ? 2 : 1; 28 | if (counter.getOrDefault(diff, 0) >= countNeeded) { 29 | return true; 30 | } 31 | } 32 | return false; 33 | } 34 | } 35 | 36 | class TwoSumSetVersion { 37 | // Method 2: HashMap + Set -> Optimize find 38 | // Time: add O(n), find O(1) 39 | // Space: O(n) 40 | Map counter; 41 | Set sums; // eager computation for all possible sums 42 | 43 | public TwoSumSetVersion() { 44 | this.counter = new HashMap<>(); 45 | this.sums = new HashSet<>(); 46 | } 47 | 48 | public void add(int number) { 49 | for (int num : counter.keySet()) { 50 | sums.add(num + number); 51 | } 52 | counter.put(number, counter.getOrDefault(number, 0) + 1); 53 | } 54 | 55 | public boolean find(int value) { 56 | return sums.contains(value); 57 | } 58 | } 59 | 60 | class TwoSumListVersion { 61 | // method 3: ArrayList + Two Pointers -> Optimized add 62 | // Time: add O(1), find O(nlogn) 63 | // Space: O(n) 64 | List nums; 65 | 66 | public TwoSumListVersion() { 67 | this.nums = new ArrayList<>(); 68 | } 69 | 70 | public void add(int number) { 71 | nums.add(number); 72 | } 73 | 74 | public boolean find(int value) { 75 | Collections.sort(nums); 76 | int left = 0; 77 | int right = nums.size() - 1; 78 | 79 | while (left < right) { 80 | int sum = nums.get(left) + nums.get(right); 81 | if (sum == value) { 82 | return true; 83 | } else if (sum > value) { 84 | right--; 85 | } else { 86 | left++; 87 | } 88 | } 89 | return false; 90 | } 91 | 92 | // method 4: ArrayList Insertion Sort + Two Pointers -> Optimize find 93 | // Time: add O(n), find O(n) 94 | // Space: O(n) 95 | public void add4(int number) { 96 | nums.add(number); 97 | int idx = nums.size() - 1; 98 | // insertion sort to swap until maintain the correct order 99 | while (idx > 0 && nums.get(idx - 1) > nums.get(idx)) { // iterate until idx = 1 100 | int tmp = nums.get(idx - 1); 101 | nums.set(idx - 1, nums.get(idx)); 102 | nums.set(idx, tmp); 103 | idx--; 104 | } 105 | } 106 | 107 | public boolean find4(int value) { 108 | int left = 0; 109 | int right = nums.size() - 1; 110 | 111 | while (left < right) { 112 | int sum = nums.get(left) + nums.get(right); 113 | if (sum == value) { 114 | return true; 115 | } else if (sum > value) { 116 | right--; 117 | } else { 118 | left++; 119 | } 120 | } 121 | return false; 122 | } 123 | } -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/TwoSumAllUniquePairs.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Given an unsorted array with duplicates and target value. 7 | * Find all pairs of elements that sum to the target number. 8 | * Return all the distinct pairs of values. 9 | */ 10 | public class TwoSumAllUniquePairs { 11 | // Solution 1: Sort + Two pointers 12 | // Time O(nlogn + n) = O(nlogn), Space O(n) 13 | public List> twoSumAllUniquePairs(int[] array, int target) { 14 | List> res = new ArrayList<>(); 15 | if (array == null || array.length == 0) { 16 | return res; 17 | } 18 | Arrays.sort(array); 19 | int left = 0; 20 | int right = array.length - 1; 21 | while (left < right) { 22 | int sum = array[left] + array[right]; 23 | if (sum == target) { 24 | res.add(Arrays.asList(array[left++], array[right--])); 25 | // deduplicate 26 | while (left < right && array[left] == array[left - 1]) { 27 | left++; 28 | } 29 | while (left < right && array[right] == array[right + 1]) { 30 | right--; 31 | } 32 | } else if (sum > target) { 33 | right--; 34 | while (left < right && array[right] == array[right + 1]) { // optional 35 | right--; 36 | } 37 | } else { 38 | left++; 39 | while (left < right && array[left] == array[left - 1]) { // optional 40 | left++; 41 | } 42 | } 43 | } 44 | return res; 45 | } 46 | 47 | // Solution 2: HashSet 48 | // Time O(n), Space O(n) 49 | public List> twoSumAllUniquePairsHashMap(int[] array, int target) { 50 | List> res = new ArrayList<>(); 51 | if (array == null || array.length == 0) { 52 | return res; 53 | } 54 | 55 | Set visited = new HashSet<>(); // record unique numbers visited 56 | Set used = new HashSet<>(); // indices used in solution pairs to deduplicate 57 | 58 | for (int num : array) { 59 | int diff = target - num; 60 | if (visited.contains(diff)) { 61 | // need to check whether this pair is already used before 62 | if (!used.contains(num) && !used.contains(diff)) { // haven't found current pair 63 | res.add(Arrays.asList(num, diff)); 64 | used.add(num); 65 | used.add(diff); 66 | } 67 | } else { 68 | visited.add(num); 69 | } 70 | } 71 | return res; 72 | } 73 | 74 | /** 75 | * Follow-up: Only return count of all unique pairs 76 | * HashMap: Time O(n), Space O(n) 77 | */ 78 | public int twoSumAllUniquePairsCount(int[] array, int target) { 79 | if (array == null || array.length == 0) { 80 | return 0; 81 | } 82 | 83 | Map visited = new HashMap<>(); 84 | // key = number, value = whether used in solution pair 85 | int count = 0; 86 | 87 | for (int num : array) { 88 | int diff = target - num; 89 | if (visited.containsKey(diff)) { 90 | // need to check whether this pair is already used before 91 | if (!visited.get(diff)) { 92 | count++; 93 | visited.put(diff, true); 94 | visited.put(num, true); 95 | } 96 | } else { 97 | visited.put(num, false); 98 | } 99 | } 100 | return count; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | # Pattern Summary for Algorithm Problems 2 | 3 | [![](https://img.shields.io/badge/LeetCode-Solution-yellow?logo=leetcode)](https://github.com/zdong1995/LeetCode-Solution-Well-Explained) [![](https://img.shields.io/badge/Algo-Pattern-blue?&logo=github)](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems) ![java](https://img.shields.io/badge/Language-Java-orange?logo=java) ![Build](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems/workflows/Build/badge.svg) [![](https://img.shields.io/badge/Github-zdong1995-blue?style=social&logo=github)](https://github.com/zdong1995/) 4 | 5 | My summary of patterns for algorithm questions, based on similarities of techniques we used to solve the problem, with detailed beginner-friendly tutorial of Data Structure and Algorithm. 6 | 7 | English version tutorial will be updating regularly, please check my website: 8 | 9 | [Algorithm Interview Preparation](https://dongxiaoran.com/en/algo/) by **zdong1995**. 10 | 11 | Welcome to check my repository [LeetCode-Solution-Well-Explained](https://github.com/zdong1995/LeetCode-Solution-Well-Explained) with detailed explanations and comments for LeetCode questions in different categories. If this is helpful for you, please feel free to add a star to the repo. Thank you! ❤️ 12 | 13 | This repo will be updated as following parts: 14 | - Part 1: Start from basic Data Structure and Algorithm to get good understanding of Recursion, use Two Pointers technique in large mounts of questions and practice sorting algorithms. 15 | 16 | - Part 2: Search algorithm, especially graph search. BFS, DFS, pruning, memoization, greedy and dynamic programming. 17 | 18 | - Part 3: Popular problems and advanced data structure like Trie, Union Find, TreeSet, TreeMap. 19 | 20 | ### Table of Contents 21 | 22 | * [Fundamental](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#fundamental) 23 | * [Recursion](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#recursion) 24 | * [Two Pointers](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#two-pointers) 25 | * [Sorting](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#sorting) 26 | * [Breadth First Search \(BFS\)](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#bfs) 27 | * [Depth First Search \(DFS\)](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#dfs) 28 | * [Dynamic Programming \(DP\)](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#dp) 29 | * [Popular Problems](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#popular) 30 | 31 | Popular interview algorithm problems summarized by different patterns. 32 | 33 | ### Fundamental 34 | 35 | * [x] Iterative in LinkedList: [Code](src/main/java/algorithm/basic/iterative) 36 | * [x] In-place Iterative in Tree: [Code](src/main/java/algorithm/basic/iterativetree) 37 | * [ ] Binary Search 38 | 39 | ### Recursion 40 | 41 | * [ ] Introduction to Recursion 42 | * [ ] Recursion in LinkedList 43 | * [ ] Recursion in Tree 44 | * [ ] Recursion to Iterative 45 | 46 | ### Two Pointers 47 | 48 | * [x] Two Sum Pattern: [Code](src/main/java/algorithm/pointers/twosum) 49 | * [ ] Slow-Fast Pointers 50 | * [ ] K-way Merge 51 | * [ ] Deduplication 52 | * [ ] Sliding Window 53 | * [ ] Partition 54 | 55 | ### Sorting 56 | 57 | * [ ] Merge Sort 58 | * [ ] Quick Sort 59 | * [ ] Quick Selection 60 | * [ ] Bucket Sort and Count Sort 61 | 62 | ### BFS 63 | 64 | * [ ] Level Order Traversal 65 | * [ ] The Shortest Path 66 | * [ ] Connected Component 67 | * [ ] Topology Sorting 68 | * [ ] Best-First Search and Dijkstra 69 | 70 | ### DFS 71 | 72 | * [ ] Subset 73 | * [ ] Permutation 74 | * [ ] Parentheses 75 | * [ ] Memoization 76 | * [ ] Graph Search 77 | 78 | ### DP 79 | 80 | * [ ] 1D DP 81 | * [ ] 2D DP 82 | * [ ] Greedy 83 | 84 | ### Popular 85 | 86 | - [ ] Merge Intervals 87 | 88 | - [ ] Two Heap Pattern 89 | 90 | - [ ] Top-K Problem 91 | 92 | - [ ] Union Find 93 | 94 | - [ ] Data Structure Design 95 | 96 | - [ ] Bit Operations -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/FourSum.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Given an array of integers and an integer target. 7 | * Find all unique quadruplets in the array which gives the sum of target. 8 | */ 9 | public class FourSum { 10 | // Method 1: HashMap + HashSet 11 | // Time O(n^2), Space O(n) 12 | public List> fourSum(int[] nums, int target) { 13 | Set> set = new HashSet<>(); 14 | Map>> map = new HashMap<>(); 15 | Arrays.sort(nums); 16 | // find the 2 sum pair as first pair in the 4 sum solution 17 | // i1 < j1 < i2 < j2 in pair (i1, j1, i2, j2), thus i1 < n-3, j1 < n-2 18 | for (int i = 0; i < nums.length - 3; i++) { // left index of the first pair 19 | for (int j = i + 1; j < nums.length - 2; j++) { 20 | int curSum = nums[i] + nums[j]; 21 | List> pairs = map.getOrDefault(curSum, new ArrayList<>()); 22 | pairs.add(Arrays.asList(i, j)); 23 | map.put(curSum, pairs); 24 | } 25 | } 26 | // deal with the second pair in the 4 sum solution 27 | // i2 > 2, j2 > 3 28 | for (int i = 2; i < nums.length - 1; i++) { 29 | for (int j = i + 1; j < nums.length; j++) { 30 | int curSum = nums[i] + nums[j]; 31 | List> firstPairs = map.get(target - curSum); 32 | if (firstPairs == null) { // can't find complement pair to current pair 33 | continue; 34 | } 35 | for (List pair : firstPairs) { 36 | if (pair.get(1) < i) { 37 | // the right index of complement pair should be smaller 38 | // than the current pair's left index, to avoid duplicate 39 | set.add(Arrays.asList(nums[pair.get(0)], nums[pair.get(1)], nums[i], nums[j])); 40 | } 41 | } 42 | } 43 | } 44 | return new ArrayList<>(set); 45 | } 46 | 47 | // Method 2: Sort + Two Pointer for Three Sum 48 | // Time O(n^3), Space O(n) for sort 49 | public List> fourSum2(int[] nums, int target) { 50 | Arrays.sort(nums); 51 | List> res = new ArrayList<>(); 52 | 53 | // enumerate all possible first number in the quadruplet 54 | for (int i = 0; i < nums.length - 3; i++) { 55 | if (i > 0 && nums[i] == nums[i - 1]) { // skip all duplicate first number 56 | continue; 57 | } 58 | findThreeSum(res, nums, target - nums[i], i); 59 | } 60 | return res; 61 | } 62 | 63 | private void findThreeSum(List> res, int[] nums, int target, int start) { 64 | for (int i = start + 1; i < nums.length; i++) { 65 | if (i > start + 1 && nums[i] == nums[i - 1]) { // skip duplicate start index 66 | continue; 67 | } 68 | // find two sum pairs equal to target - nums[i] 69 | int left = i + 1; 70 | int right = nums.length - 1; 71 | while (left < right) { 72 | int sum = nums[left] + nums[right]; 73 | if (sum == target - nums[i]) { 74 | res.add(Arrays.asList(nums[start], nums[i], nums[left++], nums[right--])); 75 | // skip duplicate 76 | while (left < right && nums[left] == nums[left - 1]) { 77 | left++; 78 | } 79 | while (left < right && nums[right] == nums[right + 1]) { 80 | right--; 81 | } 82 | } else if (sum > target - nums[i]) { 83 | right--; 84 | } else { 85 | left++; 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/merge/MergeKSortedList.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.merge; 2 | 3 | import datastrcture.ListNode; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * You are given an array of k linked lists, each is sorted in ascending order. 9 | * Merge all the linked-lists into one sorted linked-list and return it. 10 | */ 11 | public class MergeKSortedList { 12 | // Method 1: K Pointers Merge with MinHeap 13 | // Time: O(nK*logK), Space: O(K) 14 | public ListNode mergeKSortedList(ListNode[] lists) { 15 | if (lists == null || lists.length == 0) { 16 | return null; 17 | } 18 | 19 | int k = lists.length; 20 | // keep k pointers to merge k list, each time move next the list with smallest 21 | // thus we can use a min heap with size k to compare the k pointers 22 | PriorityQueue minHeap = new PriorityQueue<>(k, (n1, n2) -> n1.val - n2.val); 23 | // initialize: add k head to the heap 24 | for (ListNode head : lists) { 25 | if (head != null) { // corner case: the k list may have null list 26 | minHeap.offer(head); 27 | } 28 | } 29 | 30 | ListNode dummy = new ListNode(0); 31 | ListNode cur = dummy; 32 | 33 | // each add the smallest node to the new list, and add next node into the heap 34 | while (!minHeap.isEmpty()) { 35 | ListNode node = minHeap.poll(); 36 | if (node.next != null) { // added all the nodes of one list 37 | minHeap.offer(node.next); 38 | } 39 | cur.next = node; 40 | node.next = null; 41 | cur = node; 42 | } 43 | 44 | return dummy.next; 45 | } 46 | 47 | // Method 2: Recursive Binary Reduction, Divide and Conquer 48 | // Time: O(nK*logK), Space: O(K) for call stack 49 | public ListNode mergeKSortedList1(ListNode[] lists) { 50 | if (lists == null || lists.length == 0) { 51 | return null; 52 | } 53 | return merge(lists, 0, lists.length - 1); 54 | } 55 | 56 | // return merged lists that range from start to end 57 | private ListNode merge(ListNode[] lists, int start, int end) { 58 | // base case 59 | if (start == end) { // the list itself 60 | return lists[start]; 61 | } 62 | 63 | int mid = start + (end - start) / 2; 64 | ListNode left = merge(lists, start, mid); 65 | ListNode right = merge(lists, mid + 1, end); 66 | return mergeTwoList(left, right); 67 | } 68 | 69 | // Method 3: Iterative Binary Reduction 70 | // Time: O(nK*logK), Space: O(1) 71 | public ListNode mergeKSortedList2(ListNode[] lists) { 72 | if (lists == null || lists.length == 0) { 73 | return null; 74 | } 75 | 76 | int k = lists.length; 77 | // reuse the list node array 78 | for (int interval = 1; interval < k; interval *= 2) { 79 | for (int i = 0; i < (k - interval); i += interval * 2) { 80 | lists[i] = mergeTwoList(lists[i], lists[i + interval]); 81 | } 82 | } 83 | return lists[0]; 84 | } 85 | 86 | // Method 4: Iterative Merge Two List 87 | // Time: O(nK^2), Space: O(1) 88 | public ListNode mergeKSortedList3(ListNode[] lists) { 89 | if (lists == null || lists.length == 0) { 90 | return null; 91 | } 92 | 93 | ListNode merged = lists[0]; 94 | // for loop can cover the case that length == 1 95 | // if length = 1, the for loop will be i = 1 < 1, will not enter 96 | for (int i = 1; i < lists.length; i++) { 97 | merged = mergeTwoList(lists[i], merged); 98 | } 99 | 100 | return merged; 101 | } 102 | 103 | private ListNode mergeTwoList(ListNode head1, ListNode head2) { 104 | ListNode dummy = new ListNode(0); 105 | ListNode cur = dummy; 106 | 107 | while (head1 != null && head2 != null) { 108 | if (head1.val < head2.val) { 109 | cur.next = head1; 110 | head1 = head1.next; 111 | } else { 112 | cur.next = head2; 113 | head2 = head2.next; 114 | } 115 | cur = cur.next; 116 | } 117 | 118 | if (head1 != null) { 119 | cur.next = head1; 120 | } 121 | if (head2 != null) { 122 | cur.next = head2; 123 | } 124 | return dummy.next; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://img.shields.io/badge/LeetCode-Solution-yellow?logo=leetcode)](https://github.com/zdong1995/LeetCode-Solution-Well-Explained) [![](https://img.shields.io/badge/Algo-Pattern-blue?&logo=github)](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems) ![java](https://img.shields.io/badge/Language-Java-orange?logo=java) [![](https://img.shields.io/badge/English-ClickHere-de5f63?logo=markdown)](README-EN.md) ![Build](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems/workflows/Build/badge.svg) 2 | 3 | ![](./docs/img/qrcode.png) 4 | 文章首发于公众号『[董小染](./docs/img/wechat.png)』,算法文章主页:[https://blog.andydong.com/algo/](https://blog.andydong.com/algo/) 5 | 6 | --- 7 | 8 | PS: 本仓库含有按类别总结题目的源码,便于本地 debug 练习。对于没有收录的题目,欢迎点击上方 Badge 链接阅读我的 [LeetCode Solution Well Explained](https://github.com/zdong1995/LeetCode-Solution-Well-Explained) 代码。如果对你有帮助欢迎在 **Github 点个 Star**,给我继续更新的动力,谢谢! 9 | 10 | ![](./docs/img/algo-summary.png) 11 | 12 | 在当前 LeetCode 千题时代下,刷完所有的题目是不现实的,因此更重要的是培养自己的**算法思维**和扎实**数据结构知识**,举一反三,深化思维。我们的目的不是完成题目,而是掌握思维过程和解决问题的能力,争取**做一题解决一类题目**,一通百通,高效快速准备算法面试。 13 | 14 | 本仓库归纳总结了 LeetCode 的同类型及相似思路的题目,按照从易到难的顺序,从线性到非线性数据结构的『**增删改查**』,从基础 Iterative 到深化 Recursion 思想,从 Intuitive 的算法到高阶的优化技巧,选取都是我认为比较经典或培养算法思维的题目,带你搭建起来自己的**思维框架**和**套路模板**。 15 | 16 | - 第一部分:从基础数据结构入手,从**递归**思想的入门与深化,再到高频的**双指针**和排序算法。 17 | - 第二部分:掌握搜索算法,宽度优先,深度优先,到**记忆化存储**,**剪枝**优化,入门**动态规划**。 18 | - 第三部分:高频面试题目分析,复杂题目讲解,高级数据结构等。 19 | 20 | ![](./docs/img/ds-summary.png) 21 | 22 | 在开始刷题学习前,花几分钟阅读一下我总结的算法学习方法论和我的刷题练习经验分享,帮助你建立一个更清晰的 big picture 和学习思路。 23 | 24 | - [半年零基础到 LeetCode 300 题,我的算法学习方法论](https://blog.andydong.com/algo/basic/intro/) 25 | - [当我们谈论刷题时,到底在刷什么](https://blog.andydong.com/algo/basic/how/) 26 | 27 | ### 目录 28 | 29 | 1. [基础数据结构与算法](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#1-基础数据结构与算法) 30 | 2. [优雅重要的递归思维](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#2-优雅重要的递归思维) 31 | 3. [巧妙高效的双指针](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#3-巧妙高效的双指针) 32 | 4. [轻松手写排序算法](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#4-轻松手写排序算法) 33 | 5. [宽度优先搜索 \(BFS\)](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#5-宽度优先搜索) 34 | 6. [深度优先搜索 \(DFS\)](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#6-深度优先搜索) 35 | 7. [数学归纳法与动态规划 \(DP\)](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#7-从数学归纳法入门动态规划) 36 | 8. [高频题目类型讲解](https://github.com/zdong1995/Pattern-Summary-for-Algorithm-Problems#8-高频题目类型讲解) 37 | 38 | ### 1. 基础数据结构与算法 39 | 40 | #### 1.1 LinkedList 的 Iterative 操作 41 | - **Article**: [一题顶四题,一道题掌握 LinkedList 的 Iterative](https://blog.andydong.com/algo/basic/iterativelist/) 42 | - **Algorithm Questions**: 43 | - LC143: Reorder List 44 | - LC876: Find the middle node of Linked List 45 | - LC206: Reverse Linked List 46 | - LC21: Merge Two Sorted Linked list 47 | - **Solution**: [My Java Code](src/main/java/algorithm/basic/iterative) 48 | 49 | #### 1.2 Tree Iterative Traversal as Multi-level LinkedList 50 | 51 | - **Article**: [Tree 还可以这样 O(1) 空间遍历?四题带你深入理解 LinkedList 与 Tree 的关系](https://blog.andydong.com/algo/basic/treetolist/) 52 | - **Algorithm Questions**: 53 | - LC114: Flatten Binary Tree to Linked List 54 | - LC430: Flatten a Multilevel Doubly Linked List 55 | - LC116: Populating Next Right Pointers in Each Node 56 | - LC117: Populating Next Right Pointers in Each Node II 57 | - **Solution**: [My Java Code](src/main/java/algorithm/basic/iterativetree) 58 | 59 | 60 | ### 2. 优雅重要的递归思维 61 | 62 | - [ ] Introduction to Recursion 63 | 64 | - [ ] Recursion in LinkedList 65 | 66 | - [ ] Recursion in Tree 67 | 68 | - [ ] Recursion to Iterative 69 | 70 | ### 3. 巧妙高效的双指针 71 | 72 | - [x] Two Sum Pattern: [Code](src/main/java/algorithm/pointers/twosum) 73 | - [ ] Slow-Fast Pointers 74 | 75 | - [ ] K-way Merge 76 | 77 | - [ ] Deduplication 78 | 79 | - [ ] Sliding Window 80 | 81 | - [ ] Partition 82 | 83 | ### 4. 轻松手写排序算法 84 | 85 | - [ ] Merge Sort 86 | 87 | - [ ] Quick Sort 88 | 89 | - [ ] Quick Selection 90 | 91 | - [ ] Comparison-based Sort 92 | 93 | ## 5. 宽度优先搜索 94 | 95 | - [ ] Level Order Traversal 96 | 97 | - [ ] The Shortest Path 98 | 99 | - [ ] Connected Component 100 | 101 | - [ ] Topology Sorting 102 | 103 | - [ ] Best First Search and Dijkstra 104 | 105 | ## 6. 深度优先搜索 106 | 107 | - [ ] Subset 108 | 109 | - [ ] Permutation 110 | 111 | - [ ] Parentheses 112 | 113 | - [ ] Memoization 114 | 115 | - [ ] Graph Search 116 | 117 | ## 7. 从数学归纳法入门动态规划 118 | 119 | - [ ] 1D DP 120 | 121 | - [ ] 2D DP 122 | 123 | - [ ] Greedy 124 | 125 | ## 8. 高频题目类型讲解 126 | 127 | - [ ] Merge Intervals 128 | 129 | - [ ] Two Heap Pattern 130 | 131 | - [ ] Top-K Problem 132 | 133 | - [ ] Union Find 134 | 135 | - [ ] Data Structure Design 136 | 137 | - [ ] Bit Operations 138 | 139 | -------------------------------------------------------------------------------- /src/main/java/algorithm/pointers/twosum/ThreeSum.java: -------------------------------------------------------------------------------- 1 | package algorithm.pointers.twosum; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Given an unsorted array of integers. 7 | * Find all unique triplets in the array which gives the sum of target. 8 | */ 9 | public class ThreeSum { 10 | // Method 1: Sort + For loop 2 Sum 11 | // Time O(n^2 + nlogn) = O(n^2), Space O(n) for sort 12 | public static List> threeSum(int[] array, int target) { 13 | List> res = new ArrayList<>(); 14 | if (array == null || array.length < 3) { 15 | return res; 16 | } 17 | Arrays.sort(array); 18 | 19 | for (int i = 0; i < array.length - 2; i++) { 20 | if (i > 0 && array[i] == array[i - 1]) { // skip duplicate start index 21 | continue; 22 | } 23 | // run two sum to find pair equal to - target in range (i, n-1] 24 | int left = i + 1; 25 | int right = array.length - 1; 26 | while (left < right) { 27 | int sum = array[left] + array[right]; 28 | if (sum == target - array[i]) { 29 | res.add(Arrays.asList(array[i], array[left++], array[right--])); 30 | // skip duplicates 31 | while (left < right && array[left] == array[left - 1]) { 32 | left++; 33 | } 34 | while (left < right && array[right] == array[right + 1]) { 35 | right--; 36 | } 37 | } else if (sum < target - array[i]) { 38 | left++; 39 | } else { 40 | right--; 41 | } 42 | } 43 | } 44 | return res; 45 | } 46 | 47 | // Method 2: HashSet without Sort 48 | // Time: O(n^2) Space: O(n) 49 | public static List> threeSum3(int[] array, int target) { 50 | List> res = new ArrayList<>(); 51 | if (array == null || array.length < 3) { 52 | return res; 53 | } 54 | Set first = new HashSet<>(); 55 | 56 | for (int i = 0; i < array.length; i++) { // enumerate each first number 57 | if (first.contains(array[i])) { // skip duplicate for first number 58 | continue; 59 | } 60 | Set visited = new HashSet<>(); 61 | Set used = new HashSet<>(); 62 | int twoSum = target - array[i]; 63 | // find all unique pairs that sum to twoSum 64 | for (int j = i + 1; j < array.length; j++) { 65 | // enumerate each number and check whether the diff in hash set 66 | int diff = twoSum - array[j]; 67 | if (visited.contains(diff)) { 68 | if (!used.contains(array[j]) && !used.contains(diff)) { 69 | res.add(Arrays.asList(array[i], array[j], twoSum - array[j])); 70 | used.add(array[j]); 71 | used.add(diff); 72 | } 73 | } else { 74 | visited.add(array[j]); 75 | } 76 | } 77 | first.add(array[i]); 78 | } 79 | return res; 80 | } 81 | 82 | // Method 3: Sort + HashMap counter 83 | // Time: O(n^2 + nlogn) = O(n^2), Space = O(n) for sort + map 84 | public static List> threeSum2(int[] array, int target) { 85 | List> res = new ArrayList<>(); 86 | if (array == null || array.length < 3) { 87 | return res; 88 | } 89 | Arrays.sort(array); 90 | 91 | Map counter = new HashMap<>(); 92 | for (int num : array) { 93 | counter.put(num, counter.getOrDefault(num, 0) + 1); 94 | } 95 | 96 | int left = 0; 97 | while (left < array.length - 1) { 98 | for (int right = left + 1; right < array.length; right++) { 99 | int diff = target - array[left] - array[right]; 100 | int diffNeeded = 1; 101 | if (array[left] == diff) { 102 | diffNeeded++; 103 | } 104 | if (array[right] == diff) { 105 | diffNeeded++; 106 | } 107 | if (counter.getOrDefault(diff, 0) >= diffNeeded && diff >= array[right]) { 108 | // strictly maintain array[left] <= array[right] <= diff to deduplicate 109 | res.add(Arrays.asList(array[left], array[right], diff)); 110 | } 111 | // deduplicate 112 | while (right < array.length - 1 && array[right] == array[right + 1]) { 113 | right++; 114 | } 115 | } 116 | // deduplicate 117 | while (left < array.length - 1 && array[left] == array[left + 1]) { 118 | left++; 119 | } 120 | left++; 121 | } 122 | return res; 123 | } 124 | 125 | public static void main(String[] args) { 126 | int[] nums = {0, 1, 1, -4, 0, 6, 9, -11, 16, 8, 5, 0, 0}; 127 | int[] targets = {5, 0, -10, 16}; 128 | for (int target : targets) { 129 | System.out.println("Target is " + target); 130 | print(threeSum(nums, target)); 131 | } 132 | for (int target : targets) { 133 | System.out.println("Target is " + target); 134 | print(threeSum3(nums, target)); 135 | } 136 | } 137 | 138 | private static void print(List> list) { 139 | if (list == null || list.size() == 0 || list.get(0) == null || list.get(0).size() == 0) { 140 | return; 141 | } 142 | for (List subList : list) { 143 | System.out.println(subList); 144 | } 145 | } 146 | } 147 | --------------------------------------------------------------------------------